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        writeln!(f, "#error Sync warp is unimplemented on hip\n")
422    }
423
424    fn compile_instruction_thread_fence(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
425        writeln!(f, "__threadfence();")
426    }
427
428    // unary
429    fn compile_instruction_find_first_set<T: Component<Self>>(
430        f: &mut std::fmt::Formatter<'_>,
431        input: T,
432        out_elem: Elem<Self>,
433    ) -> std::fmt::Result {
434        write!(f, "{out_elem}(")?;
435        match input.elem() {
436            Elem::I32 | Elem::U32 => write!(f, "__ffs({input})"),
437            Elem::I64 | Elem::U64 => write!(f, "__ffsll({input})"),
438            _ => write!(f, "__ffs({}({input}))", Elem::<Self>::U32),
439        }?;
440        write!(f, ")")
441    }
442
443    fn compile_instruction_leading_zeros_scalar<T: Component<Self>>(
444        f: &mut std::fmt::Formatter<'_>,
445        input: T,
446        out_elem: Elem<Self>,
447    ) -> std::fmt::Result {
448        write!(f, "{out_elem}(")?;
449        match input.elem() {
450            Elem::I32 | Elem::U32 => write!(f, "__clz({input})"),
451            Elem::I64 | Elem::U64 => write!(f, "__clzll({input})"),
452            in_elem => write!(
453                f,
454                "__clz({}) - {}",
455                unary::zero_extend(input),
456                (size_of::<u32>() - in_elem.size()) * 8
457            ),
458        }?;
459        write!(f, ")")
460    }
461
462    fn compile_instruction_trailing_zeros_scalar<T: Component<Self>>(
463        f: &mut std::fmt::Formatter<'_>,
464        input: T,
465        out_elem: Elem<Self>,
466    ) -> std::fmt::Result {
467        // trailing_zeros = ffs - 1 for non-zero, or bit_width for zero
468        // __ffs returns 1-based index of least significant set bit, or 0 if input is 0
469        write!(f, "{out_elem}(")?;
470        match input.elem() {
471            Elem::I32 | Elem::U32 => {
472                write!(f, "({input} == 0 ? 32 : __ffs({input}) - 1)")
473            }
474            Elem::I64 | Elem::U64 => {
475                write!(f, "({input} == 0 ? 64 : __ffsll({input}) - 1)")
476            }
477            in_elem => {
478                let bits = in_elem.size() * 8;
479                let extended = unary::zero_extend(input);
480                write!(f, "({extended} == 0 ? {bits} : __ffs({extended}) - 1)")
481            }
482        }?;
483        write!(f, ")")
484    }
485
486    fn compile_saturating_add(
487        f: &mut std::fmt::Formatter<'_>,
488        _lhs: impl Display,
489        _rhs: impl Display,
490        _item: Item<Self>,
491    ) -> std::fmt::Result {
492        f.write_str(
493            "#error No native saturating add exists, TODO: Should be replaced in a preprocessor\n",
494        )
495    }
496
497    fn compile_saturating_sub(
498        f: &mut std::fmt::Formatter<'_>,
499        _lhs: impl Display,
500        _rhs: impl Display,
501        _item: Item<Self>,
502    ) -> std::fmt::Result {
503        f.write_str(
504            "#error No native saturating sub exists, TODO: Should be replaced in a preprocessor\n",
505        )
506    }
507
508    // others
509    fn compile_instruction_max_function_name(
510        f: &mut std::fmt::Formatter<'_>,
511        item: Item<Self>,
512    ) -> std::fmt::Result {
513        let max = match item.elem() {
514            Elem::F16 => "__hmax",
515            Elem::BF16 => "__hmax",
516            _ => "max",
517        };
518        write!(f, "{max}")
519    }
520
521    fn compile_instruction_min_function_name(
522        f: &mut std::fmt::Formatter<'_>,
523        item: Item<Self>,
524    ) -> std::fmt::Result {
525        let min = match item.elem() {
526            Elem::F16 => "__hmin",
527            Elem::BF16 => "__hmin",
528            _ => "min",
529        };
530        write!(f, "{min}")
531    }
532
533    // Warp
534    fn compile_warp_shuffle(
535        f: &mut std::fmt::Formatter<'_>,
536        val: &str,
537        _elem: &Elem<Self>,
538        source: &str,
539    ) -> std::fmt::Result {
540        write!(f, "__shfl({val}, {source})")
541    }
542    fn compile_warp_shuffle_xor(
543        f: &mut std::fmt::Formatter<'_>,
544        val: &str,
545        elem: &Elem<Self>,
546        offset: &str,
547    ) -> std::fmt::Result {
548        match elem {
549            Elem::BF16 => write!(
550                f,
551                "half_to_bfloat16(__shfl_xor(reinterpret_cast<__half&>({val}), {offset}))"
552            ),
553            _ => write!(f, "__shfl_xor({val}, {offset})"),
554        }
555    }
556    fn compile_warp_shuffle_up(
557        f: &mut std::fmt::Formatter<'_>,
558        val: &str,
559        _elem: &Elem<Self>,
560        offset: &str,
561    ) -> std::fmt::Result {
562        write!(f, "__shfl_up({val}, {offset})")
563    }
564    fn compile_warp_shuffle_down(
565        f: &mut std::fmt::Formatter<'_>,
566        val: &str,
567        _elem: &Elem<Self>,
568        offset: &str,
569    ) -> std::fmt::Result {
570        write!(f, "__shfl_down({val}, {offset})")
571    }
572    fn compile_warp_all<T: Component<Self>>(
573        f: &mut std::fmt::Formatter<'_>,
574        input: &T,
575    ) -> std::fmt::Result {
576        let item = input.item();
577        let elem = item.elem();
578        write!(f, "static_cast<{elem}>(__all({input}))")
579    }
580    fn compile_warp_any<T: Component<Self>>(
581        f: &mut std::fmt::Formatter<'_>,
582        input: &T,
583    ) -> std::fmt::Result {
584        let item = input.item();
585        let elem = item.elem();
586        write!(f, "static_cast<{elem}>(__any({input}))")
587    }
588    fn compile_warp_ballot(
589        f: &mut std::fmt::Formatter<'_>,
590        input: &Value<Self>,
591        out_elem: &Elem<Self>,
592    ) -> std::fmt::Result {
593        write!(f, "{out_elem}(__ballot({input}))")
594    }
595
596    fn compile_unreachable(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
597        write!(f, "__builtin_unreachable();")
598    }
599}
600
601// Coop Matrices dialect
602
603impl<M: DialectWmmaCompiler<Self>> DialectWmmaCompiler<Self> for HipDialect<M> {
604    fn compile_wmma_includes(
605        f: &mut std::fmt::Formatter<'_>,
606        flags: &Flags<Self>,
607    ) -> std::fmt::Result {
608        M::compile_wmma_includes(f, flags)
609    }
610
611    fn compile_wmma_type_definitions(
612        f: &mut std::fmt::Formatter<'_>,
613        flags: &Flags<Self>,
614    ) -> std::fmt::Result {
615        M::compile_wmma_type_definitions(f, flags)
616    }
617
618    fn compile_wmma_local_variables(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
619        M::compile_wmma_local_variables(f)
620    }
621
622    fn compile_wmma_fragment_declaration(
623        f: &mut std::fmt::Formatter<'_>,
624        val: &Value<Self>,
625        ty: &Item<Self>,
626    ) -> std::fmt::Result {
627        M::compile_wmma_fragment_declaration(f, val, ty)
628    }
629
630    fn compile_wwma_fragment_ident(
631        f: &mut std::fmt::Formatter<'_>,
632        ident: &crate::shared::FragmentIdent<Self>,
633    ) -> std::fmt::Result {
634        M::compile_wwma_fragment_ident(f, ident)
635    }
636
637    fn compile_wmma_fragment_layout(
638        f: &mut std::fmt::Formatter<'_>,
639        layout: &crate::shared::FragmentLayout<Self>,
640    ) -> std::fmt::Result {
641        M::compile_wmma_fragment_layout(f, layout)
642    }
643
644    fn compile_wmma_fragment(
645        f: &mut std::fmt::Formatter<'_>,
646        fragment: &crate::shared::FragmentType<Self>,
647    ) -> std::fmt::Result {
648        M::compile_wmma_fragment(f, fragment)
649    }
650
651    fn compile_wmma_instruction(
652        f: &mut std::fmt::Formatter<'_>,
653        instruction: &crate::shared::WmmaInstruction<Self>,
654    ) -> std::fmt::Result {
655        M::compile_wmma_instruction(f, instruction)
656    }
657
658    fn compile_manual_mma(
659        f: &mut std::fmt::Formatter<'_>,
660        mma: ManualMma<Self>,
661    ) -> std::fmt::Result {
662        M::compile_manual_mma(f, mma)
663    }
664
665    fn supported_wmma_combinations(
666        arch: &AMDArchitecture,
667    ) -> crate::shared::SupportedMmaCombinations {
668        M::supported_wmma_combinations(arch)
669    }
670
671    fn supported_mma_combinations(arch: &AMDArchitecture) -> shared::SupportedMmaCombinations {
672        M::supported_mma_combinations(arch)
673    }
674
675    fn compile_scaled_mma(
676        _f: &mut std::fmt::Formatter<'_>,
677        _mma: ManualMma<Self>,
678        _scales_a: Value<Self>,
679        _scales_b: Value<Self>,
680        _scales_factor: u32,
681    ) -> std::fmt::Result {
682        panic!("Scaled MMA not supporter in HIP")
683    }
684}
685
686impl<M: DialectWmmaCompiler<Self>> DialectProcessors<Self> for HipDialect<M> {
687    fn processors() -> Vec<Box<dyn Processor>> {
688        vec![
689            Box::new(HipMmaProcessor),
690            Box::new(SaturatingArithmeticProcessor::new(true)),
691        ]
692    }
693}