1use cubecl_core::{
2 self as cubecl,
3 cmma::{MatrixIdent, MatrixType},
4 ir::{
5 ElemType, FloatKind,
6 dialect::matrix::{ColIndexOp, MmaManualOp, RowIndexOp},
7 features::MmaConfig,
8 interfaces::TypedExt,
9 prelude::*,
10 },
11 prelude::*,
12};
13use itertools::Itertools;
14use pliron::{context::Context, r#type::TypedHandle, value::Value};
15
16use crate::{
17 hip::{
18 arch::{AMDArchitecture, AmdWmma},
19 hip_op,
20 mma::{WmmaExecute, amd_wmma, compile_fragment_intrinsic},
21 },
22 shared::{
23 CppValue, SupportedMmaCombinations, SupportedScaledMmaCombinations, lowering::LowerOp,
24 ty::TypeExtCPP,
25 },
26 target::Hip,
27};
28
29#[op_interface_impl]
30impl LowerOp<Hip> for RowIndexOp {
31 fn lower(&self, scope: &Scope) -> Vec<Value> {
32 let matrix = self.matrix_ty(scope.ctx()).deref(scope.ctx());
33 let (ident, m, k) = (matrix.ident, matrix.shape.m as u32, matrix.shape.k as u32);
34 let rdna4 = amd_wmma(scope.ctx()) == AmdWmma::Rdna4;
35 let lane_id = self.lane_id(scope.ctx());
36 let i = self.i(scope.ctx());
37 vec![row_index::expand(scope, lane_id.into(), i.into(), ident, m, k, rdna4).value(scope)]
38 }
39}
40
41#[op_interface_impl]
42impl LowerOp<Hip> for ColIndexOp {
43 fn lower(&self, scope: &Scope) -> Vec<Value> {
44 let matrix = self.matrix_ty(scope.ctx()).deref(scope.ctx());
45 let (ident, k) = (matrix.ident, matrix.shape.k as u32);
46 let rdna4 = amd_wmma(scope.ctx()) == AmdWmma::Rdna4;
47 let lane_id = self.lane_id(scope.ctx());
48 let i = self.i(scope.ctx());
49 vec![col_index::expand(scope, lane_id.into(), i.into(), ident, k, rdna4).value(scope)]
50 }
51}
52
53#[cube]
56fn split_half(lane_id: u32, i: u32, #[comptime] dim: u32) -> u32 {
57 (lane_id / 16) * comptime![dim / 2] + i
58}
59
60#[cube]
61fn row_index(
62 lane_id: u32,
63 i: u32,
64 #[comptime] ident: MatrixIdent,
65 #[comptime] m: u32,
66 #[comptime] k: u32,
67 #[comptime] rdna4: bool,
68) -> u32 {
69 match ident {
70 MatrixIdent::A => lane_id % 16,
71 MatrixIdent::B => {
72 if comptime![rdna4] {
73 split_half(lane_id, i, k)
74 } else {
75 i
77 }
78 }
79 MatrixIdent::Accumulator => {
80 if comptime![rdna4] {
81 split_half(lane_id, i, m)
82 } else {
83 i * 2 + (lane_id / 16)
85 }
86 }
87 }
88}
89
90#[cube]
91fn col_index(
92 lane_id: u32,
93 i: u32,
94 #[comptime] ident: MatrixIdent,
95 #[comptime] k: u32,
96 #[comptime] rdna4: bool,
97) -> u32 {
98 match ident {
99 MatrixIdent::A => {
100 if comptime![rdna4] {
101 split_half(lane_id, i, k)
102 } else {
103 i
104 }
105 }
106 MatrixIdent::B => lane_id % 16,
107 MatrixIdent::Accumulator => lane_id % 16,
108 }
109}
110
111hip_op!(MmaManualOp, compile_manual_mma);
112
113pub(super) fn compile_manual_mma(op: &MmaManualOp, ctx: &Context) -> String {
114 let frag_a = op.registers_a(ctx);
115 let frag_b = op.registers_b(ctx);
116 let frag_c = op.registers_c(ctx);
117 let frag_d = op.registers_d(ctx);
118 let shape = op.shape(ctx).0;
119
120 let elem_a = frag_a.scalar_ty(ctx);
122 let elem_c = frag_c.scalar_ty(ctx);
123 let elem_d = frag_d.scalar_ty(ctx).to_cpp(ctx);
124
125 let extension = WmmaExecute::from_manual(shape, elem_a, elem_c);
126
127 let cd_elems = shape.num_elems(MatrixIdent::Accumulator) / 32;
128 let ab_elems = amd_wmma(ctx).frag_ab_elems(shape.k);
129
130 let frag_cd_step = match amd_wmma(ctx) {
133 AmdWmma::Rdna3 => 4usize.div_ceil(elem_c.size(ctx)),
134 AmdWmma::Rdna4 => 1,
135 };
136
137 let frag = |val: Value, len: usize| {
140 let elem = val.scalar_ty(ctx).to_cpp(ctx);
141 let ptr = format!("reinterpret_cast<const {elem}*>({}.data)", val.name(ctx));
142 (0..len).map(|i| format!("{ptr}[{i}]")).join(", ")
143 };
144
145 let frag_a = frag(frag_a, ab_elems);
146 let frag_b = frag(frag_b, ab_elems);
147 let frag_c = {
150 let elem = elem_c.to_cpp(ctx);
151 let frag_c = frag_c.name(ctx);
152 let ptr = format!("reinterpret_cast<const {elem}*>({frag_c}.data)");
153 (0..cd_elems)
154 .flat_map(|i| {
155 let ptr = ptr.clone();
156 (0..frag_cd_step).map(move |_| format!("{ptr}[{i}]"))
157 })
158 .join(", ")
159 };
160
161 let name = extension.fn_name(ctx);
163
164 let mut out = String::from("{{");
165 out.push_str(&format!(
166 "{} frag_d_tmp = {{}};",
167 compile_fragment_intrinsic(ctx, &extension.frag_d)
168 ));
169
170 out.push_str(&format!(
171 "{name}({}{{{frag_a}}}, {}{{{frag_b}}}, {}{{{frag_c}}}, frag_d_tmp);",
172 compile_fragment_intrinsic(ctx, &extension.frag_a),
173 compile_fragment_intrinsic(ctx, &extension.frag_b),
174 compile_fragment_intrinsic(ctx, &extension.frag_c)
175 ));
176
177 let frag_d_ptr = format!("reinterpret_cast<{elem_d}*>({}->data)", frag_d.name(ctx));
178
179 for i in 0..cd_elems {
180 out.push_str(&format!(
181 "{frag_d_ptr}[{i}] = frag_d_tmp[{i} * {frag_cd_step}];"
182 ));
183 }
184
185 out.push_str("}}");
186
187 out
188}
189
190pub fn supported_mma_combinations(arch: &AMDArchitecture) -> SupportedMmaCombinations {
191 const ENABLED: bool = true;
193
194 if !ENABLED {
195 return Vec::new();
196 }
197
198 let mut result: SupportedMmaCombinations = vec![];
201 if arch.wmma_generation().is_some() {
202 let types = vec![
204 (
205 ElemType::Float(FloatKind::F16),
206 ElemType::Float(FloatKind::F32),
207 ),
208 (
209 ElemType::Float(FloatKind::BF16),
210 ElemType::Float(FloatKind::F32),
211 ),
212 ];
213 let combinations = types.into_iter().map(|(ab_elem, cd_elem)| MmaConfig {
214 a_type: ab_elem,
215 b_type: ab_elem,
216 cd_type: cd_elem,
217 m: 16,
218 n: 16,
219 k: 16,
220 });
221 result.extend(combinations);
222 }
223 result
224}
225
226pub fn supported_scaled_mma_combinations(
227 _arch: &AMDArchitecture,
228) -> SupportedScaledMmaCombinations {
229 vec![]
230}
231
232pub fn contiguous_elements_rdna3(
233 ctx: &Context,
234 ident: MatrixIdent,
235 matrix: TypedHandle<MatrixType>,
236) -> usize {
237 contiguous_elements(AmdWmma::Rdna3, ctx, ident, matrix)
238}
239
240pub fn contiguous_elements_rdna4(
241 ctx: &Context,
242 ident: MatrixIdent,
243 matrix: TypedHandle<MatrixType>,
244) -> usize {
245 contiguous_elements(AmdWmma::Rdna4, ctx, ident, matrix)
246}
247
248fn contiguous_elements(
249 wmma: AmdWmma,
250 ctx: &Context,
251 ident: MatrixIdent,
252 matrix: TypedHandle<MatrixType>,
253) -> usize {
254 let matrix = matrix.deref(ctx);
255 let max_vector_size = 16 / matrix.elem_ty.size(ctx);
257 match ident {
258 MatrixIdent::A | MatrixIdent::B => wmma.frag_ab_elems(matrix.shape.k).min(max_vector_size),
261 MatrixIdent::Accumulator => 1,
262 }
263}