1use super::{
2 AddressSpace, Extension,
3 arch::MetalArchitecture,
4 extension::{format_fast_recip, format_ffs, format_hypot, format_mulhi, format_rhypot},
5 format_erf, format_global_binding_arg, format_metal_builtin_binding_arg, format_safe_tanh,
6};
7use crate::{
8 Dialect,
9 shared::{
10 self, Builtin, Component, CubeIndexFlags, DialectBindings, DialectCubeBuiltins,
11 DialectIncludes, DialectInstructions, DialectProcessors, DialectTypes,
12 DialectWarpReduceCompiler, DialectWmmaCompiler, Elem, Flags, FmtLeft, FragmentIdent,
13 FragmentLayout, FragmentType, Instruction, Item, KernelArg, ManualMma, SharedMemory,
14 SupportedMmaCombinations, Value, WarpInstruction, WmmaInstruction, wmma_api_base,
15 },
16};
17use core::panic;
18use cubecl_core::ir::{self as gpu, features::MmaConfig};
19use std::fmt::Display;
20
21#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
22pub struct MslDialect {}
23
24impl Dialect for MslDialect {
27 type Architecture = MetalArchitecture;
28}
29
30impl MslDialect {
31 fn warp_op_vectorized(
32 f: &mut core::fmt::Formatter<'_>,
33 input: &Value<Self>,
34 out: &Value<Self>,
35 simd_op_prefix: &str,
36 simd_op_suffix: &str,
37 ) -> core::fmt::Result {
38 let out = out.fmt_left();
39 let (open, in_open, in_close, close) = if matches!(input.item().elem(), Elem::BF16) {
41 ("bfloat(", "float(", ")", ")")
42 } else {
43 ("", "", "", "")
44 };
45
46 if let Item::Vector(_, vectorization) = input.item() {
47 f.write_fmt(format_args!("{out} = {} {{", input.item()))?;
48
49 for k in 0..vectorization {
50 let comma = if k + 1 < vectorization { "," } else { "" };
51 writeln!(
52 f,
53 "{open}{simd_op_prefix}{in_open}{input}.i_{k}{in_close}{simd_op_suffix}{close}{comma}"
54 )?;
55 }
56
57 f.write_fmt(format_args!("}};\n"))
58 } else {
59 writeln!(
60 f,
61 "{out} = {open}{simd_op_prefix}{in_open}{input}{in_close}{simd_op_suffix}{close};"
62 )
63 }
64 }
65
66 fn warp_shuffle(
67 f: &mut core::fmt::Formatter<'_>,
68 op: &str,
69 val: &str,
70 elem: &Elem<Self>,
71 arg: &str,
72 ) -> core::fmt::Result {
73 if matches!(elem, Elem::BF16) {
75 write!(f, "as_type<bfloat>({op}(as_type<ushort>({val}), {arg}))")
76 } else {
77 write!(f, "{op}({val}, {arg})")
78 }
79 }
80}
81
82impl DialectWarpReduceCompiler<Self> for MslDialect {
83 fn warp_reduce_sum(
84 f: &mut core::fmt::Formatter<'_>,
85 input: &Value<Self>,
86 out: &Value<Self>,
87 ) -> core::fmt::Result {
88 Self::warp_op_vectorized(f, input, out, "simd_sum(", ")")
89 }
90 fn warp_reduce_prod(
91 f: &mut core::fmt::Formatter<'_>,
92 input: &Value<Self>,
93 out: &Value<Self>,
94 ) -> core::fmt::Result {
95 Self::warp_op_vectorized(f, input, out, "simd_product(", ")")
96 }
97 fn warp_reduce_max(
98 f: &mut core::fmt::Formatter<'_>,
99 input: &Value<Self>,
100 out: &Value<Self>,
101 ) -> core::fmt::Result {
102 Self::warp_op_vectorized(f, input, out, "simd_max(", ")")
103 }
104 fn warp_reduce_min(
105 f: &mut core::fmt::Formatter<'_>,
106 input: &Value<Self>,
107 out: &Value<Self>,
108 ) -> core::fmt::Result {
109 Self::warp_op_vectorized(f, input, out, "simd_min(", ")")
110 }
111 fn warp_reduce_all(
112 f: &mut core::fmt::Formatter<'_>,
113 input: &Value<Self>,
114 out: &Value<Self>,
115 ) -> core::fmt::Result {
116 Self::warp_op_vectorized(f, input, out, "simd_and(", "? 1u : 0u) != 0u")
117 }
118 fn warp_reduce_any(
119 f: &mut core::fmt::Formatter<'_>,
120 input: &Value<Self>,
121 out: &Value<Self>,
122 ) -> core::fmt::Result {
123 Self::warp_op_vectorized(f, input, out, "simd_or(", "? 1u : 0u) != 0u")
124 }
125 fn warp_reduce_sum_inclusive(
126 f: &mut core::fmt::Formatter<'_>,
127 input: &Value<Self>,
128 out: &Value<Self>,
129 ) -> core::fmt::Result {
130 Self::warp_op_vectorized(f, input, out, "simd_prefix_inclusive_sum(", ")")
131 }
132 fn warp_reduce_prod_inclusive(
133 f: &mut core::fmt::Formatter<'_>,
134 input: &Value<Self>,
135 out: &Value<Self>,
136 ) -> core::fmt::Result {
137 Self::warp_op_vectorized(f, input, out, "simd_prefix_inclusive_product(", ")")
138 }
139 fn warp_reduce_sum_exclusive(
140 f: &mut core::fmt::Formatter<'_>,
141 input: &Value<Self>,
142 out: &Value<Self>,
143 ) -> core::fmt::Result {
144 Self::warp_op_vectorized(f, input, out, "simd_prefix_exclusive_sum(", ")")
145 }
146 fn warp_reduce_prod_exclusive(
147 f: &mut core::fmt::Formatter<'_>,
148 input: &Value<Self>,
149 out: &Value<Self>,
150 ) -> core::fmt::Result {
151 Self::warp_op_vectorized(f, input, out, "simd_prefix_exclusive_product(", ")")
152 }
153}
154
155impl DialectIncludes<Self> for MslDialect {
158 type Extension = Extension<Self>;
159
160 fn compile_includes(f: &mut std::fmt::Formatter<'_>, _flags: &Flags<Self>) -> std::fmt::Result {
161 write!(
162 f,
163 "
164#include <metal_stdlib>
165using namespace metal;
166"
167 )?;
168 Ok(())
169 }
170
171 fn compile_extensions(
172 f: &mut std::fmt::Formatter<'_>,
173 extensions: &[Self::Extension],
174 ) -> std::fmt::Result {
175 for extension in extensions {
176 match extension {
177 Extension::Erf(input, output) => format_erf::<Self>(f, input, output)?,
178 Extension::Ffs(elem) => format_ffs(f, elem)?,
179 Extension::MulHi(elem) => format_mulhi(f, elem)?,
180 Extension::SafeTanh(item) => format_safe_tanh::<Self>(f, item)?,
181 Extension::Hypot(elem) => format_hypot::<Self>(f, elem)?,
182 Extension::Rhypot(elem) => format_rhypot::<Self>(f, elem)?,
183 Extension::FastRecip => format_fast_recip(f)?,
184 Extension::NoExtension => {}
185 }
186 }
187 Ok(())
188 }
189
190 fn register_instruction_extension(
191 extensions: &mut Vec<Self::Extension>,
192 instruction: &Instruction<Self>,
193 ) {
194 let mut register_extension = |extension: Self::Extension| {
195 if !extensions.contains(&extension) {
196 extensions.push(extension);
197 }
198 };
199 #[allow(clippy::single_match)]
200 match instruction {
201 shared::Instruction::<Self>::Erf(instruction) => {
202 register_extension(Extension::Erf(
203 instruction.input.elem(),
204 instruction.out.elem(),
205 ));
206 }
207 shared::Instruction::<Self>::FindFirstSet(instruction) => {
208 let input_elem = instruction.input.elem();
209 match input_elem {
210 Elem::U32 | Elem::U64 => {
211 register_extension(Extension::Ffs(instruction.input.elem()));
212 }
213 Elem::I32 => {
214 register_extension(Extension::Ffs(Elem::<Self>::U32));
215 register_extension(Extension::Ffs(instruction.input.elem()));
216 }
217 Elem::I64 => {
218 register_extension(Extension::Ffs(Elem::<Self>::U64));
219 register_extension(Extension::Ffs(instruction.input.elem()));
220 }
221 _ => {
222 register_extension(Extension::Ffs(Elem::<Self>::U32));
223 }
224 }
225 }
226 shared::Instruction::<Self>::HiMul(instruction) => {
227 register_extension(Extension::MulHi(instruction.out.elem()));
228 }
229 shared::Instruction::<Self>::Tanh(instruction) => {
230 register_extension(Extension::SafeTanh(instruction.input.item()));
231 }
232 shared::Instruction::<Self>::Hypot(instruction) => {
233 let elem = match instruction.out.elem() {
235 Elem::F16 | Elem::F16x2 | Elem::BF16 | Elem::BF16x2 => Elem::F32,
236 other => other,
237 };
238 register_extension(Extension::Hypot(elem));
239 }
240 shared::Instruction::<Self>::Rhypot(instruction) => {
241 let elem = match instruction.out.elem() {
243 Elem::F16 | Elem::F16x2 | Elem::BF16 | Elem::BF16x2 => Elem::F32,
244 other => other,
245 };
246 register_extension(Extension::Rhypot(elem));
247 }
248 shared::Instruction::<Self>::FastRecip(_) => {
249 register_extension(Extension::FastRecip);
250 }
251 _ => {}
252 }
253 }
254
255 fn register_warp_instruction_extension(
256 _extensions: &mut Vec<Self::Extension>,
257 _instruction: &WarpInstruction<Self>,
258 ) {
259 }
260}
261
262impl DialectTypes<Self> for MslDialect {
265 fn item_can_be_optimized() -> bool {
266 false
267 }
268
269 fn compile_type_definitions(
270 f: &mut std::fmt::Formatter<'_>,
271 items: &std::collections::HashSet<crate::shared::Item<Self>>,
272 scalars: &[(Elem<Self>, usize)],
273 info: &cubecl_core::Info,
274 flags: &Flags<Self>,
275 ) -> std::fmt::Result {
276 for item in items.iter() {
277 if let Item::Vector(inner, vectorization) = item {
278 let alignment = item.size();
279 if *vectorization > 1 {
280 write!(
281 f,
282 "
283struct alignas({alignment}) {item} {{"
284 )?;
285
286 for i in 0..*vectorization {
287 write!(
288 f,
289 "
290 {inner} i_{i};"
291 )?;
292 }
293
294 f.write_str("\n};\n")?;
295 }
296 }
297 }
298
299 shared::type_info_definition_sized(f, info, scalars, flags.address_type)?;
300 Ok(())
301 }
302
303 fn compile_elem(
304 f: &mut std::fmt::Formatter<'_>,
305 elem: &shared::Elem<Self>,
306 _words: bool,
307 ) -> std::fmt::Result {
308 match elem {
310 shared::Elem::FP4(_)
311 | shared::Elem::FP4x2(_)
312 | shared::Elem::FP6(_)
313 | shared::Elem::FP6x2(_)
314 | shared::Elem::FP8(_)
315 | shared::Elem::FP8x2(_) => f.write_str("#error FP4/FP6/FP8 not supported in Metal\n"),
316 shared::Elem::F16 => f.write_str("half"),
317 shared::Elem::F16x2 => f.write_str("#error type F162 not supported!\n"),
318 shared::Elem::F32 => f.write_str("float"),
319 shared::Elem::F64 => f.write_str("#error type double not supported!\n"),
320 shared::Elem::BF16 => f.write_str("bfloat"),
321 shared::Elem::BF16x2 => f.write_str("#error type BF162 not supported!\n"),
322 shared::Elem::TF32 => f.write_str("float"),
323 shared::Elem::I8 => f.write_str("char"),
324 shared::Elem::I16 => f.write_str("short"),
325 shared::Elem::I32 => f.write_str("int"),
326 shared::Elem::I64 => f.write_str("long"),
327 shared::Elem::U8 => f.write_str("uchar"),
328 shared::Elem::U16 => f.write_str("ushort"),
329 shared::Elem::U32 => f.write_str("uint"),
330 shared::Elem::U64 => f.write_str("ulong"),
331 shared::Elem::Bool => f.write_str("bool"),
332 shared::Elem::None => f.write_str("<none>"),
333 shared::Elem::_Dialect(_) => Ok(()),
334 }
335 }
336
337 fn compile_item(f: &mut std::fmt::Formatter<'_>, item: &Item<Self>) -> std::fmt::Result {
338 match item {
339 Item::Scalar(elem) => write!(f, "{elem}"),
340 Item::Vector(inner, vectorization) => {
341 Self::compile_item(f, inner.as_ref())?;
342 write!(f, "_{vectorization}")
343 }
344 Item::NativeVector(elem, vectorization) => {
345 Self::compile_elem(f, elem, true)?;
346 write!(f, "{vectorization}")
347 }
348 Item::Atomic(inner) => {
349 write!(f, "atomic_{inner}")
350 }
351 Item::Pointer(inner, class) => {
352 let address_space = match class {
353 shared::PointerClass::Global(_)
356 if matches!(inner.value_ty(), Item::Atomic(_)) =>
357 {
358 AddressSpace::Device
359 }
360 shared::PointerClass::Global(vis) => (*vis).into(),
361 shared::PointerClass::Shared => AddressSpace::ThreadGroup,
362 shared::PointerClass::Local => AddressSpace::Thread,
363 };
364 write!(f, "{address_space} ")?;
365 match inner.as_ref() {
366 Item::DynamicArray(inner) => write!(f, "{inner}*"),
367 other => write!(f, "{other}*"),
368 }
369 }
370 Item::Array(inner, size) => {
371 write!(f, "array<{inner}, {size}>")
372 }
373 Item::DynamicArray(inner) => {
374 write!(f, "{inner}*")
375 }
376 Item::Fragment(fragment_type) => write!(f, "{fragment_type}"),
377 Item::Barrier(_) | Item::BarrierToken(_) => {
378 unimplemented!("metal doesn't support barrier object")
379 }
380 Item::TensorMap => unimplemented!("TensorMap not supported on Metal"),
381 }
382 }
383
384 fn address_space_for_value(value: &Value<Self>) -> String {
385 format!("{} ", AddressSpace::from(value))
386 }
387
388 fn compile_local_memory_qualifier(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
389 write!(f, "thread")
390 }
391
392 fn compile_shared_memory_declaration(
393 f: &mut std::fmt::Formatter<'_>,
394 shared: &SharedMemory<Self>,
395 ) -> std::fmt::Result {
396 let SharedMemory { ptr, offset, .. } = shared;
397 let ptr_ty = ptr.item();
398 let size_bytes = shared.size();
399 writeln!(f, "// Shared value size: {size_bytes} bytes")?;
400 writeln!(
401 f,
402 "{ptr_ty} {ptr} = reinterpret_cast<{ptr_ty}>(&dynamic_shared_mem[{offset}]);"
403 )
404 }
405}
406
407impl DialectBindings<Self> for MslDialect {
410 fn compile_kernel_signature(
411 f: &mut std::fmt::Formatter<'_>,
412 kernel_name: &str,
413 tensor_maps: &[KernelArg<Self>],
414 buffers: &[KernelArg<Self>],
415 flags: &Flags<Self>,
416 ) -> std::fmt::Result {
417 write!(
418 (f),
419 "
420[[kernel]]
421void {kernel_name}("
422 )?;
423 let mut buffer_idx = 0;
425 debug_assert!(
426 tensor_maps.is_empty(),
427 "Tensor maps aren't supported for metal"
428 );
429 for b in buffers.iter() {
430 format_global_binding_arg(b, &mut buffer_idx, f)?;
431 }
432
433 if flags.has_info {
434 let comma = if buffer_idx > 0 { "," } else { "" };
435 let (address_space, val) = match flags.has_dynamic_meta {
436 true => (AddressSpace::ConstDevice, "info_st* info_ptr"),
437 false => (AddressSpace::Constant, "info_st& info"),
438 };
439 let attribute = address_space.attribute();
440
441 write!(f, "{comma}\n {address_space} {val}",)?;
442 attribute.indexed_fmt(buffer_idx, f)?;
444 buffer_idx += 1;
445 }
446
447 let builtins = vec![
449 (
450 flags.indexes.absolute_pos_tuple,
451 Builtin::<Self>::AbsolutePosBaseName,
452 ),
453 (
454 flags.indexes.cube_dim_tuple,
455 Builtin::<Self>::CubeDimBaseName,
456 ),
457 (
458 flags.indexes.cube_count_tuple,
459 Builtin::<Self>::CubeCountBaseName,
460 ),
461 (flags.indexes.unit_pos, Builtin::<Self>::UnitPos),
462 (
463 flags.indexes.unit_pos_tuple,
464 Builtin::<Self>::UnitPosBaseName,
465 ),
466 (
467 flags.indexes.cube_pos_tuple,
468 Builtin::<Self>::CubePosBaseName,
469 ),
470 (flags.indexes.unit_pos_plane, Builtin::<Self>::UnitPosPlane),
471 (flags.indexes.plane_dim, Builtin::<Self>::PlaneDim),
472 (flags.indexes.plane_pos, Builtin::<Self>::PlanePos),
473 ];
474 let comma = buffer_idx > 0;
475 builtins
476 .iter()
477 .filter(|(cond, _)| *cond)
478 .try_for_each(|(_, val)| format_metal_builtin_binding_arg(f, val, comma))?;
479 f.write_str("\n)")
480 }
481
482 fn compile_bindings_body(
483 f: &mut std::fmt::Formatter<'_>,
484 body: &shared::Body<Self>,
485 ) -> std::fmt::Result {
486 if !body.shared_memories.is_empty() {
487 let size = body
488 .shared_memories
489 .iter()
490 .map(|it| it.offset + it.size())
491 .max()
492 .unwrap();
493
494 writeln!(f, "threadgroup uchar dynamic_shared_mem[{size}];",)?;
495 }
496 if body.info_by_ptr && body.has_dynamic_meta {
497 let address_space = AddressSpace::ConstDevice;
498 writeln!(f, "const {address_space} info_st& info = *info_ptr;")?;
499 writeln!(
500 f,
501 "const {address_space} {addr}* dynamic_meta = reinterpret_cast<const {address_space} {addr}*>(
502 reinterpret_cast<const {address_space} char*>(info_ptr) + sizeof(info_st)
503 );\n",
504 addr = body.address_type,
505 )?;
506 }
507 Ok(())
508 }
509}
510
511impl DialectCubeBuiltins<Self> for MslDialect {
514 fn builtin_rules(flags: &CubeIndexFlags) -> CubeIndexFlags {
516 let absolute_pos = flags.absolute_pos;
517 let cube_count = flags.cube_count;
518 let cube_dim = flags.cube_dim;
519 let cube_pos = flags.cube_pos;
520 let plane_dim_checked = flags.plane_dim_checked;
521 let plane_index = flags.plane_pos;
522 let unit_pos = flags.unit_pos;
523 let absolute_pos_tuple = flags.absolute_pos_tuple || absolute_pos;
524 let cube_count_tuple = flags.cube_count_tuple || cube_count || cube_pos || absolute_pos;
525 let cube_dim_tuple = flags.cube_dim_tuple || cube_dim || absolute_pos || plane_dim_checked;
526 let cube_pos_tuple = flags.cube_pos_tuple || cube_pos;
527 let cluster_pos = flags.cluster_pos;
528 let plane_dim = flags.plane_dim || plane_dim_checked || plane_index;
529 let unit_pos_plane = flags.unit_pos_plane || plane_index;
530 let unit_pos_tuple = flags.unit_pos_tuple || unit_pos;
531 CubeIndexFlags {
532 absolute_pos_tuple,
533 absolute_pos,
534 cube_count_tuple,
535 cube_count,
536 cube_dim_tuple,
537 cube_dim,
538 cube_pos_tuple,
539 cube_pos,
540 plane_dim,
541 plane_dim_checked,
542 plane_pos: plane_index,
543 unit_pos_tuple,
544 unit_pos,
545 unit_pos_plane,
546 cluster_pos,
547 }
548 }
549
550 fn compile_absolute_pos_tuple_computation(
551 _f: &mut std::fmt::Formatter<'_>,
552 ) -> std::fmt::Result {
553 Ok(())
555 }
556
557 fn compile_absolute_pos_base_name(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
558 f.write_str("thread_pos_in_grid")
559 }
560
561 fn compile_absolute_pos(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
562 f.write_str("thread_index_in_grid")
563 }
564
565 fn compile_absolute_pos_x(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
566 Self::compile_absolute_pos_base_name(f)?;
567 write!(f, ".x")
568 }
569
570 fn compile_absolute_pos_y(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
571 Self::compile_absolute_pos_base_name(f)?;
572 write!(f, ".y")
573 }
574
575 fn compile_absolute_pos_z(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
576 Self::compile_absolute_pos_base_name(f)?;
577 write!(f, ".z")
578 }
579
580 fn compile_cube_count_base_name(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
581 f.write_str("threadgroups_per_grid")
582 }
583
584 fn compile_cube_count(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
585 f.write_str("total_threadgroups_in_grid")
586 }
587
588 fn compile_cube_count_x(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
589 Self::compile_cube_count_base_name(f)?;
590 write!(f, ".x")
591 }
592
593 fn compile_cube_count_y(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
594 Self::compile_cube_count_base_name(f)?;
595 write!(f, ".y")
596 }
597
598 fn compile_cube_count_z(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
599 Self::compile_cube_count_base_name(f)?;
600 write!(f, ".z")
601 }
602
603 fn compile_cube_dim_base_name(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
604 f.write_str("threads_per_threadgroup")
605 }
606
607 fn compile_cube_dim(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
608 f.write_str("total_thread_in_threadgroup")
609 }
610
611 fn compile_cube_dim_x(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
612 Self::compile_cube_dim_base_name(f)?;
613 write!(f, ".x")
614 }
615
616 fn compile_cube_dim_y(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
617 Self::compile_cube_dim_base_name(f)?;
618 write!(f, ".y")
619 }
620
621 fn compile_cube_dim_z(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
622 Self::compile_cube_dim_base_name(f)?;
623 write!(f, ".z")
624 }
625
626 fn compile_cube_pos_base_name(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
627 f.write_str("threadgroup_pos_in_grid")
628 }
629
630 fn compile_cube_pos(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
631 f.write_str("threadgroup_index_in_grid")
632 }
633
634 fn compile_cube_pos_x(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
635 Self::compile_cube_pos_base_name(f)?;
636 write!(f, ".x")
637 }
638
639 fn compile_cube_pos_y(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
640 Self::compile_cube_pos_base_name(f)?;
641 write!(f, ".y")
642 }
643
644 fn compile_cube_pos_z(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
645 Self::compile_cube_pos_base_name(f)?;
646 write!(f, ".z")
647 }
648
649 fn compile_unit_pos_computation(_f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
650 Ok(())
652 }
653
654 fn compile_unit_pos_base_name(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
655 f.write_str("thread_pos_in_threadgroup")
656 }
657
658 fn compile_unit_pos(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
659 f.write_str("thread_index_in_threadgroup")
660 }
661
662 fn compile_unit_pos_x(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
663 Self::compile_unit_pos_base_name(f)?;
664 write!(f, ".x")
665 }
666
667 fn compile_unit_pos_y(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
668 Self::compile_unit_pos_base_name(f)?;
669 write!(f, ".y")
670 }
671
672 fn compile_unit_pos_z(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
673 Self::compile_unit_pos_base_name(f)?;
674 write!(f, ".z")
675 }
676
677 fn compile_plane_dim(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
678 f.write_str("simd_size")
679 }
680
681 fn compile_plane_dim_checked(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
682 f.write_str("threads_per_simdgroup_checked")
683 }
684
685 fn compile_plane_pos(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
686 f.write_str("simd_group_id")
687 }
688
689 fn compile_unit_pos_plane(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
690 f.write_str("simd_lane_id")
691 }
692}
693
694impl DialectInstructions<Self> for MslDialect {
697 fn compile_atomic_add(
699 f: &mut std::fmt::Formatter<'_>,
700 lhs: &Value<Self>,
701 rhs: &Value<Self>,
702 out: &Value<Self>,
703 ) -> std::fmt::Result {
704 let out = out.fmt_left();
705 writeln!(
706 f,
707 "{out} = atomic_fetch_add_explicit({lhs}, {rhs}, memory_order_relaxed);"
708 )
709 }
710
711 fn compile_atomic_and(
712 f: &mut std::fmt::Formatter<'_>,
713 lhs: &Value<Self>,
714 rhs: &Value<Self>,
715 out: &Value<Self>,
716 ) -> std::fmt::Result {
717 let out = out.fmt_left();
718 writeln!(
719 f,
720 "{out} = atomic_fetch_and_explicit({lhs}, {rhs}, memory_order_relaxed);"
721 )
722 }
723
724 fn compile_atomic_cas(
725 f: &mut std::fmt::Formatter<'_>,
726 input: &Value<Self>,
727 cmp: &Value<Self>,
728 val: &Value<Self>,
729 out: &Value<Self>,
730 ) -> std::fmt::Result {
731 let expected_name = format!("{out}_expected");
732 let out_item = out.item();
733 writeln!(f, "{out_item} {expected_name} = {cmp};")?;
734 writeln!(
735 f,
736 "atomic_compare_exchange_weak_explicit({input}, &{expected_name}, {val}, memory_order_relaxed, memory_order_relaxed);"
737 )?;
738 let out = out.fmt_left();
739 writeln!(f, "{out} = {expected_name};")
740 }
741
742 fn compile_atomic_load(
743 f: &mut std::fmt::Formatter<'_>,
744 input: &Value<Self>,
745 out: &Value<Self>,
746 ) -> std::fmt::Result {
747 let out = out.fmt_left();
748 writeln!(
749 f,
750 "{out} = atomic_load_explicit({input}, memory_order_relaxed);"
751 )
752 }
753
754 fn compile_atomic_max(
755 f: &mut std::fmt::Formatter<'_>,
756 lhs: &Value<Self>,
757 rhs: &Value<Self>,
758 out: &Value<Self>,
759 ) -> std::fmt::Result {
760 let out = out.fmt_left();
761 writeln!(
762 f,
763 "{out} = atomic_fetch_max_explicit({lhs}, {rhs}, memory_order_relaxed);"
764 )
765 }
766
767 fn compile_atomic_min(
768 f: &mut std::fmt::Formatter<'_>,
769 lhs: &Value<Self>,
770 rhs: &Value<Self>,
771 out: &Value<Self>,
772 ) -> std::fmt::Result {
773 let out = out.fmt_left();
774 writeln!(
775 f,
776 "{out} = atomic_fetch_min_explicit({lhs}, {rhs}, memory_order_relaxed);"
777 )
778 }
779
780 fn compile_atomic_or(
781 f: &mut std::fmt::Formatter<'_>,
782 lhs: &Value<Self>,
783 rhs: &Value<Self>,
784 out: &Value<Self>,
785 ) -> std::fmt::Result {
786 let out = out.fmt_left();
787 writeln!(
788 f,
789 "{out} = atomic_fetch_or_explicit({lhs}, {rhs}, memory_order_relaxed);"
790 )
791 }
792
793 fn compile_atomic_store(
794 f: &mut std::fmt::Formatter<'_>,
795 input: &Value<Self>,
796 out: &Value<Self>,
797 ) -> std::fmt::Result {
798 writeln!(
799 f,
800 "atomic_store_explicit({out}, {input}, memory_order_relaxed);"
801 )
802 }
803
804 fn compile_atomic_sub(
805 f: &mut std::fmt::Formatter<'_>,
806 lhs: &Value<Self>,
807 rhs: &Value<Self>,
808 out: &Value<Self>,
809 ) -> std::fmt::Result {
810 let out = out.fmt_left();
811 writeln!(
812 f,
813 "{out} = atomic_fetch_sub_explicit({lhs}, {rhs}, memory_order_relaxed);"
814 )
815 }
816
817 fn compile_atomic_swap(
818 f: &mut std::fmt::Formatter<'_>,
819 lhs: &Value<Self>,
820 rhs: &Value<Self>,
821 out: &Value<Self>,
822 ) -> std::fmt::Result {
823 let out = out.fmt_left();
824 writeln!(
825 f,
826 "{out} = atomic_exchange_explicit({lhs}, {rhs}, memory_order_relaxed);"
827 )
828 }
829
830 fn compile_atomic_xor(
831 f: &mut std::fmt::Formatter<'_>,
832 lhs: &Value<Self>,
833 rhs: &Value<Self>,
834 out: &Value<Self>,
835 ) -> std::fmt::Result {
836 let out = out.fmt_left();
837 writeln!(
838 f,
839 "{out} = atomic_fetch_xor_explicit({lhs}, {rhs}, memory_order_relaxed);"
840 )
841 }
842
843 fn compile_saturating_add(
844 f: &mut std::fmt::Formatter<'_>,
845 lhs: impl Display,
846 rhs: impl Display,
847 _item: Item<Self>,
848 ) -> std::fmt::Result {
849 write!(f, "addsat({lhs}, {rhs})")
850 }
851
852 fn compile_saturating_sub(
853 f: &mut std::fmt::Formatter<'_>,
854 lhs: impl Display,
855 rhs: impl Display,
856 _item: Item<Self>,
857 ) -> std::fmt::Result {
858 write!(f, "subsat({lhs}, {rhs})")
859 }
860
861 fn compile_instruction_printf(
863 f: &mut std::fmt::Formatter<'_>,
864 format_string: &str,
865 args: &[Value<Self>],
866 ) -> std::fmt::Result {
867 let args = args.iter().map(|arg| format!("{arg}")).collect::<Vec<_>>();
868 let args = match args.is_empty() {
869 true => "".to_string(),
870 false => format!(", {}", args.join(",")),
871 };
872 writeln!(f, "os_log_default.log({format_string:?}{args});")
873 }
874
875 fn compile_instruction_log1p_scalar<T: Component<Self>>(
877 f: &mut std::fmt::Formatter<'_>,
878 input: T,
879 ) -> std::fmt::Result {
880 match input.elem() {
881 Elem::F16 | Elem::F16x2 | Elem::BF16 | Elem::BF16x2 => {
882 write!(f, "log(half(1.0f) + {input})")
883 }
884 _ => write!(f, "log(1.0f + {input})"),
885 }
886 }
887
888 fn compile_instruction_expm1_scalar<T: Component<Self>>(
890 f: &mut std::fmt::Formatter<'_>,
891 input: T,
892 ) -> std::fmt::Result {
893 let elem = input.elem();
903 match elem {
904 Elem::F16 | Elem::F16x2 | Elem::BF16 | Elem::BF16x2 => {
907 write!(
908 f,
909 "{elem}(precise::exp(float({input})) == 1.0f ? float({input}) : (isinf(precise::exp(float({input}))) ? precise::exp(float({input})) : (precise::exp(float({input})) == 0.0f ? precise::exp(float({input})) - 1.0f : (precise::exp(float({input})) - 1.0f) * float({input}) / precise::log(precise::exp(float({input}))))))"
910 )
911 }
912 _ => write!(
913 f,
914 "(precise::exp({input}) == 1.0f ? {input} : (isinf(precise::exp({input})) ? precise::exp({input}) : (precise::exp({input}) == 0.0f ? precise::exp({input}) - 1.0f : (precise::exp({input}) - 1.0f) * {input} / precise::log(precise::exp({input})))))"
915 ),
916 }
917 }
918
919 fn compile_instruction_sync_threads(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
921 writeln!(f, "threadgroup_barrier(mem_flags::mem_threadgroup);")
922 }
923
924 fn compile_instruction_sync_warp(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
925 writeln!(f, "simdgroup_barrier(mem_flags::mem_none);")
926 }
927
928 fn compile_instruction_thread_fence(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
929 writeln!(f, "threadgroup_thread_fence(mem_flags::mem_device);")
930 }
931
932 fn compile_instruction_tanh_scalar<T: Component<Self>>(
934 f: &mut std::fmt::Formatter<'_>,
935 input: T,
936 ) -> std::fmt::Result {
937 write!(f, "safe_tanh_scalar({input})")
938 }
939
940 fn compile_instruction_find_first_set<T: Component<Self>>(
942 f: &mut std::fmt::Formatter<'_>,
943 input: T,
944 out_elem: Elem<Self>,
945 ) -> std::fmt::Result {
946 write!(f, "{out_elem}(")?;
947 match input.elem() {
948 Elem::I32 | Elem::U32 => write!(f, "__ffs({input})"),
949 Elem::I64 | Elem::U64 => write!(f, "__ffsll({input})"),
950 _ => write!(f, "__ffs({}({input}))", Elem::<Self>::I32),
951 }?;
952 write!(f, ")")
953 }
954
955 fn compile_instruction_leading_zeros_scalar<T: Component<Self>>(
956 f: &mut std::fmt::Formatter<'_>,
957 input: T,
958 out_elem: Elem<Self>,
959 ) -> std::fmt::Result {
960 write!(f, "{out_elem}(clz({input}))")
961 }
962
963 fn compile_instruction_trailing_zeros_scalar<T: Component<Self>>(
964 f: &mut std::fmt::Formatter<'_>,
965 input: T,
966 out_elem: Elem<Self>,
967 ) -> std::fmt::Result {
968 write!(f, "{out_elem}(ctz({input}))")
969 }
970
971 fn compile_instruction_popcount_scalar<T: Component<Self>>(
972 f: &mut std::fmt::Formatter<'_>,
973 input: T,
974 out_elem: Elem<Self>,
975 ) -> std::fmt::Result {
976 write!(f, "{out_elem}(")?;
977 match input.elem() {
978 Elem::I32 | Elem::U32 | Elem::I64 | Elem::U64 => write!(f, "popcount({input})"),
979 _ => write!(f, "popcount({})", shared::unary::zero_extend(input)),
980 }?;
981 write!(f, ")")
982 }
983
984 fn compile_instruction_reverse_bits_scalar<T: Component<Self>>(
985 f: &mut std::fmt::Formatter<'_>,
986 input: T,
987 out_elem: Elem<Self>,
988 ) -> std::fmt::Result {
989 write!(f, "{out_elem}(")?;
990 match out_elem {
991 Elem::I32 | Elem::U32 | Elem::I64 | Elem::U64 => write!(f, "reverse_bits({input})"),
992 _ => write!(
993 f,
994 "reverse_bits({}) >> {}",
995 shared::unary::zero_extend(input),
996 (size_of::<u32>() - out_elem.size()) * 8
997 ),
998 }?;
999 write!(f, ")")
1000 }
1001
1002 fn compile_instruction_max_function_name(
1004 f: &mut std::fmt::Formatter<'_>,
1005 _item: Item<Self>,
1006 ) -> std::fmt::Result {
1007 write!(f, "max")
1008 }
1009
1010 fn compile_instruction_min_function_name(
1011 f: &mut std::fmt::Formatter<'_>,
1012 _item: Item<Self>,
1013 ) -> std::fmt::Result {
1014 write!(f, "min")
1015 }
1016
1017 fn compile_instruction_powf(
1018 f: &mut std::fmt::Formatter<'_>,
1019 lhs: &str,
1020 rhs: &str,
1021 elem: Elem<Self>,
1022 ) -> std::fmt::Result {
1023 write!(f, "pow({lhs}, {elem}({rhs}))")
1024 }
1025
1026 fn compile_instruction_hypot(
1027 f: &mut std::fmt::Formatter<'_>,
1028 lhs: &str,
1029 rhs: &str,
1030 _elem: Elem<Self>,
1031 ) -> std::fmt::Result {
1032 write!(f, "hypot({lhs}, {rhs})")
1033 }
1034
1035 fn compile_instruction_rhypot(
1036 f: &mut std::fmt::Formatter<'_>,
1037 lhs: &str,
1038 rhs: &str,
1039 _elem: Elem<Self>,
1040 ) -> std::fmt::Result {
1041 write!(f, "rhypot({lhs}, {rhs})")
1042 }
1043
1044 fn compile_instruction_half_function_name_prefix() -> &'static str {
1045 ""
1046 }
1047
1048 fn compile_instruction_half2_function_name_prefix() -> &'static str {
1049 ""
1050 }
1051
1052 fn compile_fast_math_function_name(name: &'static str) -> &'static str {
1053 match name {
1055 "__expf" => "fast::exp",
1056 "__logf" => "fast::log",
1057 "__sinf" => "fast::sin",
1058 "__cosf" => "fast::cos",
1059 "__fsqrt_rn" => "fast::sqrt",
1060 "__frsqrt_rn" => "fast::rsqrt",
1061 "__tanhf" => "fast::tanh",
1062 "__fdividef" => "fast::divide",
1063 "__powf" => "fast::pow",
1064 "__frcp_rn" => "fast_recip",
1065 other => other,
1066 }
1067 }
1068
1069 fn compile_warp_shuffle(
1071 f: &mut std::fmt::Formatter<'_>,
1072 val: &str,
1073 elem: &Elem<Self>,
1074 source: &str,
1075 ) -> std::fmt::Result {
1076 Self::warp_shuffle(f, "simd_shuffle", val, elem, source)
1077 }
1078
1079 fn compile_warp_shuffle_xor(
1080 f: &mut std::fmt::Formatter<'_>,
1081 val: &str,
1082 elem: &Elem<Self>,
1083 offset: &str,
1084 ) -> std::fmt::Result {
1085 Self::warp_shuffle(f, "simd_shuffle_xor", val, elem, offset)
1086 }
1087
1088 fn compile_warp_shuffle_up(
1089 f: &mut std::fmt::Formatter<'_>,
1090 val: &str,
1091 elem: &Elem<Self>,
1092 offset: &str,
1093 ) -> std::fmt::Result {
1094 Self::warp_shuffle(f, "simd_shuffle_up", val, elem, offset)
1095 }
1096
1097 fn compile_warp_shuffle_down(
1098 f: &mut std::fmt::Formatter<'_>,
1099 val: &str,
1100 elem: &Elem<Self>,
1101 offset: &str,
1102 ) -> std::fmt::Result {
1103 Self::warp_shuffle(f, "simd_shuffle_down", val, elem, offset)
1104 }
1105
1106 fn compile_warp_all<T: Component<Self>>(
1107 f: &mut std::fmt::Formatter<'_>,
1108 input: &T,
1109 ) -> std::fmt::Result {
1110 write!(f, "simd_all({input})")
1111 }
1112
1113 fn compile_warp_any<T: Component<Self>>(
1114 f: &mut std::fmt::Formatter<'_>,
1115 input: &T,
1116 ) -> std::fmt::Result {
1117 write!(f, "simd_any({input})")
1118 }
1119
1120 fn compile_warp_ballot(
1121 f: &mut std::fmt::Formatter<'_>,
1122 input: &Value<Self>,
1123 out_elem: &Elem<Self>,
1124 ) -> std::fmt::Result {
1125 write!(f, "{out_elem}(uint64_t(simd_ballot({input})))")
1126 }
1127
1128 fn compile_unreachable(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1129 write!(f, "__builtin_unreachable();")
1130 }
1131}
1132
1133impl DialectWmmaCompiler<Self> for MslDialect {
1136 fn compile_wmma_includes(
1137 f: &mut std::fmt::Formatter<'_>,
1138 _flags: &Flags<Self>,
1139 ) -> std::fmt::Result {
1140 writeln!(f, "#include <metal_simdgroup_matrix>")
1141 }
1142
1143 fn compile_wmma_local_variables(_f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1144 Ok(())
1146 }
1147
1148 fn compile_wmma_fragment_declaration(
1149 f: &mut std::fmt::Formatter<'_>,
1150 val: &crate::shared::Value<MslDialect>,
1151 ty: &crate::shared::Item<MslDialect>,
1152 ) -> std::fmt::Result {
1153 wmma_api_base::compile_fragment_declaration(f, val, ty)
1154 }
1155
1156 fn compile_wwma_fragment_ident(
1157 _f: &mut std::fmt::Formatter<'_>,
1158 _ident: &FragmentIdent<Self>,
1159 ) -> std::fmt::Result {
1160 Ok(())
1162 }
1163
1164 fn compile_wmma_fragment_layout(
1165 _f: &mut std::fmt::Formatter<'_>,
1166 _layout: &FragmentLayout<Self>,
1167 ) -> std::fmt::Result {
1168 Ok(())
1170 }
1171
1172 fn compile_wmma_fragment(
1173 f: &mut std::fmt::Formatter<'_>,
1174 fragment: &FragmentType<Self>,
1175 ) -> std::fmt::Result {
1176 let ty = fragment.elem;
1177 let m = fragment.m;
1179 let n = fragment.n;
1180 let k = fragment.k;
1181 if m != 8 || n != 8 || k != 8 {
1182 panic!("{m}x{n}x{k} fragments not supported. Only 8x8x8 fragments are supported.");
1183 }
1184 write!(f, "simdgroup_{ty}8x8")
1185 }
1186
1187 fn compile_wmma_instruction(
1188 f: &mut std::fmt::Formatter<'_>,
1189 instruction: &WmmaInstruction<Self>,
1190 ) -> std::fmt::Result {
1191 match instruction {
1192 WmmaInstruction::Fill { frag, value } => {
1193 match *frag.item().value_ty() {
1194 Item::Fragment { .. } => {
1195 let ty = frag.elem();
1196 writeln!(
1198 f,
1199 "*{frag} = make_filled_simdgroup_matrix<{ty}, 8, 8>({value});"
1200 )
1201 }
1202 _ => panic!("should be a fragment"),
1203 }
1204 }
1205 WmmaInstruction::Load {
1206 frag,
1207 ptr,
1208 stride,
1209 layout: _layout,
1210 } => {
1211 let transpose = match *frag.item().value_ty() {
1212 Item::Fragment(inner) => match inner.layout {
1213 Some(FragmentLayout::RowMajor) => false,
1214 Some(FragmentLayout::ColMajor) => true,
1215 _ => false,
1216 },
1217 _ => panic!("should be a fragment"),
1218 };
1219 if let Item::Vector(..) = *ptr.item().value_ty() {
1220 let elem_ptr = ptr.item().as_scalar();
1221 writeln!(
1222 f,
1223 "simdgroup_load(*{frag}, ({elem_ptr})({ptr}), {stride}, 0, {transpose});"
1224 )
1225 } else {
1226 writeln!(
1227 f,
1228 "simdgroup_load(*{frag}, {ptr}, {stride}, 0, {transpose});"
1229 )
1230 }
1231 }
1232 WmmaInstruction::Execute {
1233 frag_a: a,
1234 frag_b: b,
1235 frag_c: c,
1236 frag_d: d,
1237 ..
1238 } => {
1239 writeln!(f, "simdgroup_multiply_accumulate(*{d}, {a}, {b}, {c});")
1240 }
1241 WmmaInstruction::Store {
1242 frag,
1243 stride,
1244 destination,
1245 layout: _layout,
1246 } => {
1247 let item = destination.item();
1248 let mut reinterpret_cast = item.vectorization() > 1;
1249 let elem = match item.value_ty().elem() {
1250 Elem::BF16 => {
1251 reinterpret_cast = true;
1252 Elem::F16
1253 }
1254 _ => *item.elem(),
1255 };
1256 let scalar_ptr = item.as_scalar().with_elem(elem);
1257 if reinterpret_cast {
1258 writeln!(
1259 f,
1260 "simdgroup_store({frag}, reinterpret_cast<{scalar_ptr}>({destination}), {stride});"
1261 )
1262 } else {
1263 writeln!(f, "simdgroup_store({frag}, {destination}, {stride});")
1264 }?;
1265 writeln!(f, "simdgroup_barrier(mem_flags::mem_none);")
1266 }
1267 WmmaInstruction::Cast { input, output } => {
1268 writeln!(f, "simdgroup_barrier(mem_flags::mem_none);")?;
1269 let ty = match *output.item().value_ty() {
1270 Item::Fragment(frag) => frag.elem,
1271 _ => panic!("should be a fragment"),
1272 };
1273 match ty {
1274 Elem::BF16 => {
1275 let addr_space = Self::address_space_for_value(output);
1276 let elem = Elem::<Self>::F16;
1277 writeln!(
1280 f,
1281 "for(int e=0; e<8; e++) {{
1282 {ty} elem = {ty}({input}.thread_elements()[e]);
1283 {output}->thread_elements()[e] = *reinterpret_cast<{addr_space}{elem} *>(&elem);
1284}}"
1285 )
1286 }
1287 _ => {
1288 writeln!(
1289 f,
1290 "for(int e=0; e<8; e++) {{
1291 {output}->thread_elements()[e] = {ty}({input}.thread_elements()[e]);
1292}}"
1293 )
1294 }
1295 }
1296 }
1297 WmmaInstruction::ExecuteManual {
1298 shape,
1299 frag_a,
1300 frag_b,
1301 frag_c,
1302 frag_d,
1303 } => {
1304 Self::compile_manual_mma(f, ManualMma::new(*shape, frag_a, frag_b, frag_c, frag_d))
1305 }
1306 WmmaInstruction::ExecuteScaled {
1307 shape,
1308 frag_a,
1309 frag_b,
1310 frag_c,
1311 frag_d,
1312 scales_a,
1313 scales_b,
1314 scales_factor,
1315 } => Self::compile_scaled_mma(
1316 f,
1317 ManualMma::new(*shape, frag_a, frag_b, frag_c, frag_d),
1318 *scales_a,
1319 *scales_b,
1320 *scales_factor,
1321 ),
1322 WmmaInstruction::LdMatrix { .. } | WmmaInstruction::StMatrix { .. } => {
1323 f.write_str("#error WmmaInstruction Ld & St Matrix not supported on Metal\n")
1324 }
1325 }
1326 }
1327
1328 fn compile_manual_mma(
1329 f: &mut std::fmt::Formatter<'_>,
1330 _mma: shared::ManualMma<Self>,
1331 ) -> std::fmt::Result {
1332 f.write_str("#error manual mma not supported on Metal\n")
1333 }
1334
1335 fn compile_scaled_mma(
1336 f: &mut std::fmt::Formatter<'_>,
1337 _mma: shared::ManualMma<Self>,
1338 _scales_a: Value<Self>,
1339 _scales_b: Value<Self>,
1340 _scales_factor: u32,
1341 ) -> std::fmt::Result {
1342 f.write_str("#error scaled mma not supported on Metal\n")
1343 }
1344
1345 fn supported_wmma_combinations(_arch: &MetalArchitecture) -> SupportedMmaCombinations {
1346 let types = vec![
1347 (
1348 gpu::ElemType::Float(gpu::FloatKind::F16).into(),
1349 gpu::ElemType::Float(gpu::FloatKind::F16).into(),
1350 gpu::ElemType::Float(gpu::FloatKind::F16).into(),
1351 ),
1352 (
1353 gpu::ElemType::Float(gpu::FloatKind::F16).into(),
1354 gpu::ElemType::Float(gpu::FloatKind::F16).into(),
1355 gpu::ElemType::Float(gpu::FloatKind::F32).into(),
1356 ),
1357 (
1358 gpu::ElemType::Float(gpu::FloatKind::BF16).into(),
1359 gpu::ElemType::Float(gpu::FloatKind::BF16).into(),
1360 gpu::ElemType::Float(gpu::FloatKind::BF16).into(),
1361 ),
1362 (
1363 gpu::ElemType::Float(gpu::FloatKind::F32).into(),
1364 gpu::ElemType::Float(gpu::FloatKind::F32).into(),
1365 gpu::ElemType::Float(gpu::FloatKind::F32).into(),
1366 ),
1367 ];
1368 types
1369 .into_iter()
1370 .map(|(a_type, b_type, cd_type)| MmaConfig {
1371 a_type,
1372 b_type,
1373 cd_type,
1374 m: 8,
1375 n: 8,
1376 k: 8,
1377 })
1378 .collect()
1379 }
1380
1381 fn supported_mma_combinations(_arch: &MetalArchitecture) -> SupportedMmaCombinations {
1382 Vec::new()
1383 }
1384}
1385
1386impl DialectProcessors<Self> for MslDialect {
1389 fn processors() -> Vec<Box<dyn gpu::Processor>> {
1390 Vec::new()
1391 }
1392}