Skip to main content

cubek_std/tile/variants/instruction/mma/
base.rs

1use cubecl::{
2    cmma::MmaDefinition,
3    define_size,
4    ir::{DeviceProperties, MatrixIdent, StorageType},
5    prelude::*,
6};
7
8use crate::{
9    MatrixLayout, StageIdent, TileSize,
10    tile::{
11        SharedTile, Tile, TileKind, TileKindExpand, TileScope,
12        variants::instruction::mma::{MmaStageWriter, mma_fill_fragment, mma_load_strided},
13    },
14};
15
16// Fragment inner vector sizes for the three MMA roles. Bound at allocation time
17// via `mma_register_vector_sizes` to match the hardware's `def.vector_size(...)`
18// for each role — these are independent of the outer Tile enum's stage vector `V`.
19define_size!(pub NL);
20define_size!(pub NR);
21define_size!(pub NA);
22
23/// Single MMA tile carrier. The role (Lhs / Rhs / Acc) lives inside
24/// [`MmaFragment`] because each role's fragment uses a different inner vector
25/// size (`NL` / `NR` / `NA`); the outer carrier holds the shared comptime
26/// metadata the tile body uses. The matmul-level configuration that produced
27/// these values lives in cubek-matmul as `MmaMatmul`.
28#[derive(CubeType)]
29pub struct MmaTile<N: Numeric> {
30    pub fragment: MmaFragment<N>,
31    #[cube(comptime)]
32    pub matrix_layout: MatrixLayout,
33    #[cube(comptime)]
34    pub tile_size: TileSize,
35    #[cube(comptime)]
36    pub mma_io_config: MmaIOConfig,
37}
38
39#[derive(CubeType)]
40pub enum MmaFragment<N: Numeric> {
41    Lhs(Array<Vector<N, NL>>),
42    Rhs(Array<Vector<N, NR>>),
43    Acc(Array<Vector<N, NA>>),
44}
45
46/// Hardware-capability-driven choice of load/store methods for the MMA tile.
47/// Determined once per `(device, dtypes)` and carried by the tile because the
48/// fragment readers/writers branch on it.
49#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
50pub struct MmaIOConfig {
51    pub lhs_load_method: LoadMethod,
52    pub rhs_load_method: LoadMethod,
53    pub acc_load_method: LoadMethod,
54    pub store_method: StoreMethod,
55}
56
57#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
58pub enum LoadMethod {
59    Manual,
60    LoadMatrix,
61}
62
63#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
64pub enum StoreMethod {
65    Manual,
66    StoreMatrix,
67}
68
69impl MmaIOConfig {
70    pub fn new(
71        device_props: &DeviceProperties,
72        lhs_stage: StorageType,
73        rhs_stage: StorageType,
74        acc_stage: StorageType,
75    ) -> Self {
76        Self {
77            lhs_load_method: load_method(device_props, lhs_stage),
78            rhs_load_method: load_method(device_props, rhs_stage),
79            acc_load_method: load_method(device_props, acc_stage),
80            store_method: store_method(device_props, acc_stage),
81        }
82    }
83
84    pub fn load_method(&self, ident: MatrixIdent) -> LoadMethod {
85        match ident {
86            MatrixIdent::A => self.lhs_load_method,
87            MatrixIdent::B => self.rhs_load_method,
88            MatrixIdent::Accumulator => self.acc_load_method,
89        }
90    }
91
92    pub fn store_method(&self) -> StoreMethod {
93        self.store_method
94    }
95}
96
97fn load_method(device_props: &DeviceProperties, dtype: StorageType) -> LoadMethod {
98    if !matches!(dtype, StorageType::Packed(_, _))
99        && device_props.features.matmul.ldmatrix.contains(&dtype)
100    {
101        LoadMethod::LoadMatrix
102    } else {
103        LoadMethod::Manual
104    }
105}
106
107fn store_method(device_props: &DeviceProperties, dtype: StorageType) -> StoreMethod {
108    if !matches!(dtype, StorageType::Packed(_, _))
109        && device_props.features.matmul.stmatrix.contains(&dtype)
110    {
111        StoreMethod::StoreMatrix
112    } else {
113        StoreMethod::Manual
114    }
115}
116
117#[cube]
118fn make_mma_definition<L: Numeric, R: Numeric, A: Numeric>(
119    #[comptime] tile_size: TileSize,
120) -> MmaDefinition<L, R, A> {
121    MmaDefinition::new(
122        tile_size.m() as usize,
123        tile_size.n() as usize,
124        tile_size.k() as usize,
125    )
126}
127
128#[cube]
129#[allow(unused_variables)]
130pub fn mma_register_vector_sizes<L: Numeric, R: Numeric, A: Numeric>(def: MmaDefinition<L, R, A>) {
131    let vector_size_a = def.vector_size(MatrixIdent::A);
132    let vector_size_b = def.vector_size(MatrixIdent::B);
133    let vector_size_acc = def.vector_size(MatrixIdent::Accumulator);
134    intrinsic!(|scope| {
135        scope.register_size::<NL>(vector_size_a);
136        scope.register_size::<NR>(vector_size_b);
137        scope.register_size::<NA>(vector_size_acc);
138    });
139}
140
141#[cube]
142pub fn mma_allocate_lhs<L: Numeric, R: Numeric, A: Numeric, Sc: TileScope>(
143    #[comptime] layout: MatrixLayout,
144    #[comptime] tile_size: TileSize,
145    #[comptime] mma_io_config: MmaIOConfig,
146) -> Tile<L, Sc> {
147    let def = make_mma_definition::<L, R, A>(tile_size);
148    mma_register_vector_sizes(def);
149    let vector_count = def.vectors_per_lane(MatrixIdent::A);
150
151    Tile::from_kind(TileKind::new_Mma(MmaTile::<L> {
152        fragment: MmaFragment::new_Lhs(Array::new(vector_count)),
153        matrix_layout: layout,
154        tile_size,
155        mma_io_config,
156    }))
157}
158
159#[cube]
160pub fn mma_allocate_rhs<R: Numeric, L: Numeric, A: Numeric, Sc: TileScope>(
161    #[comptime] layout: MatrixLayout,
162    #[comptime] tile_size: TileSize,
163    #[comptime] mma_io_config: MmaIOConfig,
164) -> Tile<R, Sc> {
165    let def = make_mma_definition::<L, R, A>(tile_size);
166    mma_register_vector_sizes(def);
167    let vector_count = def.vectors_per_lane(MatrixIdent::B);
168
169    Tile::from_kind(TileKind::new_Mma(MmaTile::<R> {
170        fragment: MmaFragment::new_Rhs(Array::new(vector_count)),
171        matrix_layout: layout,
172        tile_size,
173        mma_io_config,
174    }))
175}
176
177#[cube]
178pub fn mma_allocate_acc<A: Numeric, L: Numeric, R: Numeric, Sc: TileScope>(
179    #[comptime] layout: MatrixLayout,
180    #[comptime] tile_size: TileSize,
181    #[comptime] mma_io_config: MmaIOConfig,
182) -> Tile<A, Sc> {
183    let def = make_mma_definition::<L, R, A>(tile_size);
184    mma_register_vector_sizes(def);
185    let vector_count = def.vectors_per_lane(MatrixIdent::Accumulator);
186
187    Tile::from_kind(TileKind::new_Mma(MmaTile::<A> {
188        fragment: MmaFragment::new_Acc(Array::new(vector_count)),
189        matrix_layout: layout,
190        tile_size,
191        mma_io_config,
192    }))
193}
194
195#[cube]
196impl<A: Numeric> MmaTile<A> {
197    /// Executes `lhs · rhs`, accumulating into `self`. Each operand must
198    /// carry the role its position requires (`Lhs`, `Rhs`, `Acc`).
199    pub fn mma<L: Numeric, R: Numeric>(&mut self, lhs: &MmaTile<L>, rhs: &MmaTile<R>) {
200        match &lhs.fragment {
201            MmaFragment::Lhs(lf) => match &rhs.fragment {
202                MmaFragment::Rhs(rf) => match &mut self.fragment {
203                    MmaFragment::Acc(af) => {
204                        mma_execute(lf, rf, af, self.matrix_layout, self.tile_size);
205                    }
206                    MmaFragment::Lhs(_) | MmaFragment::Rhs(_) => {
207                        panic!("Mma: expected Acc role for accumulator")
208                    }
209                },
210                MmaFragment::Lhs(_) | MmaFragment::Acc(_) => {
211                    panic!("Mma: expected Rhs role for rhs")
212                }
213            },
214            MmaFragment::Rhs(_) | MmaFragment::Acc(_) => {
215                panic!("Mma: expected Lhs role for lhs")
216            }
217        }
218    }
219}
220
221#[cube]
222impl<N: Numeric> MmaTile<N> {
223    /// Copies into the mma fragment from `source`. Supported sources:
224    /// `Shared` (per-role load) and `None` (zero-init, Acc only).
225    /// `L` / `R` / `A` are the matmul triple's role types — needed by the
226    /// per-role load functions. When `self` is in role X, `N` substitutes
227    /// for the X type and the other two are taken from the caller's
228    /// generics.
229    pub fn copy_from<SE: Numeric, SS: Size, L: Numeric, R: Numeric, A: Numeric, Sc: TileScope>(
230        &mut self,
231        source: &Tile<SE, Sc>,
232        #[comptime] _ident: StageIdent,
233    ) {
234        match &source.kind {
235            TileKind::SharedTile(shared) => match &mut self.fragment {
236                MmaFragment::Lhs(f) => mma_load_lhs_from_shared::<SE, SS, N, R, A>(
237                    shared,
238                    f,
239                    self.matrix_layout,
240                    self.tile_size,
241                    self.mma_io_config,
242                ),
243                MmaFragment::Rhs(f) => mma_load_rhs_from_shared::<SE, SS, N, L, A>(
244                    shared,
245                    f,
246                    self.matrix_layout,
247                    self.tile_size,
248                    self.mma_io_config,
249                ),
250                MmaFragment::Acc(f) => mma_load_acc_from_shared::<SE, SS, N, L, R>(
251                    shared,
252                    f,
253                    self.matrix_layout,
254                    self.tile_size,
255                    self.mma_io_config,
256                ),
257            },
258            TileKind::None => match &mut self.fragment {
259                MmaFragment::Acc(f) => {
260                    mma_load_acc_zeros::<N, L, R>(
261                        f,
262                        self.matrix_layout,
263                        self.tile_size,
264                        self.mma_io_config,
265                    );
266                }
267                MmaFragment::Lhs(_) | MmaFragment::Rhs(_) => {
268                    panic!("Mma zero-load only supported for Acc role")
269                }
270            },
271            TileKind::Cmma(_)
272            | TileKind::Mma(_)
273            | TileKind::Register(_)
274            | TileKind::PlaneVec(_)
275            | TileKind::Interleaved(_)
276            | TileKind::Unit(_)
277            | TileKind::WhiteboxFragment(_)
278            | TileKind::RowWise(_)
279            | TileKind::Bounce(_)
280            | TileKind::Stage(_)
281            | TileKind::Partition(_)
282            | TileKind::Pipelined(_) => panic!("MmaTile::copy_from: unsupported source variant"),
283        }
284    }
285
286    /// Zero-init the mma fragment (Acc role only).
287    pub fn init_zero<L: Numeric, R: Numeric>(&mut self) {
288        match &mut self.fragment {
289            MmaFragment::Acc(f) => {
290                mma_load_acc_zeros::<N, L, R>(
291                    f,
292                    self.matrix_layout,
293                    self.tile_size,
294                    self.mma_io_config,
295                );
296            }
297            MmaFragment::Lhs(_) | MmaFragment::Rhs(_) => {
298                panic!("MmaTile::init_zero: only Acc role supported")
299            }
300        }
301    }
302}
303
304// ===========================================================================
305// Compute: matmul / load / write / zero-init
306// ===========================================================================
307
308#[cube]
309pub fn mma_execute<L: Numeric, R: Numeric, A: Numeric>(
310    lhs: &Array<Vector<L, NL>>,
311    rhs: &Array<Vector<R, NR>>,
312    acc: &mut Array<Vector<A, NA>>,
313    #[comptime] _matrix_layout: MatrixLayout,
314    #[comptime] tile_size: TileSize,
315) {
316    let def = MmaDefinition::<L, R, A>::new(
317        tile_size.m() as usize,
318        tile_size.n() as usize,
319        tile_size.k() as usize,
320    );
321    let out_arr = def.execute(lhs, rhs, &*acc);
322    let num_vectors = def.vectors_per_lane(MatrixIdent::Accumulator);
323    #[unroll]
324    for i in 0..num_vectors {
325        acc[i] = out_arr[i];
326    }
327}
328
329#[cube]
330pub fn mma_load_lhs_from_shared<E: Numeric, ES: Size, L: Numeric, R: Numeric, A: Numeric>(
331    shared: &SharedTile<E>,
332    fragment: &mut Array<Vector<L, NL>>,
333    #[comptime] matrix_layout: MatrixLayout,
334    #[comptime] tile_size: TileSize,
335    #[comptime] mma_io_config: MmaIOConfig,
336) {
337    let shared = shared.view::<ES>();
338    let def = make_mma_definition::<L, R, A>(tile_size);
339    mma_load_strided(
340        &shared,
341        fragment,
342        &def,
343        MatrixIdent::A,
344        matrix_layout,
345        tile_size,
346        mma_io_config,
347    );
348}
349
350#[cube]
351pub fn mma_load_rhs_from_shared<E: Numeric, ES: Size, R: Numeric, L: Numeric, A: Numeric>(
352    shared: &SharedTile<E>,
353    fragment: &mut Array<Vector<R, NR>>,
354    #[comptime] matrix_layout: MatrixLayout,
355    #[comptime] tile_size: TileSize,
356    #[comptime] mma_io_config: MmaIOConfig,
357) {
358    let shared = shared.view::<ES>();
359    let def = make_mma_definition::<L, R, A>(tile_size);
360    mma_load_strided(
361        &shared,
362        fragment,
363        &def,
364        MatrixIdent::B,
365        matrix_layout,
366        tile_size,
367        mma_io_config,
368    );
369}
370
371#[cube]
372pub fn mma_load_acc_from_shared<E: Numeric, ES: Size, A: Numeric, L: Numeric, R: Numeric>(
373    shared: &SharedTile<E>,
374    fragment: &mut Array<Vector<A, NA>>,
375    #[comptime] matrix_layout: MatrixLayout,
376    #[comptime] tile_size: TileSize,
377    #[comptime] mma_io_config: MmaIOConfig,
378) {
379    let shared = shared.view::<ES>();
380    let def = make_mma_definition::<L, R, A>(tile_size);
381    mma_load_strided(
382        &shared,
383        fragment,
384        &def,
385        MatrixIdent::Accumulator,
386        matrix_layout,
387        tile_size,
388        mma_io_config,
389    );
390}
391
392#[cube]
393pub fn mma_load_acc_zeros<A: Numeric, L: Numeric, R: Numeric>(
394    fragment: &mut Array<Vector<A, NA>>,
395    #[comptime] matrix_layout: MatrixLayout,
396    #[comptime] tile_size: TileSize,
397    #[comptime] mma_io_config: MmaIOConfig,
398) {
399    let _ = (matrix_layout, mma_io_config);
400    let def = make_mma_definition::<L, R, A>(tile_size);
401    mma_fill_fragment::<A, NA, A, L, R, A>(
402        &A::from_int(0),
403        fragment,
404        &def,
405        MatrixIdent::Accumulator,
406    );
407}
408
409#[cube]
410pub fn mma_write_to_shared<E: Numeric, ES: Size, A: Numeric, L: Numeric, R: Numeric>(
411    shared: &mut SharedTile<E>,
412    fragment: &Array<Vector<A, NA>>,
413    #[comptime] tile_size: TileSize,
414    #[comptime] mma_io_config: MmaIOConfig,
415) {
416    let mut shared = shared.view::<ES>();
417    let def = make_mma_definition::<L, R, A>(tile_size);
418    let out_layout = comptime!(shared.layout);
419    MmaStageWriter::store_fragment(
420        &mut shared,
421        fragment,
422        &def,
423        MatrixIdent::Accumulator,
424        out_layout,
425        tile_size.m(),
426        mma_io_config,
427    );
428}