1use cubecl::prelude::*;
2
3use crate::{
4 MatrixLayout, StageIdent, TileSize,
5 tile::{
6 Plane, RowWise, SharedTile, StridedTile, Tile, TileKind, TileKindExpand, TileScope,
7 mask::Mask,
8 variants::unit::{UnitTile, UnitTileLayout},
9 },
10};
11
12#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
15pub enum ProductType {
16 Inner,
21 Outer,
26}
27
28impl ProductType {
29 pub fn from_layouts(
30 lhs_layout: MatrixLayout,
31 rhs_layout: MatrixLayout,
32 tile_size: TileSize,
33 ) -> Self {
34 let lhs_preferred = match lhs_layout {
35 MatrixLayout::RowMajor => ProductType::Inner,
36 MatrixLayout::ColMajor => ProductType::Outer,
37 };
38 let rhs_preferred = match rhs_layout {
39 MatrixLayout::RowMajor => ProductType::Outer,
40 MatrixLayout::ColMajor => ProductType::Inner,
41 };
42
43 if lhs_preferred == rhs_preferred {
44 lhs_preferred
45 } else if tile_size.m() == 1 {
46 rhs_preferred
47 } else if tile_size.n() == 1 {
48 lhs_preferred
49 } else {
50 ProductType::Outer
52 }
53 }
54}
55
56#[derive(CubeType)]
62pub struct RegisterTile<N: Numeric> {
63 pub tile: UnitTile<N>,
64 #[cube(comptime)]
65 pub matrix_layout: MatrixLayout,
66 #[cube(comptime)]
67 pub tile_size: TileSize,
68 #[cube(comptime)]
69 pub product_type: ProductType,
70}
71
72#[cube]
73impl<E: Float> RegisterTile<E> {
74 pub fn row_max(&self, acc: &mut RowWise<E>, base: &RowWise<E>) {
75 self.tile.row_max(acc, base);
76 }
77
78 pub fn row_sum(&self, acc: &mut RowWise<E>) {
79 self.tile.row_sum(acc);
80 }
81
82 pub fn exp_diff(&mut self, rowwise: &RowWise<E>) {
83 self.tile.exp_diff(rowwise);
84 }
85
86 pub fn rowwise_scale(&mut self, scale: &RowWise<E>) {
87 self.tile.rowwise_scale(scale);
88 }
89
90 pub fn scale_and_mask<M: Mask>(&mut self, scale: E, mask: &M) {
91 self.tile.scale_and_mask::<M>(scale, mask);
92 }
93
94 pub fn fill_zero(&mut self) {
95 self.tile.fill_zero();
96 }
97
98 pub fn write_to<Lhs: Float>(&self, dest: &mut RegisterTile<Lhs>) {
102 self.tile.write_to::<Lhs>(&mut dest.tile);
103 }
104}
105
106#[cube]
107impl<A: Numeric> RegisterTile<A> {
108 pub fn mma<L: Numeric, R: Numeric>(&mut self, lhs: &RegisterTile<L>, rhs: &RegisterTile<R>) {
111 register_execute(
112 &lhs.tile.data,
113 &rhs.tile.data,
114 &mut self.tile.data,
115 self.tile_size,
116 self.product_type,
117 );
118 }
119}
120
121#[cube]
122impl<N: Numeric> RegisterTile<N> {
123 pub fn copy_from<SE: Numeric, SS: Size, Sc: TileScope>(
126 &mut self,
127 source: &Tile<SE, Sc>,
128 #[comptime] ident: StageIdent,
129 ) {
130 match &source.kind {
131 TileKind::SharedTile(shared) => {
132 register_load_from_shared::<SE, SS, N>(
133 shared,
134 &mut self.tile.data,
135 self.matrix_layout,
136 self.tile_size,
137 self.product_type,
138 ident,
139 );
140 }
141 TileKind::None => {
142 register_load_zeros::<N>(&mut self.tile.data, self.tile_size, ident);
143 }
144 TileKind::Cmma(_)
145 | TileKind::Mma(_)
146 | TileKind::Register(_)
147 | TileKind::PlaneVec(_)
148 | TileKind::Interleaved(_)
149 | TileKind::Unit(_)
150 | TileKind::WhiteboxFragment(_)
151 | TileKind::RowWise(_)
152 | TileKind::Bounce(_)
153 | TileKind::Stage(_)
154 | TileKind::Partition(_)
155 | TileKind::Pipelined(_) => {
156 panic!("RegisterTile::copy_from: unsupported source variant")
157 }
158 }
159 }
160
161 pub fn init_zero(&mut self, #[comptime] ident: StageIdent) {
162 register_load_zeros::<N>(&mut self.tile.data, self.tile_size, ident);
163 }
164}
165
166#[cube]
167impl<Acc: Float> RegisterTile<Acc> {
168 pub fn softmax<Lhs: Float, M: Mask>(
171 &mut self,
172 mask: &M,
173 softmaxed: &mut Tile<Lhs, Plane>,
174 state: &mut (RowWise<Acc>, RowWise<Acc>),
175 head_dim_factor: Acc,
176 ) -> RowWise<Acc> {
177 let num_rows = comptime!(state.0.num_rows);
178 let mut max_buf = RowWise::<Acc>::new_min_value(num_rows);
179 let mut sum_buf = RowWise::<Acc>::new_zero(num_rows);
180
181 self.scale_and_mask::<M>(head_dim_factor, mask);
182 self.row_max(&mut max_buf, &state.0);
183 self.exp_diff(&max_buf);
184 self.row_sum(&mut sum_buf);
185
186 let exp_m_diff = state.0.exp_diff(&max_buf);
187 let new_l = exp_m_diff.mul(&state.1).add(&sum_buf);
188
189 match &mut softmaxed.kind {
190 TileKind::Register(d) => self.write_to::<Lhs>(d),
191 TileKind::Bounce(_) => {
192 panic!("RegisterTile::softmax: Bounce destination not supported")
193 }
194 TileKind::WhiteboxFragment(_) => {
195 panic!("RegisterTile::softmax: WhiteboxFragment destination not supported")
196 }
197 TileKind::Unit(_) => panic!("RegisterTile::softmax: Unit destination not supported"),
198 _ => panic!("RegisterTile::softmax: unsupported softmaxed variant"),
199 }
200
201 RowWise::copy_from(&mut state.0, &max_buf);
202 RowWise::copy_from(&mut state.1, &new_l);
203
204 exp_m_diff
205 }
206}
207
208#[cube]
209pub fn register_allocate_lhs<L: Numeric, Sc: TileScope>(
210 #[comptime] layout: MatrixLayout,
211 #[comptime] tile_size: TileSize,
212 #[comptime] product_type: ProductType,
213) -> Tile<L, Sc> {
214 let m = comptime!(tile_size.m());
215 let k = comptime!(tile_size.k());
216 let inner_layout = comptime!(UnitTileLayout::new(m, k, false));
217 Tile::from_kind(TileKind::new_Register(RegisterTile::<L> {
218 tile: UnitTile::<L>::new(inner_layout),
219 matrix_layout: layout,
220 tile_size,
221 product_type,
222 }))
223}
224
225#[cube]
226pub fn register_allocate_rhs<R: Numeric, Sc: TileScope>(
227 #[comptime] layout: MatrixLayout,
228 #[comptime] tile_size: TileSize,
229 #[comptime] product_type: ProductType,
230) -> Tile<R, Sc> {
231 let n = comptime!(tile_size.n());
232 let k = comptime!(tile_size.k());
233 let inner_layout = comptime!(UnitTileLayout::new(n, k, false));
234 Tile::from_kind(TileKind::new_Register(RegisterTile::<R> {
235 tile: UnitTile::<R>::new(inner_layout),
236 matrix_layout: layout,
237 tile_size,
238 product_type,
239 }))
240}
241
242#[cube]
243pub fn register_allocate_acc<A: Numeric, Sc: TileScope>(
244 #[comptime] layout: MatrixLayout,
245 #[comptime] tile_size: TileSize,
246 #[comptime] product_type: ProductType,
247) -> Tile<A, Sc> {
248 let m = comptime!(tile_size.m());
249 let n = comptime!(tile_size.n());
250 let inner_layout = comptime!(UnitTileLayout::new(m, n, false));
251 Tile::from_kind(TileKind::new_Register(RegisterTile::<A> {
252 tile: UnitTile::<A>::new(inner_layout),
253 matrix_layout: layout,
254 tile_size,
255 product_type,
256 }))
257}
258
259pub(crate) const UNROLL: bool = false;
264
265#[cube]
266pub fn register_execute<L: Numeric, R: Numeric, A: Numeric>(
267 lhs: &Array<L>,
268 rhs: &Array<R>,
269 acc: &mut Array<A>,
270 #[comptime] tile_size: TileSize,
271 #[comptime] product_type: ProductType,
272) {
273 let m = tile_size.m();
274 let n = tile_size.n();
275 let k = tile_size.k();
276 match product_type {
277 ProductType::Inner => {
278 inner_product::<L, R, A>(lhs, rhs, acc, m, n, k);
279 }
280 ProductType::Outer => {
281 outer_product::<L, R, A>(lhs, rhs, acc, m, n, k);
282 }
283 }
284}
285
286#[cube]
287fn inner_product<L: Numeric, R: Numeric, A: Numeric>(
288 lhs: &Array<L>,
289 rhs: &Array<R>,
290 acc: &mut Array<A>,
291 #[comptime] m: u32,
292 #[comptime] n: u32,
293 #[comptime] k: u32,
294) {
295 #[unroll(UNROLL)]
296 for m_ in 0..m as usize {
297 #[unroll(UNROLL)]
298 for n_ in 0..n as usize {
299 #[unroll(UNROLL)]
300 for k_ in 0..k as usize {
301 let lhs_elem = A::cast_from(lhs[m_ * k as usize + k_]);
302 let rhs_elem = A::cast_from(rhs[n_ * k as usize + k_]);
303 acc[m_ * n as usize + n_] += lhs_elem * rhs_elem;
304 }
305 }
306 }
307}
308
309#[cube]
310fn outer_product<L: Numeric, R: Numeric, A: Numeric>(
311 lhs: &Array<L>,
312 rhs: &Array<R>,
313 acc: &mut Array<A>,
314 #[comptime] m: u32,
315 #[comptime] n: u32,
316 #[comptime] k: u32,
317) {
318 #[unroll(UNROLL)]
319 for k_ in 0..k as usize {
320 #[unroll(UNROLL)]
321 for m_ in 0..m as usize {
322 let lhs_elem = A::cast_from(lhs[k_ * m as usize + m_]);
323 #[unroll(UNROLL)]
324 for n_ in 0..n as usize {
325 let rhs_elem = A::cast_from(rhs[k_ * n as usize + n_]);
326 acc[m_ * n as usize + n_] += lhs_elem * rhs_elem;
327 }
328 }
329 }
330}
331
332#[cube]
333#[allow(clippy::too_many_arguments)]
334pub fn register_load_from_shared<E: Numeric, ES: Size, N: Numeric>(
335 shared: &SharedTile<E>,
336 arr: &mut Array<N>,
337 #[comptime] matrix_layout: MatrixLayout,
338 #[comptime] tile_size: TileSize,
339 #[comptime] product_type: ProductType,
340 #[comptime] ident: StageIdent,
341) {
342 let shared = shared.view::<ES>();
343 let shared = &shared;
344 let m = tile_size.m();
345 let n = tile_size.n();
346 let k = tile_size.k();
347
348 match ident {
349 StageIdent::Lhs => match product_type {
350 ProductType::Inner => match matrix_layout {
351 MatrixLayout::RowMajor => {
352 load_plain::<E, ES, N>(shared, arr, m, k);
353 }
354 MatrixLayout::ColMajor => {
355 load_transposed::<E, ES, N>(shared, arr, k, m);
356 }
357 },
358 ProductType::Outer => match matrix_layout {
359 MatrixLayout::RowMajor => {
360 load_transposed::<E, ES, N>(shared, arr, m, k);
361 }
362 MatrixLayout::ColMajor => {
363 load_plain::<E, ES, N>(shared, arr, k, m);
364 }
365 },
366 },
367 StageIdent::Rhs => match product_type {
368 ProductType::Inner => match matrix_layout {
369 MatrixLayout::RowMajor => {
370 load_transposed::<E, ES, N>(shared, arr, k, n);
371 }
372 MatrixLayout::ColMajor => {
373 load_plain::<E, ES, N>(shared, arr, n, k);
374 }
375 },
376 ProductType::Outer => match matrix_layout {
377 MatrixLayout::RowMajor => {
378 load_plain::<E, ES, N>(shared, arr, k, n);
379 }
380 MatrixLayout::ColMajor => {
381 load_transposed::<E, ES, N>(shared, arr, n, k);
382 }
383 },
384 },
385 StageIdent::Acc => match matrix_layout {
386 MatrixLayout::RowMajor => {
387 load_plain::<E, ES, N>(shared, arr, m, n);
388 }
389 MatrixLayout::ColMajor => {
390 load_transposed::<E, ES, N>(shared, arr, n, m);
391 }
392 },
393 _ => panic!("Invalid ident for Register load"),
394 }
395}
396
397#[cube]
398fn load_plain<E: Numeric, ES: Size, N: Numeric>(
399 tile: &StridedTile<E, ES>,
400 arr: &mut Array<N>,
401 #[comptime] num_segments: u32,
402 #[comptime] segment_size: u32,
403) {
404 let line_size = ES::value() as u32;
405 let num_lines_per_segment = segment_size / line_size;
406
407 #[unroll(UNROLL)]
408 for segment in 0..num_segments {
409 #[unroll(UNROLL)]
410 for line_within_segment in 0..num_lines_per_segment {
411 let line = tile.get_vector(segment, line_within_segment);
412 #[unroll]
413 for pos_within_line in 0..line_size {
414 arr[(segment * segment_size + line_within_segment * line_size + pos_within_line)
415 as usize] = N::cast_from(line.extract(pos_within_line as usize));
416 }
417 }
418 }
419}
420
421#[cube]
422fn load_transposed<E: Numeric, ES: Size, N: Numeric>(
423 tile: &StridedTile<E, ES>,
424 arr: &mut Array<N>,
425 #[comptime] num_segments: u32,
426 #[comptime] segment_size: u32,
427) {
428 let line_size = ES::value() as u32;
429 let num_lines_per_segment = segment_size / line_size;
430
431 #[unroll(UNROLL)]
432 for segment in 0..num_segments {
433 #[unroll(UNROLL)]
434 for line_within_segment in 0..num_lines_per_segment {
435 let line = tile.get_vector(segment, line_within_segment);
436 #[unroll]
437 for pos_within_line in 0..line_size {
438 arr[((line_within_segment * line_size + pos_within_line) * num_segments + segment)
439 as usize] = N::cast_from(line.extract(pos_within_line as usize));
440 }
441 }
442 }
443}
444
445#[cube]
446pub fn register_load_zeros<N: Numeric>(
447 arr: &mut Array<N>,
448 #[comptime] tile_size: TileSize,
449 #[comptime] ident: StageIdent,
450) {
451 let size = match ident {
452 StageIdent::Lhs => tile_size.m() * tile_size.k(),
453 StageIdent::Rhs => tile_size.n() * tile_size.k(),
454 StageIdent::Acc | StageIdent::Out => tile_size.m() * tile_size.n(),
455 };
456 for i in 0..size {
457 arr[i as usize] = N::from_int(0);
458 }
459}
460
461#[cube]
462pub fn register_write_to_shared<E: Numeric, ES: Size, A: Numeric>(
463 shared: &mut SharedTile<E>,
464 arr: &Array<A>,
465 #[comptime] tile_size: TileSize,
466) {
467 let mut shared = shared.view::<ES>();
468 let shared = &mut shared;
469 let out_vector_size = shared.container.vector_size().comptime() as u32;
470 let size_mn = tile_size.m() * tile_size.n();
471
472 #[unroll(false)]
473 for i in 0..size_mn / out_vector_size {
474 let offs = shared.stage_offset(i);
475 let mut vector = Vector::<A, ES>::empty();
476 #[unroll]
477 for j in 0..out_vector_size {
478 vector.insert(j as usize, arr[(i * out_vector_size + j) as usize]);
479 }
480 shared.container[offs as usize] = Vector::cast_from(vector);
481 }
482}