1use core::cell::Ref;
2
3use cubecl_core::{
4 cmma::{MatrixLayout, MatrixShape, MatrixType},
5 ir::{
6 dialect::matrix::{CastOp, FillOp, LoadOp, MultiplyAccumulateOp, StoreOp},
7 interfaces::TypedExt,
8 prelude::*,
9 },
10};
11
12use crate::{
13 metal::{metal_op, ty::metal_ty},
14 shared::{
15 CompilationOptions, CppValue, DeclareMatrixOp,
16 ty::{TypeExtCPP, TypeToCPP},
17 wmma_api_base::{self, as_scalar_ptr},
18 },
19 target::Metal,
20};
21
22pub fn compile_cmma_includes_metal() -> String {
23 "#include <metal_simdgroup_matrix>\n".into()
24}
25
26metal_ty!(MatrixType, |ty, ctx| {
27 let MatrixShape { m, n, k } = ty.shape;
28 let ty = ty.elem_ty.to_cpp(ctx);
29 if m != 8 || n != 8 || k != 8 {
31 panic!("{m}x{n}x{k} fragments not supported. Only 8x8x8 fragments are supported.");
32 }
33 format!("simdgroup_{ty}8x8")
34});
35
36metal_op!(DeclareMatrixOp, |op, ctx| {
37 wmma_api_base::compile_matrix_declaration(
38 ctx,
39 op.get_result(ctx),
40 op.value_ty(ctx).get_type(ctx),
41 )
42});
43
44metal_op!(FillOp, |op, ctx| {
45 let mat = op.matrix(ctx).name(ctx);
46 let value = op.value(ctx).name(ctx);
47 let ty = op.value(ctx).get_type(ctx).to_cpp(ctx);
48
49 format!("*{mat} = make_filled_simdgroup_matrix<{ty}, 8, 8>({value});\n",)
50});
51
52metal_op!(LoadOp, |op, ctx| {
53 let frag = op.matrix(ctx).name(ctx);
54 let ptr = as_scalar_ptr(ctx, op.source(ctx));
55 let stride = op.stride(ctx).name(ctx);
56 let mat_ty = matrix_ty(ctx, op.matrix(ctx));
57 let transpose = match mat_ty.layout {
58 MatrixLayout::RowMajor | MatrixLayout::Undefined => false,
59 MatrixLayout::ColMajor => true,
60 };
61 format!("simdgroup_load(*{frag}, {ptr}, {stride}, 0, {transpose});\n")
62});
63
64metal_op!(StoreOp, |op, ctx| {
65 let mat = op.matrix(ctx).name(ctx);
66 let destination = as_scalar_ptr(ctx, op.destination(ctx));
67 let stride = op.stride(ctx).name(ctx);
68 format!(
69 "
70simdgroup_store(*{mat}, {destination}, {stride});
71simdgroup_barrier(mem_flags::mem_threadgroup);"
72 )
73});
74
75metal_op!(MultiplyAccumulateOp, |op, ctx| {
76 let a = op.mat_a(ctx).name(ctx);
77 let b = op.mat_b(ctx).name(ctx);
78 let c = op.mat_c(ctx).name(ctx);
79 let d = op.mat_d(ctx).name(ctx);
80 format!("simdgroup_multiply_accumulate(*{d}, *{a}, *{b}, *{c});\n")
81});
82
83metal_op!(CastOp, |op, ctx| {
84 let input = op.input(ctx).name(ctx);
85 let output = op.output(ctx).name(ctx);
86 let output_ty = matrix_ty(ctx, op.output(ctx));
87 let ty = output_ty.elem_ty.to_cpp(ctx);
88 let threads_per_simdgroup = ctx.aux_ty::<CompilationOptions>().warp_size;
89 let elements_held_by_each_thread =
90 elements_held_by_each_thread(&output_ty.shape, threads_per_simdgroup);
91 format!(
92 "
93simdgroup_barrier(mem_flags::mem_none);
94for(int e=0; e<{elements_held_by_each_thread}; e++) {{
95 {output}->thread_elements()[e] = {ty}({input}->thread_elements()[e]);
96}}"
97 )
98});
99
100fn elements_held_by_each_thread(shape: &MatrixShape, threads_per_simdgroup: usize) -> usize {
101 (shape.m * shape.n) / threads_per_simdgroup
102}
103
104fn matrix_ty(ctx: &Context, ty: impl Typed) -> Ref<'_, MatrixType> {
105 let ty = ty.unwrap_ptr(ctx).deref(ctx);
106 Ref::map(ty, |ty| {
107 ty.downcast_ref::<MatrixType>().expect("Should be matrix")
108 })
109}