Skip to main content

cubecl_cpp/hip/
dialect.rs

1use core::any::TypeId;
2use std::fmt::Display;
3use std::{collections::HashSet, marker::PhantomData};
4
5use cubecl_core::{
6    ir::Processor, post_processing::saturating::SaturatingArithmeticProcessor, prelude::Visibility,
7};
8
9use crate::shared::{DialectWarpReduceCompiler, PointerClass};
10use crate::{
11    Dialect,
12    shared::{
13        self, DialectBindings, DialectCubeBuiltins, DialectIncludes, DialectTypes,
14        DialectWmmaCompiler, Flags, Item, KernelArg, ManualMma,
15    },
16};
17use crate::{
18    hip::processors::HipMmaProcessor,
19    shared::{
20        Component, DialectInstructions, DialectProcessors, Elem, Instruction, Value, unary,
21        value_to_frag,
22    },
23};
24
25use super::Extension;
26use super::arch::AMDArchitecture;
27use super::extension::{WmmaExtension, format_f162bf16, format_max, format_min};
28use super::mma::{WmmaCast, WmmaExecute, WmmaFill, WmmaIntrinsicCompiler, WmmaLoad, WmmaStore};
29
30#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
31pub struct HipDialect<M> {
32    _wmma_compiler: PhantomData<M>,
33}
34
35// Base dialect
36
37impl<M: DialectWmmaCompiler<Self>> Dialect for HipDialect<M> {
38    type Architecture = AMDArchitecture;
39}
40
41impl<M: DialectWmmaCompiler<Self>> DialectWarpReduceCompiler<Self> for HipDialect<M> {}
42
43// Includes
44
45impl<M: DialectWmmaCompiler<Self>> DialectIncludes<Self> for HipDialect<M> {
46    type Extension = Extension<Self>;
47
48    fn compile_includes(f: &mut std::fmt::Formatter<'_>, flags: &Flags<Self>) -> std::fmt::Result {
49        f.write_str("#include <hip/hip_runtime.h>\n")?;
50        if flags.elem_bf16 {
51            f.write_str("#include <hip/hip_bf16.h>\n")?;
52        }
53        if flags.elem_f16 {
54            f.write_str("#include <hip/hip_fp16.h>\n")?;
55        }
56        if flags.inst_wmma {
57            Self::compile_wmma_includes(f, flags)?;
58        }
59        Ok(())
60    }
61
62    fn compile_extensions(
63        f: &mut std::fmt::Formatter<'_>,
64        extensions: &[Self::Extension],
65    ) -> std::fmt::Result {
66        for extension in extensions {
67            match extension {
68                Extension::F162BF16 => format_f162bf16(f)?,
69                Extension::Max(val) => format_max::<Self>(f, val)?,
70                Extension::Min(val) => format_min::<Self>(f, val)?,
71                Extension::NoExtension => {}
72                Extension::Wmma(inst) => inst.format_wmma(f)?,
73            }
74        }
75        Ok(())
76    }
77
78    fn register_instruction_extension(
79        extensions: &mut Vec<Self::Extension>,
80        instruction: &Instruction<Self>,
81    ) {
82        let mut register_extension = |extension: Self::Extension| {
83            if !extensions.contains(&extension) {
84                extensions.push(extension);
85            }
86        };
87        #[allow(clippy::single_match)]
88        match instruction {
89            shared::Instruction::<Self>::Max(op) => {
90                register_extension(Extension::Max(*op.lhs.item().elem()));
91            }
92            shared::Instruction::<Self>::Min(op) => {
93                register_extension(Extension::Min(*op.lhs.item().elem()));
94            }
95            _ => {}
96        }
97    }
98
99    fn register_warp_instruction_extension(
100        extensions: &mut Vec<Self::Extension>,
101        instruction: &shared::WarpInstruction<Self>,
102    ) {
103        let mut register_extension = |extension: Self::Extension| {
104            if !extensions.contains(&extension) {
105                extensions.push(extension);
106            }
107        };
108
109        #[allow(clippy::single_match)]
110        match instruction {
111            shared::WarpInstruction::<Self>::ReduceMax { input, .. } => {
112                let input_item = input.item();
113                let input_elem = input_item.elem();
114                if *input_elem == Elem::<Self>::BF16 {
115                    register_extension(Extension::F162BF16);
116                }
117                register_extension(Extension::Max(*input_elem));
118            }
119            shared::WarpInstruction::<Self>::ReduceMin { input, .. } => {
120                let input_item = input.item();
121                let input_elem = input_item.elem();
122                if *input_elem == Elem::<Self>::BF16 {
123                    register_extension(Extension::F162BF16);
124                }
125                register_extension(Extension::Min(*input_elem));
126            }
127            shared::WarpInstruction::<Self>::ReduceProd { input, .. } => {
128                let input_item = input.item();
129                let input_elem = input_item.elem();
130                if *input_elem == Elem::<Self>::BF16 {
131                    register_extension(Extension::F162BF16);
132                }
133            }
134            shared::WarpInstruction::<Self>::ReduceSum { input, .. } => {
135                let input_item = input.item();
136                let input_elem = input_item.elem();
137                if *input_elem == Elem::<Self>::BF16 {
138                    register_extension(Extension::F162BF16);
139                }
140            }
141            _ => {}
142        }
143    }
144
145    fn register_wmma_instruction_extension(
146        extensions: &mut Vec<Self::Extension>,
147        instruction: &shared::WmmaInstruction<Self>,
148    ) {
149        if TypeId::of::<M>() == TypeId::of::<WmmaIntrinsicCompiler>() {
150            let extension = match instruction {
151                shared::WmmaInstruction::Fill { frag, .. } => {
152                    Extension::Wmma(WmmaExtension::Fill(WmmaFill::new(value_to_frag(frag))))
153                }
154                shared::WmmaInstruction::Load { frag, layout, .. } => Extension::Wmma(
155                    WmmaExtension::Load(WmmaLoad::new(value_to_frag(frag), *layout)),
156                ),
157                shared::WmmaInstruction::LdMatrix { .. }
158                | shared::WmmaInstruction::StMatrix { .. } => {
159                    panic!("Invalid extension: StMatrix & LdMatrix not supported for HIP");
160                }
161                shared::WmmaInstruction::Execute {
162                    frag_a,
163                    frag_b,
164                    frag_c,
165                    frag_d,
166                    warp_size: _,
167                } => Extension::Wmma(WmmaExtension::Execute(WmmaExecute::new(
168                    value_to_frag(frag_a),
169                    value_to_frag(frag_b),
170                    value_to_frag(frag_c),
171                    value_to_frag(frag_d),
172                ))),
173                shared::WmmaInstruction::ExecuteManual {
174                    shape,
175                    frag_a,
176                    frag_c,
177                    ..
178                } => Extension::Wmma(WmmaExtension::Execute(WmmaExecute::from_manual(
179                    *shape,
180                    frag_a.elem(),
181                    frag_c.elem(),
182                ))),
183                shared::WmmaInstruction::ExecuteScaled { .. } => {
184                    panic!("Invalid extension: ExecuteScaled not supported for HIP");
185                }
186                shared::WmmaInstruction::Store { frag, layout, .. } => Extension::Wmma(
187                    WmmaExtension::Store(WmmaStore::new(value_to_frag(frag), *layout)),
188                ),
189                shared::WmmaInstruction::Cast { input, output } => Extension::Wmma(
190                    WmmaExtension::Cast(WmmaCast::new(value_to_frag(input), value_to_frag(output))),
191                ),
192            };
193
194            if !extensions.contains(&extension) {
195                extensions.push(extension);
196            }
197        } else if let shared::WmmaInstruction::ExecuteManual {
198            shape,
199            frag_a,
200            frag_c,
201            ..
202        } = instruction
203        {
204            let extension = Extension::Wmma(WmmaExtension::Execute(WmmaExecute::from_manual(
205                *shape,
206                frag_a.elem(),
207                frag_c.elem(),
208            )));
209
210            if !extensions.contains(&extension) {
211                extensions.push(extension);
212            }
213        }
214    }
215}
216
217// Types
218
219impl<M: DialectWmmaCompiler<Self>> DialectTypes<Self> for HipDialect<M> {
220    fn item_can_be_optimized() -> bool {
221        // for now deactivate support for half2 and bfloat162 because the HIP API lack support for it.
222        false
223    }
224
225    fn compile_type_definitions(
226        f: &mut std::fmt::Formatter<'_>,
227        items: &HashSet<Item<Self>>,
228        scalars: &[(Elem<Self>, usize)],
229        info: &cubecl_core::Info,
230        flags: &Flags<Self>,
231    ) -> std::fmt::Result {
232        let mut items_deduplicated = HashSet::new();
233
234        for item in items {
235            let mut item = *item.value_ty();
236            match item {
237                Item::NativeVector(..) => {
238                    continue;
239                }
240                Item::Atomic(inner) => {
241                    item = *inner;
242                }
243                _ => {}
244            }
245            items_deduplicated.insert(item);
246        }
247
248        shared::type_definitions::<Self>(f)?;
249        shared::type_vectorized_definitions::<Self>(f, &items_deduplicated)?;
250
251        shared::type_info_definition_sized(f, info, scalars, flags.address_type)?;
252
253        if flags.inst_wmma {
254            Self::compile_wmma_type_definitions(f, flags)?;
255        }
256
257        Ok(())
258    }
259
260    fn compile_elem(
261        f: &mut std::fmt::Formatter<'_>,
262        elem: &shared::Elem<Self>,
263        words: bool,
264    ) -> std::fmt::Result {
265        if words {
266            match elem {
267                shared::Elem::F32 => f.write_str("float"),
268                shared::Elem::F64 => f.write_str("double"),
269                shared::Elem::TF32 => f.write_str("float"),
270                shared::Elem::I8 => f.write_str("char"),
271                shared::Elem::I16 => f.write_str("short"),
272                shared::Elem::I32 => f.write_str("int"),
273                shared::Elem::I64 => f.write_str("long"),
274                shared::Elem::U8 => f.write_str("uchar"),
275                shared::Elem::U16 => f.write_str("ushort"),
276                shared::Elem::U32 => f.write_str("uint"),
277                shared::Elem::U64 => f.write_str("ulong"),
278                _ => Self::compile_elem(f, elem, false),
279            }
280        } else {
281            match elem {
282                shared::Elem::FP4(_)
283                | shared::Elem::FP4x2(_)
284                | shared::Elem::FP6(_)
285                | shared::Elem::FP6x2(_)
286                | shared::Elem::FP8(_)
287                | shared::Elem::FP8x2(_) => {
288                    f.write_str("#error FP4/FP6/FP8 not supported in HIP\n")
289                }
290                shared::Elem::F16 => f.write_str("__half"),
291                shared::Elem::F16x2 => f.write_str("__half2"),
292                shared::Elem::F32 => f.write_str("float"),
293                shared::Elem::F64 => f.write_str("double"),
294                shared::Elem::BF16 => f.write_str("__hip_bfloat16"),
295                shared::Elem::BF16x2 => f.write_str("__hip_bfloat162"),
296                shared::Elem::TF32 => f.write_str("float"),
297                shared::Elem::I8 => f.write_str("int8"),
298                shared::Elem::I16 => f.write_str("int16"),
299                shared::Elem::I32 => f.write_str("int32"),
300                shared::Elem::I64 => f.write_str("int64"),
301                shared::Elem::U8 => f.write_str("uint8"),
302                shared::Elem::U16 => f.write_str("uint16"),
303                shared::Elem::U32 => f.write_str("uint32"),
304                shared::Elem::U64 => f.write_str("uint64"),
305                shared::Elem::Bool => f.write_str("bool"),
306                shared::Elem::None => f.write_str("<none>"),
307                shared::Elem::_Dialect(_) => Ok(()),
308            }
309        }
310    }
311
312    fn compile_item(f: &mut std::fmt::Formatter<'_>, item: &Item<Self>) -> std::fmt::Result {
313        match item {
314            Item::Scalar(elem) => write!(f, "{elem}"),
315            Item::Vector(inner, vectorization) => {
316                write!(f, "{inner}_{vectorization}")
317            }
318            Item::NativeVector(elem, vectorization) => {
319                Self::compile_elem(f, elem, true)?;
320                write!(f, "{vectorization}")
321            }
322            Item::Atomic(inner) => Self::compile_item(f, inner.as_ref()),
323            Item::Pointer(inner, class) => {
324                if let PointerClass::Global(Visibility::Read | Visibility::Uniform) = class {
325                    f.write_str("const ")?;
326                }
327                match inner.as_ref() {
328                    Item::DynamicArray(inner) => write!(f, "{inner}*"),
329                    other => write!(f, "{other}*"),
330                }
331            }
332            Item::Array(inner, size) => {
333                write!(f, "array<{inner}, {size}>")
334            }
335            Item::DynamicArray(inner) => {
336                write!(f, "{inner}*")
337            }
338            Item::Fragment(fragment_type) => write!(f, "{fragment_type}"),
339            Item::Barrier(_) | Item::BarrierToken(_) => {
340                panic!("Barrier object not supported in HIP")
341            }
342            Item::TensorMap => panic!("TensorMap not supported on HIP"),
343        }
344    }
345
346    fn compile_local_memory_qualifier(_f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
347        Ok(())
348    }
349}
350
351// Kernel argument bindings
352
353impl<M: DialectWmmaCompiler<Self>> DialectBindings<Self> for HipDialect<M> {
354    fn compile_kernel_signature(
355        f: &mut std::fmt::Formatter<'_>,
356        kernel_name: &str,
357        tensor_maps: &[KernelArg<Self>],
358        buffers: &[KernelArg<Self>],
359        flags: &Flags<Self>,
360    ) -> std::fmt::Result {
361        write!(
362            f,
363            "
364
365extern \"C\" __global__ void __launch_bounds__({}) {kernel_name}(
366",
367            flags.cube_dim.num_elems()
368        )?;
369        shared::compile_bindings::<Self>(f, tensor_maps, buffers, flags.has_info)?;
370        shared::compile_info_dynamic::<Self>(f, flags)?;
371        f.write_str("\n)")?;
372
373        Ok(())
374    }
375
376    fn compile_bindings_body(
377        f: &mut std::fmt::Formatter<'_>,
378        body: &shared::Body<Self>,
379    ) -> std::fmt::Result {
380        if !body.shared_memories.is_empty() {
381            let max_align = body
382                .shared_memories
383                .iter()
384                .map(|smem| smem.align)
385                .max()
386                .unwrap();
387            // The `__align__` instead of `alignas` is on purpose - the compiler is currently bugged
388            // with `extern __shared__ alignas` and doesn't properly parse it.
389            writeln!(
390                f,
391                "extern __shared__ __align__({max_align}) uchar dynamic_shared_mem[];"
392            )?;
393        }
394        if body.info_by_ptr {
395            f.write_str("const info_st& info = *info_ptr;\n")?;
396            // Could use `info_ptr + 1` but that seems dirty, so use manual `sizeof` instead
397            writeln!(
398                f,
399                "const {addr}* dynamic_meta = reinterpret_cast<const {addr}*>(
400                    reinterpret_cast<const char*>(info_ptr) + sizeof(info_st)
401                );\n",
402                addr = body.address_type,
403            )?;
404        }
405        Ok(())
406    }
407}
408
409// Cube builtins dialect
410
411impl<M: DialectWmmaCompiler<Self>> DialectCubeBuiltins<Self> for HipDialect<M> {}
412
413// Instructions
414
415impl<M: DialectWmmaCompiler<Self>> DialectInstructions<Self> for HipDialect<M> {
416    fn compile_instruction_sync_threads(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
417        writeln!(f, "__syncthreads();\n")
418    }
419
420    fn compile_instruction_sync_warp(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
421        // HIP has no `__syncwarp`. AMD wavefronts execute in lockstep, so the
422        // execution half of the sync is a compiler scheduling barrier; the
423        // wavefront-scope fence supplies the memory ordering `__syncwarp`
424        // carries on CUDA (LDS/global writes by other lanes of the wave are
425        // visible past the sync).
426        writeln!(
427            f,
428            "__builtin_amdgcn_fence(__ATOMIC_ACQ_REL, \"wavefront\");"
429        )?;
430        writeln!(f, "__builtin_amdgcn_wave_barrier();\n")
431    }
432
433    fn compile_instruction_thread_fence(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
434        writeln!(f, "__threadfence();")
435    }
436
437    // unary
438    fn compile_instruction_find_first_set<T: Component<Self>>(
439        f: &mut std::fmt::Formatter<'_>,
440        input: T,
441        out_elem: Elem<Self>,
442    ) -> std::fmt::Result {
443        write!(f, "{out_elem}(")?;
444        match input.elem() {
445            Elem::I32 | Elem::U32 => write!(f, "__ffs({input})"),
446            Elem::I64 | Elem::U64 => write!(f, "__ffsll({input})"),
447            _ => write!(f, "__ffs({}({input}))", Elem::<Self>::U32),
448        }?;
449        write!(f, ")")
450    }
451
452    fn compile_instruction_leading_zeros_scalar<T: Component<Self>>(
453        f: &mut std::fmt::Formatter<'_>,
454        input: T,
455        out_elem: Elem<Self>,
456    ) -> std::fmt::Result {
457        write!(f, "{out_elem}(")?;
458        match input.elem() {
459            Elem::I32 | Elem::U32 => write!(f, "__clz({input})"),
460            Elem::I64 | Elem::U64 => write!(f, "__clzll({input})"),
461            in_elem => write!(
462                f,
463                "__clz({}) - {}",
464                unary::zero_extend(input),
465                (size_of::<u32>() - in_elem.size()) * 8
466            ),
467        }?;
468        write!(f, ")")
469    }
470
471    fn compile_instruction_trailing_zeros_scalar<T: Component<Self>>(
472        f: &mut std::fmt::Formatter<'_>,
473        input: T,
474        out_elem: Elem<Self>,
475    ) -> std::fmt::Result {
476        // trailing_zeros = ffs - 1 for non-zero, or bit_width for zero
477        // __ffs returns 1-based index of least significant set bit, or 0 if input is 0
478        write!(f, "{out_elem}(")?;
479        match input.elem() {
480            Elem::I32 | Elem::U32 => {
481                write!(f, "({input} == 0 ? 32 : __ffs({input}) - 1)")
482            }
483            Elem::I64 | Elem::U64 => {
484                write!(f, "({input} == 0 ? 64 : __ffsll({input}) - 1)")
485            }
486            in_elem => {
487                let bits = in_elem.size() * 8;
488                let extended = unary::zero_extend(input);
489                write!(f, "({extended} == 0 ? {bits} : __ffs({extended}) - 1)")
490            }
491        }?;
492        write!(f, ")")
493    }
494
495    fn compile_saturating_add(
496        f: &mut std::fmt::Formatter<'_>,
497        _lhs: impl Display,
498        _rhs: impl Display,
499        _item: Item<Self>,
500    ) -> std::fmt::Result {
501        f.write_str(
502            "#error No native saturating add exists, TODO: Should be replaced in a preprocessor\n",
503        )
504    }
505
506    fn compile_saturating_sub(
507        f: &mut std::fmt::Formatter<'_>,
508        _lhs: impl Display,
509        _rhs: impl Display,
510        _item: Item<Self>,
511    ) -> std::fmt::Result {
512        f.write_str(
513            "#error No native saturating sub exists, TODO: Should be replaced in a preprocessor\n",
514        )
515    }
516
517    // others
518    fn compile_instruction_max_function_name(
519        f: &mut std::fmt::Formatter<'_>,
520        item: Item<Self>,
521    ) -> std::fmt::Result {
522        let max = match item.elem() {
523            Elem::F16 => "__hmax",
524            Elem::BF16 => "__hmax",
525            _ => "max",
526        };
527        write!(f, "{max}")
528    }
529
530    fn compile_instruction_min_function_name(
531        f: &mut std::fmt::Formatter<'_>,
532        item: Item<Self>,
533    ) -> std::fmt::Result {
534        let min = match item.elem() {
535            Elem::F16 => "__hmin",
536            Elem::BF16 => "__hmin",
537            _ => "min",
538        };
539        write!(f, "{min}")
540    }
541
542    // Warp
543    fn compile_warp_shuffle(
544        f: &mut std::fmt::Formatter<'_>,
545        val: &str,
546        _elem: &Elem<Self>,
547        source: &str,
548    ) -> std::fmt::Result {
549        write!(f, "__shfl({val}, {source})")
550    }
551    fn compile_warp_shuffle_xor(
552        f: &mut std::fmt::Formatter<'_>,
553        val: &str,
554        elem: &Elem<Self>,
555        offset: &str,
556    ) -> std::fmt::Result {
557        match elem {
558            Elem::BF16 => write!(
559                f,
560                "half_to_bfloat16(__shfl_xor(reinterpret_cast<__half&>({val}), {offset}))"
561            ),
562            _ => write!(f, "__shfl_xor({val}, {offset})"),
563        }
564    }
565    fn compile_warp_shuffle_up(
566        f: &mut std::fmt::Formatter<'_>,
567        val: &str,
568        _elem: &Elem<Self>,
569        offset: &str,
570    ) -> std::fmt::Result {
571        write!(f, "__shfl_up({val}, {offset})")
572    }
573    fn compile_warp_shuffle_down(
574        f: &mut std::fmt::Formatter<'_>,
575        val: &str,
576        _elem: &Elem<Self>,
577        offset: &str,
578    ) -> std::fmt::Result {
579        write!(f, "__shfl_down({val}, {offset})")
580    }
581    fn compile_warp_all<T: Component<Self>>(
582        f: &mut std::fmt::Formatter<'_>,
583        input: &T,
584    ) -> std::fmt::Result {
585        let item = input.item();
586        let elem = item.elem();
587        write!(f, "static_cast<{elem}>(__all({input}))")
588    }
589    fn compile_warp_any<T: Component<Self>>(
590        f: &mut std::fmt::Formatter<'_>,
591        input: &T,
592    ) -> std::fmt::Result {
593        let item = input.item();
594        let elem = item.elem();
595        write!(f, "static_cast<{elem}>(__any({input}))")
596    }
597    fn compile_warp_ballot(
598        f: &mut std::fmt::Formatter<'_>,
599        input: &Value<Self>,
600        out_elem: &Elem<Self>,
601    ) -> std::fmt::Result {
602        write!(f, "{out_elem}(__ballot({input}))")
603    }
604
605    fn compile_unreachable(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
606        write!(f, "__builtin_unreachable();")
607    }
608}
609
610// Coop Matrices dialect
611
612impl<M: DialectWmmaCompiler<Self>> DialectWmmaCompiler<Self> for HipDialect<M> {
613    fn compile_wmma_includes(
614        f: &mut std::fmt::Formatter<'_>,
615        flags: &Flags<Self>,
616    ) -> std::fmt::Result {
617        M::compile_wmma_includes(f, flags)
618    }
619
620    fn compile_wmma_type_definitions(
621        f: &mut std::fmt::Formatter<'_>,
622        flags: &Flags<Self>,
623    ) -> std::fmt::Result {
624        M::compile_wmma_type_definitions(f, flags)
625    }
626
627    fn compile_wmma_local_variables(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
628        M::compile_wmma_local_variables(f)
629    }
630
631    fn compile_wmma_fragment_declaration(
632        f: &mut std::fmt::Formatter<'_>,
633        val: &Value<Self>,
634        ty: &Item<Self>,
635    ) -> std::fmt::Result {
636        M::compile_wmma_fragment_declaration(f, val, ty)
637    }
638
639    fn compile_wwma_fragment_ident(
640        f: &mut std::fmt::Formatter<'_>,
641        ident: &crate::shared::FragmentIdent<Self>,
642    ) -> std::fmt::Result {
643        M::compile_wwma_fragment_ident(f, ident)
644    }
645
646    fn compile_wmma_fragment_layout(
647        f: &mut std::fmt::Formatter<'_>,
648        layout: &crate::shared::FragmentLayout<Self>,
649    ) -> std::fmt::Result {
650        M::compile_wmma_fragment_layout(f, layout)
651    }
652
653    fn compile_wmma_fragment(
654        f: &mut std::fmt::Formatter<'_>,
655        fragment: &crate::shared::FragmentType<Self>,
656    ) -> std::fmt::Result {
657        M::compile_wmma_fragment(f, fragment)
658    }
659
660    fn compile_wmma_instruction(
661        f: &mut std::fmt::Formatter<'_>,
662        instruction: &crate::shared::WmmaInstruction<Self>,
663    ) -> std::fmt::Result {
664        M::compile_wmma_instruction(f, instruction)
665    }
666
667    fn compile_manual_mma(
668        f: &mut std::fmt::Formatter<'_>,
669        mma: ManualMma<Self>,
670    ) -> std::fmt::Result {
671        M::compile_manual_mma(f, mma)
672    }
673
674    fn supported_wmma_combinations(
675        arch: &AMDArchitecture,
676    ) -> crate::shared::SupportedMmaCombinations {
677        M::supported_wmma_combinations(arch)
678    }
679
680    fn supported_mma_combinations(arch: &AMDArchitecture) -> shared::SupportedMmaCombinations {
681        M::supported_mma_combinations(arch)
682    }
683
684    fn compile_scaled_mma(
685        _f: &mut std::fmt::Formatter<'_>,
686        _mma: ManualMma<Self>,
687        _scales_a: Value<Self>,
688        _scales_b: Value<Self>,
689        _scales_factor: u32,
690    ) -> std::fmt::Result {
691        panic!("Scaled MMA not supporter in HIP")
692    }
693}
694
695impl<M: DialectWmmaCompiler<Self>> DialectProcessors<Self> for HipDialect<M> {
696    fn processors() -> Vec<Box<dyn Processor>> {
697        vec![
698            Box::new(HipMmaProcessor),
699            Box::new(SaturatingArithmeticProcessor::new(true)),
700        ]
701    }
702}