1use cubecl;
2use cubecl::prelude::*;
3
4use crate::StageIdent;
5use crate::tile::{
6 Plane, RowWise, Tile, TileKind, TileKindExpand,
7 mask::Mask,
8 scope::{TileScope, assert_plane_scope},
9 variants::{
10 instruction::cmma::CmmaTile,
11 whitebox_fragment::{InnerLayout, WhiteboxFragment, WhiteboxFragmentLayout},
12 },
13};
14
15#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
23pub struct BounceConfig {
24 pub tile_shape: (u32, u32),
25 pub num_planes: u32,
26 pub plane_dim: u32,
27 pub inner_layout: InnerLayout,
28}
29
30#[derive(CubeType)]
31pub struct BounceTile<N: Numeric> {
32 pub cmma: CmmaTile<N>,
33 pub smem: Shared<[N]>,
34 pub fragment: WhiteboxFragment<N>,
35}
36
37#[cube]
38impl<N: Numeric> BounceTile<N> {
39 pub fn new(cmma: CmmaTile<N>, #[comptime] cfg: BounceConfig) -> BounceTile<N> {
40 let total_tile_size = comptime!((cfg.tile_shape.0 * cfg.tile_shape.1) as usize);
41 let smem_size = comptime!(total_tile_size * cfg.num_planes as usize);
42 let start = UNIT_POS_Y as usize * total_tile_size;
43 let end = start + total_tile_size;
44 let smem = Shared::new_slice(smem_size).map(|smem| &smem[start..end]);
45
46 let layout = comptime!(WhiteboxFragmentLayout::new(
47 cfg.tile_shape,
48 cfg.plane_dim,
49 cfg.inner_layout
50 ));
51 let fragment = WhiteboxFragment::new(layout);
52
53 BounceTile::<N> {
54 cmma,
55 smem,
56 fragment,
57 }
58 }
59}
60
61#[cube]
62impl<E: Float> BounceTile<E> {
63 pub fn cmma_to_fragment(&mut self) {
67 let stride = comptime!(self.cmma.tile_size.n());
68 cubecl::cmma::store(
69 &mut self.smem,
70 &self.cmma.matrix,
71 stride,
72 cubecl::cmma::MatrixLayout::RowMajor,
73 );
74 sync_cube();
75 self.fragment.load_from_slice(&self.smem);
76 sync_cube();
77 }
78
79 pub fn fragment_to_cmma(&mut self) {
83 let stride = comptime!(self.cmma.tile_size.n());
84 self.fragment.store_to(&mut self.smem);
85 sync_cube();
86 cubecl::cmma::load_with_layout(
87 &mut self.cmma.matrix,
88 &self.smem,
89 stride,
90 cubecl::cmma::MatrixLayout::RowMajor,
91 );
92 }
93
94 pub fn row_max(&self, acc: &mut RowWise<E>, base: &RowWise<E>) {
95 self.fragment.row_max(acc, base);
96 }
97
98 pub fn row_sum(&self, acc: &mut RowWise<E>) {
99 self.fragment.row_sum(acc);
100 }
101
102 pub fn exp_diff(&mut self, rowwise: &RowWise<E>) {
103 self.fragment.exp_diff(rowwise);
104 }
105
106 pub fn rowwise_scale(&mut self, scale: &RowWise<E>) {
107 self.fragment.rowwise_scale(scale);
108 }
109
110 pub fn scale_and_mask<M: Mask>(&mut self, scale: E, mask: &M) {
111 self.fragment.scale_and_mask::<M>(scale, mask);
112 }
113
114 pub fn fill_zero(&mut self) {
118 cubecl::cmma::fill(&mut self.cmma.matrix, E::from_int(0));
119 }
120
121 pub fn write_fragment_to<Lhs: Float, Sc: TileScope>(&self, softmaxed: &mut Tile<Lhs, Sc>) {
126 write_fragment_into::<E, Lhs, Sc>(&self.fragment, softmaxed);
127 }
128}
129
130#[cube]
131fn write_fragment_into<Acc: Float, Lhs: Float, Sc: TileScope>(
132 src: &WhiteboxFragment<Acc>,
133 softmaxed: &mut Tile<Lhs, Sc>,
134) {
135 match &mut softmaxed.kind {
136 TileKind::Bounce(d) => {
137 let stride = comptime!(d.cmma.tile_size.n());
138 src.store_to(&mut d.smem);
139 sync_cube();
140 cubecl::cmma::load(&mut d.cmma.matrix, &d.smem, stride);
141 }
142 TileKind::WhiteboxFragment(d) => {
143 let total = comptime!(src.layout.unit_size.0 * src.layout.unit_size.1);
144 for i in 0..total {
145 d.array[i as usize] = Lhs::cast_from(src.array[i as usize]);
146 }
147 }
148 _ => panic!("write_fragment_to: unsupported softmaxed variant"),
149 }
150}
151
152#[cube]
153pub fn allocate_bounce_tile<E: Numeric, Sc: TileScope>(
156 cmma: CmmaTile<E>,
157 #[comptime] cfg: BounceConfig,
158) -> Tile<E, Sc> {
159 comptime!(assert_plane_scope(Sc::KIND));
160 Tile::from_kind(TileKind::new_Bounce(BounceTile::<E>::new(cmma, cfg)))
161}
162
163#[cube]
164impl<N: Numeric> BounceTile<N> {
165 pub fn copy_from<SE: Numeric, SS: Size, Sc: TileScope>(
170 &mut self,
171 source: &Tile<SE, Sc>,
172 #[comptime] ident: StageIdent,
173 ) {
174 self.cmma.copy_from::<SE, SS, Sc>(source, ident);
175 }
176
177 pub fn init_zero(&mut self) {
179 self.cmma.init_zero();
180 }
181}
182
183#[cube]
184impl<Acc: Float> BounceTile<Acc> {
185 pub fn softmax<Lhs: Float, M: Mask>(
192 &mut self,
193 mask: &M,
194 softmaxed: &mut Tile<Lhs, Plane>,
195 state: &mut (RowWise<Acc>, RowWise<Acc>),
196 head_dim_factor: Acc,
197 ) -> RowWise<Acc> {
198 let num_rows = comptime!(state.0.num_rows);
199 let mut max_buf = RowWise::<Acc>::new_min_value(num_rows);
200 let mut sum_buf = RowWise::<Acc>::new_zero(num_rows);
201
202 self.cmma_to_fragment();
203
204 self.scale_and_mask::<M>(head_dim_factor, mask);
205 self.row_max(&mut max_buf, &state.0);
206 self.exp_diff(&max_buf);
207 self.row_sum(&mut sum_buf);
208
209 let exp_m_diff = state.0.exp_diff(&max_buf);
210 let new_l = exp_m_diff.mul(&state.1).add(&sum_buf);
211
212 self.write_fragment_to::<Lhs, Plane>(softmaxed);
213
214 RowWise::copy_from(&mut state.0, &max_buf);
215 RowWise::copy_from(&mut state.1, &new_l);
216
217 exp_m_diff
218 }
219}