Skip to main content

cubecl_cpp/cuda/
dialect.rs

1use std::{collections::HashSet, fmt::Display, marker::PhantomData};
2
3use cubecl_core::{
4    ir::{BarrierLevel, Processor},
5    post_processing::saturating::SaturatingArithmeticProcessor,
6    prelude::Visibility,
7};
8
9use crate::{
10    Dialect,
11    cuda::{
12        extension::{Fragment, LdMatrix, MmaExecute, MmaExecuteScaled, MmaExtension, StMatrix},
13        processors::CudaMmaProcessor,
14        ptx::*,
15    },
16    shared::{
17        self, Component, DialectBindings, DialectCubeBuiltins, DialectIncludes,
18        DialectInstructions, DialectProcessors, DialectTypes, DialectWarpReduceCompiler,
19        DialectWmmaCompiler, Elem, FP4Kind, FP6Kind, FP8Kind, Flags, Instruction, Item, KernelArg,
20        ManualMma, PointerClass, Value, WarpInstruction, unary,
21    },
22};
23
24use super::{Extension, arch::CudaArchitecture};
25
26#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
27pub struct CudaDialect<M> {
28    _wmma_compiler: PhantomData<M>,
29}
30
31impl<M: DialectWmmaCompiler<Self>> Dialect for CudaDialect<M> {
32    type Architecture = CudaArchitecture;
33}
34
35impl<M: DialectWmmaCompiler<Self>> DialectIncludes<Self> for CudaDialect<M> {
36    type Extension = Extension<Self>;
37
38    fn compile_includes(f: &mut std::fmt::Formatter<'_>, flags: &Flags<Self>) -> std::fmt::Result {
39        f.write_str("#include <cuda_runtime.h>\n")?;
40        if flags.elem_fp4 {
41            f.write_str("#include <cuda_fp4.h>\n")?;
42        }
43        if flags.elem_fp6 {
44            f.write_str("#include <cuda_fp6.h>\n")?;
45        }
46        if flags.elem_fp8 {
47            f.write_str("#include <cuda_fp8.h>\n")?;
48        }
49        if flags.elem_bf16 {
50            f.write_str("#include <cuda_bf16.h>\n")?;
51        }
52        if flags.elem_f16 {
53            f.write_str("#include <cuda_fp16.h>\n")?;
54        }
55
56        // tf32 conversion function is in mma header
57        if flags.inst_wmma || flags.elem_tf32 {
58            Self::compile_wmma_includes(f, flags)?;
59        }
60
61        if flags.op_barrier || flags.inst_tma || flags.indexes.cluster_pos {
62            f.write_str("#include <cooperative_groups.h>\n")?;
63            f.write_str("#include <cooperative_groups/memcpy_async.h>\n")?;
64            f.write_str("#include <cuda/barrier>\n")?;
65        }
66        if flags.inst_ptx_wrappers {
67            f.write_str("#include <cuda/ptx>\n")?;
68        }
69        if flags.inst_tma {
70            f.write_str(
71                "typedef struct CUtensorMap_st {
72alignas(64) unsigned long long int opaque[16];
73} CUtensorMap;\n",
74            )?;
75        }
76        Ok(())
77    }
78
79    fn compile_extensions(
80        f: &mut std::fmt::Formatter<'_>,
81        extensions: &[Self::Extension],
82    ) -> std::fmt::Result {
83        for extension in extensions {
84            match extension {
85                Extension::NoExtension => {}
86                Extension::Mma(mma) => mma.format_extension(f)?,
87            }
88        }
89        Ok(())
90    }
91
92    fn register_instruction_extension(
93        _extensions: &mut Vec<Self::Extension>,
94        _instruction: &Instruction<Self>,
95    ) {
96    }
97
98    fn register_warp_instruction_extension(
99        _extensions: &mut Vec<Self::Extension>,
100        _instruction: &WarpInstruction<Self>,
101    ) {
102    }
103
104    fn register_wmma_instruction_extension(
105        extensions: &mut Vec<Self::Extension>,
106        instruction: &shared::WmmaInstruction<Self>,
107    ) {
108        match instruction {
109            shared::WmmaInstruction::ExecuteManual {
110                shape,
111                frag_a,
112                frag_b,
113                frag_c,
114                frag_d,
115            } => {
116                let ext = Extension::Mma(MmaExtension::Execute(MmaExecute::new(
117                    *shape,
118                    Fragment(frag_a.elem()),
119                    Fragment(frag_b.elem()),
120                    Fragment(frag_c.elem()),
121                    Fragment(frag_d.elem()),
122                )));
123                if !extensions.contains(&ext) {
124                    extensions.push(ext);
125                }
126            }
127            shared::WmmaInstruction::ExecuteScaled {
128                shape,
129                frag_a,
130                frag_b,
131                frag_c,
132                frag_d,
133                scales_a,
134                scales_factor,
135                ..
136            } => {
137                let ext = Extension::Mma(MmaExtension::ExecuteScaled(MmaExecuteScaled::new(
138                    *shape,
139                    Fragment(frag_a.elem()),
140                    Fragment(frag_b.elem()),
141                    Fragment(frag_c.elem()),
142                    Fragment(frag_d.elem()),
143                    scales_a.elem(),
144                    *scales_factor,
145                )));
146                if !extensions.contains(&ext) {
147                    extensions.push(ext);
148                }
149            }
150            shared::WmmaInstruction::LdMatrix {
151                output,
152                factor,
153                transpose,
154                ..
155            } => {
156                let ext = Extension::Mma(MmaExtension::LdMatrix(LdMatrix::new(
157                    output.elem(),
158                    *factor,
159                    *transpose,
160                )));
161                if !extensions.contains(&ext) {
162                    extensions.push(ext);
163                }
164            }
165            shared::WmmaInstruction::StMatrix {
166                registers,
167                factor,
168                transpose,
169                ..
170            } => {
171                let ext = Extension::Mma(MmaExtension::StMatrix(StMatrix::new(
172                    registers.elem(),
173                    *factor,
174                    *transpose,
175                )));
176                if !extensions.contains(&ext) {
177                    extensions.push(ext);
178                }
179            }
180            _ => {}
181        }
182    }
183}
184
185// Types
186
187impl<M: DialectWmmaCompiler<Self>> DialectTypes<Self> for CudaDialect<M> {
188    fn item_can_be_optimized() -> bool {
189        true
190    }
191
192    fn compile_type_definitions(
193        f: &mut std::fmt::Formatter<'_>,
194        items: &HashSet<Item<Self>>,
195        scalars: &[(Elem<Self>, usize)],
196        info: &cubecl_core::Info,
197        flags: &Flags<Self>,
198    ) -> std::fmt::Result {
199        // All FP4/FP6/FP8 elems map to the same type, so we need to deduplicate them
200        let mut items_deduplicated = HashSet::new();
201
202        for item in items {
203            let mut item = *item.value_ty();
204            match item {
205                Item::NativeVector(..) => {
206                    continue;
207                }
208                Item::Atomic(inner) => {
209                    item = *inner;
210                }
211                _ => {}
212            }
213            match item.elem() {
214                Elem::FP4(_) => {
215                    item = item.with_elem(Elem::FP4(FP4Kind::E2M1));
216                }
217                Elem::FP4x2(_) => {
218                    item = item.with_elem(Elem::FP4x2(FP4Kind::E2M1));
219                }
220                Elem::FP6(_) => {
221                    item = item.with_elem(Elem::FP6(FP6Kind::E2M3));
222                }
223                Elem::FP6x2(_) => {
224                    item = item.with_elem(Elem::FP6x2(FP6Kind::E2M3));
225                }
226                Elem::FP8(_) => {
227                    item = item.with_elem(Elem::FP8(FP8Kind::E4M3));
228                }
229                Elem::FP8x2(_) => {
230                    item = item.with_elem(Elem::FP8x2(FP8Kind::E4M3));
231                }
232                _ => {}
233            }
234            items_deduplicated.insert(item);
235        }
236
237        shared::type_definitions::<Self>(f)?;
238        shared::type_vectorized_definitions::<Self>(f, &items_deduplicated)?;
239
240        shared::type_info_definition_sized(f, info, scalars, flags.address_type)?;
241
242        if flags.inst_wmma {
243            Self::compile_wmma_type_definitions(f, flags)?;
244        }
245
246        Ok(())
247    }
248
249    fn compile_polyfills(f: &mut std::fmt::Formatter<'_>, flags: &Flags<Self>) -> std::fmt::Result {
250        if flags.inst_tma_im2col {
251            writeln!(f, "{TMA_LOAD_IM2COL}")?;
252        }
253        if flags.inst_async_copy {
254            writeln!(f, "{COPY_ASYNC}")?;
255        }
256        Ok(())
257    }
258
259    fn compile_elem(
260        f: &mut std::fmt::Formatter<'_>,
261        elem: &shared::Elem<Self>,
262        words: bool,
263    ) -> std::fmt::Result {
264        if words {
265            match elem {
266                shared::Elem::F32 => f.write_str("float"),
267                shared::Elem::F64 => f.write_str("double"),
268                shared::Elem::TF32 => f.write_str("float"),
269                shared::Elem::I8 => f.write_str("char"),
270                shared::Elem::I16 => f.write_str("short"),
271                shared::Elem::I32 => f.write_str("int"),
272                shared::Elem::I64 => f.write_str("long"),
273                shared::Elem::U8 => f.write_str("uchar"),
274                shared::Elem::U16 => f.write_str("ushort"),
275                shared::Elem::U32 => f.write_str("uint"),
276                shared::Elem::U64 => f.write_str("ulong"),
277                _ => Self::compile_elem(f, elem, false),
278            }
279        } else {
280            match elem {
281                shared::Elem::FP4(_) => write!(f, "__nv_fp4_storage_t"),
282                shared::Elem::FP4x2(_) => write!(f, "__nv_fp4x2_storage_t"),
283                shared::Elem::FP6(_) => write!(f, "__nv_fp6_storage_t"),
284                shared::Elem::FP6x2(_) => write!(f, "__nv_fp6x2_storage_t"),
285                shared::Elem::FP8(_) => write!(f, "__nv_fp8_storage_t"),
286                shared::Elem::FP8x2(_) => write!(f, "__nv_fp8x2_storage_t"),
287                shared::Elem::F16 => f.write_str("__half"),
288                shared::Elem::F16x2 => f.write_str("__half2"),
289                shared::Elem::F32 => f.write_str("float"),
290                shared::Elem::F64 => f.write_str("double"),
291                shared::Elem::BF16 => f.write_str("__nv_bfloat16"),
292                shared::Elem::BF16x2 => f.write_str("__nv_bfloat162"),
293                shared::Elem::TF32 => f.write_str("float"),
294                shared::Elem::I8 => f.write_str("int8"),
295                shared::Elem::I16 => f.write_str("int16"),
296                shared::Elem::I32 => f.write_str("int32"),
297                shared::Elem::I64 => f.write_str("int64"),
298                shared::Elem::U8 => f.write_str("uint8"),
299                shared::Elem::U16 => f.write_str("uint16"),
300                shared::Elem::U32 => f.write_str("uint32"),
301                shared::Elem::U64 => f.write_str("uint64"),
302                shared::Elem::Bool => f.write_str("bool"),
303                shared::Elem::None => f.write_str("<none>"),
304                shared::Elem::_Dialect(_) => Ok(()),
305            }
306        }
307    }
308
309    fn compile_item(f: &mut std::fmt::Formatter<'_>, item: &Item<Self>) -> std::fmt::Result {
310        match item {
311            Item::Scalar(elem) => write!(f, "{elem}"),
312            Item::Vector(inner, vectorization) => {
313                write!(f, "{inner}_{vectorization}")
314            }
315            Item::NativeVector(elem, vectorization) => {
316                Self::compile_elem(f, elem, true)?;
317                write!(f, "{vectorization}")
318            }
319            Item::Atomic(inner) => Self::compile_item(f, inner.as_ref()),
320            Item::Pointer(inner, class) => {
321                if let PointerClass::Global(Visibility::Read | Visibility::Uniform) = class {
322                    f.write_str("const ")?;
323                }
324                match inner.as_ref() {
325                    Item::DynamicArray(inner) => write!(f, "{inner}*"),
326                    other => write!(f, "{other}*"),
327                }
328            }
329            Item::Array(inner, size) => {
330                write!(f, "array<{inner}, {size}>")
331            }
332            Item::DynamicArray(inner) => {
333                write!(f, "{inner}*")
334            }
335            Item::Fragment(fragment_type) => write!(f, "{fragment_type}"),
336            Item::BarrierToken(BarrierLevel::Cube) => {
337                write!(f, "cuda::barrier<cuda::thread_scope_block>::arrival_token")
338            }
339            Item::BarrierToken(BarrierLevel::Unit) => {
340                write!(f, "cuda::barrier<cuda::thread_scope_thread>::arrival_token")
341            }
342            Item::TensorMap => f.write_str("CUtensorMap"),
343            Item::Barrier(BarrierLevel::Unit) => {
344                f.write_str("cuda::barrier<cuda::thread_scope_thread>")
345            }
346            Item::Barrier(BarrierLevel::Cube) => {
347                f.write_str("cuda::barrier<cuda::thread_scope_block>")
348            }
349        }
350    }
351
352    fn compile_local_memory_qualifier(_f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
353        Ok(())
354    }
355}
356
357// Kernel argument bindings
358
359impl<M: DialectWmmaCompiler<Self>> DialectBindings<Self> for CudaDialect<M> {
360    fn compile_kernel_signature(
361        f: &mut std::fmt::Formatter<'_>,
362        kernel_name: &str,
363        tensor_maps: &[KernelArg<Self>],
364        buffers: &[KernelArg<Self>],
365        flags: &Flags<Self>,
366    ) -> std::fmt::Result {
367        write!(
368            f,
369            "
370
371extern \"C\" __global__ void __launch_bounds__({})",
372            flags.cube_dim.num_elems()
373        )?;
374        if let Some(cluster_dim) = flags.cluster_dim {
375            write!(
376                f,
377                "__cluster_dims__({}, {}, {}) ",
378                cluster_dim.x, cluster_dim.y, cluster_dim.z
379            )?;
380        }
381        writeln!(f, "{kernel_name} (")?;
382
383        shared::compile_bindings(f, tensor_maps, buffers, flags.has_info)?;
384        if flags.use_grid_constants {
385            shared::compile_info_static(f, flags)?;
386        } else {
387            shared::compile_info_dynamic(f, flags)?;
388        }
389        f.write_str("\n)")?;
390        //
391        Ok(())
392    }
393
394    fn compile_bindings_body(
395        f: &mut std::fmt::Formatter<'_>,
396        body: &shared::Body<Self>,
397    ) -> std::fmt::Result {
398        if !body.shared_memories.is_empty() {
399            let max_align = body
400                .shared_memories
401                .iter()
402                .map(|smem| smem.align)
403                .max()
404                .unwrap();
405            // The `__align__` instead of `alignas` is on purpose - the compiler is currently bugged
406            // with `extern __shared__ alignas` and doesn't properly parse it.
407            writeln!(
408                f,
409                "extern __shared__ __align__({max_align}) uint8 dynamic_shared_mem[];"
410            )?;
411        }
412        if body.info_by_ptr {
413            f.write_str("const info_st& info = *info_ptr;\n")?;
414            // Could use `info_ptr + 1` but that seems dirty, so use manual `sizeof` instead
415            writeln!(
416                f,
417                "const {addr}* dynamic_meta = reinterpret_cast<const {addr}*>(
418                    reinterpret_cast<const char*>(info_ptr) + sizeof(info_st)
419                );\n",
420                addr = body.address_type,
421            )?;
422        }
423        Ok(())
424    }
425}
426
427impl<M: DialectWmmaCompiler<Self>> DialectWarpReduceCompiler<Self> for CudaDialect<M> {}
428
429// Cube builtins dialect
430
431impl<M: DialectWmmaCompiler<Self>> DialectCubeBuiltins<Self> for CudaDialect<M> {
432    fn compile_cluster_pos(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
433        write!(f, "cluster.block_rank()")
434    }
435
436    fn compile_cluster_pos_x(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
437        write!(f, "cluster.block_index().x")
438    }
439
440    fn compile_cluster_pos_y(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
441        write!(f, "cluster.block_index().y")
442    }
443
444    fn compile_cluster_pos_z(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
445        write!(f, "cluster.block_index().z")
446    }
447}
448
449// Instructions
450
451impl<M: DialectWmmaCompiler<Self>> DialectInstructions<Self> for CudaDialect<M> {
452    // sync
453    fn compile_instruction_sync_threads(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
454        writeln!(f, "__syncthreads();\n")
455    }
456
457    fn compile_instruction_sync_warp(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
458        writeln!(f, "__syncwarp();\n")
459    }
460
461    fn compile_instruction_thread_fence(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
462        writeln!(f, "__threadfence();")
463    }
464
465    // unary
466    fn compile_instruction_find_first_set<T: Component<Self>>(
467        f: &mut std::fmt::Formatter<'_>,
468        input: T,
469        out_elem: Elem<Self>,
470    ) -> std::fmt::Result {
471        write!(f, "{out_elem}(")?;
472        match input.elem() {
473            Elem::I32 => write!(f, "__ffs({input})"),
474            Elem::U32 => write!(f, "__ffs({}({input}))", Elem::<Self>::I32),
475            Elem::I64 => write!(f, "__ffsll({input})"),
476            Elem::U64 => write!(f, "__ffsll({}({input}))", Elem::<Self>::I64),
477            _ => write!(f, "__ffs({}({input}))", Elem::<Self>::I32),
478        }?;
479        write!(f, ")")
480    }
481
482    fn compile_instruction_leading_zeros_scalar<T: Component<Self>>(
483        f: &mut std::fmt::Formatter<'_>,
484        input: T,
485        out_elem: Elem<Self>,
486    ) -> std::fmt::Result {
487        write!(f, "{out_elem}(")?;
488        match input.elem() {
489            Elem::I32 => write!(f, "__clz({input})"),
490            Elem::U32 => write!(f, "__clz({}({input}))", Elem::<Self>::I32),
491            Elem::I64 => write!(f, "__clzll({input})"),
492            Elem::U64 => write!(f, "__clzll({}({input}))", Elem::<Self>::I64),
493            in_elem => write!(
494                f,
495                "{out_elem}(__clz({}) - {})",
496                unary::zero_extend(input),
497                (size_of::<u32>() - in_elem.size()) * 8
498            ),
499        }?;
500        write!(f, ")")
501    }
502
503    fn compile_instruction_trailing_zeros_scalar<T: Component<Self>>(
504        f: &mut std::fmt::Formatter<'_>,
505        input: T,
506        out_elem: Elem<Self>,
507    ) -> std::fmt::Result {
508        // CUDA doesn't have a direct ctz intrinsic, but __ffs returns 1-indexed position
509        // of the first set bit from LSB (0 if no bit set).
510        // trailing_zeros(x) = x == 0 ? bitwidth : __ffs(x) - 1
511        write!(f, "{out_elem}(")?;
512        match input.elem() {
513            Elem::I32 | Elem::U32 => {
514                write!(f, "({input} == 0 ? 32 : __ffs({input}) - 1)")
515            }
516            Elem::I64 | Elem::U64 => {
517                write!(f, "({input} == 0 ? 64 : __ffsll({input}) - 1)")
518            }
519            in_elem => {
520                let bits = in_elem.size() * 8;
521                let extended = unary::zero_extend(input);
522                write!(f, "({extended} == 0 ? {bits} : __ffs({extended}) - 1)")
523            }
524        }?;
525        write!(f, ")")
526    }
527
528    fn compile_saturating_add(
529        f: &mut std::fmt::Formatter<'_>,
530        lhs: impl Display,
531        rhs: impl Display,
532        item: Item<Self>,
533    ) -> std::fmt::Result {
534        let elem = item.elem();
535        match elem {
536            Elem::I32 => {
537                write!(
538                    f,
539                    r#"[&]() -> {elem} {{
540    {elem} result;
541    asm("add.sat.s32 %0, %1, %2;"
542        : "=r"(result)
543        : "r"({lhs}), "r"({rhs}));
544    return result;
545        }}()"#
546                )
547            }
548            _ => unreachable!("Should be replaced by polyfill"),
549        }
550    }
551
552    fn compile_saturating_sub(
553        f: &mut std::fmt::Formatter<'_>,
554        lhs: impl Display,
555        rhs: impl Display,
556        item: Item<Self>,
557    ) -> std::fmt::Result {
558        let elem = item.elem();
559        // Native instruction only exists for signed int, unsigned should be removed in a preprocessor
560        match elem {
561            Elem::I32 => {
562                write!(
563                    f,
564                    r#"[&]() -> {elem} {{
565    {elem} result;
566    asm("sub.sat.s32 %0, %1, %2;"
567        : "=r"(result)
568        : "r"({lhs}), "r"({rhs}));
569    return result;
570        }}()"#
571                )
572            }
573            _ => unreachable!("Should be replaced by polyfill"),
574        }
575    }
576
577    // others
578    fn compile_instruction_max_function_name(
579        f: &mut std::fmt::Formatter<'_>,
580        item: Item<Self>,
581    ) -> std::fmt::Result {
582        let max = match item.elem() {
583            Elem::F16 | Elem::BF16 => "__hmax",
584            Elem::F16x2 | Elem::BF16x2 => "__hmax2",
585            _ => "max",
586        };
587        write!(f, "{max}")
588    }
589
590    fn compile_instruction_min_function_name(
591        f: &mut std::fmt::Formatter<'_>,
592        item: Item<Self>,
593    ) -> std::fmt::Result {
594        let min = match item.elem() {
595            Elem::F16 | Elem::BF16 => "__hmin",
596            Elem::F16x2 | Elem::BF16x2 => "__hmin2",
597            _ => "min",
598        };
599        write!(f, "{min}")
600    }
601
602    // warp
603    fn compile_warp_shuffle(
604        f: &mut std::fmt::Formatter<'_>,
605        val: &str,
606        _elem: &Elem<Self>,
607        source: &str,
608    ) -> std::fmt::Result {
609        write!(f, "__shfl_sync(-1, {val}, {source})")
610    }
611    fn compile_warp_shuffle_xor(
612        f: &mut std::fmt::Formatter<'_>,
613        val: &str,
614        _elem: &Elem<Self>,
615        offset: &str,
616    ) -> std::fmt::Result {
617        write!(f, "__shfl_xor_sync(-1, {val}, {offset})")
618    }
619    fn compile_warp_shuffle_up(
620        f: &mut std::fmt::Formatter<'_>,
621        val: &str,
622        _elem: &Elem<Self>,
623        offset: &str,
624    ) -> std::fmt::Result {
625        write!(f, "__shfl_up_sync(-1, {val}, {offset})")
626    }
627    fn compile_warp_shuffle_down(
628        f: &mut std::fmt::Formatter<'_>,
629        val: &str,
630        _elem: &Elem<Self>,
631        offset: &str,
632    ) -> std::fmt::Result {
633        write!(f, "__shfl_down_sync(-1, {val}, {offset})")
634    }
635    fn compile_warp_all<T: Component<Self>>(
636        f: &mut std::fmt::Formatter<'_>,
637        input: &T,
638    ) -> std::fmt::Result {
639        write!(f, "__all_sync(-1, {input})")
640    }
641    fn compile_warp_any<T: Component<Self>>(
642        f: &mut std::fmt::Formatter<'_>,
643        input: &T,
644    ) -> std::fmt::Result {
645        write!(f, "__any_sync(-1, {input})")
646    }
647
648    fn compile_warp_ballot(
649        f: &mut std::fmt::Formatter<'_>,
650        input: &Value<Self>,
651        _out_elem: &Elem<Self>,
652    ) -> std::fmt::Result {
653        write!(f, "__ballot_sync(-1, {input})")
654    }
655
656    fn compile_warp_elect(f: &mut std::fmt::Formatter<'_>, out: &str) -> std::fmt::Result {
657        let elem = Elem::<Self>::Bool;
658        let uint32 = Elem::<Self>::U32;
659        // Used to have a wrapper but it has been removed in newer version due to being
660        // "incomplete". We only need the predicate and have a fixed mask, so it's trivial to
661        // implement.
662        writeln!(
663            f,
664            r#"{out} = {elem}([&]() -> {uint32} {{
665    {uint32} pred = 0;
666    asm volatile(
667        "{{\n"
668        "     .reg .pred %%px;\n"
669        "     elect.sync _|%%px, 0xffffffff;\n"
670        "     selp.b32 %0, 1, 0, %%px;\n"
671        "}}\n"
672        : "+r"(pred));
673    return pred;
674        }}());"#
675        )
676    }
677
678    fn compile_unreachable(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
679        write!(f, "__builtin_unreachable();")
680    }
681}
682
683// Coop Matrices dialect
684
685impl<M: DialectWmmaCompiler<Self>> DialectWmmaCompiler<Self> for CudaDialect<M> {
686    fn compile_wmma_includes(
687        f: &mut std::fmt::Formatter<'_>,
688        flags: &Flags<Self>,
689    ) -> std::fmt::Result {
690        M::compile_wmma_includes(f, flags)
691    }
692
693    fn compile_wmma_type_definitions(
694        f: &mut std::fmt::Formatter<'_>,
695        flags: &Flags<Self>,
696    ) -> std::fmt::Result {
697        M::compile_wmma_type_definitions(f, flags)
698    }
699
700    fn compile_wmma_local_variables(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
701        M::compile_wmma_local_variables(f)
702    }
703
704    fn compile_wmma_fragment_declaration(
705        f: &mut std::fmt::Formatter<'_>,
706        val: &Value<Self>,
707        value_ty: &Item<Self>,
708    ) -> std::fmt::Result {
709        M::compile_wmma_fragment_declaration(f, val, value_ty)
710    }
711
712    fn compile_wwma_fragment_ident(
713        f: &mut std::fmt::Formatter<'_>,
714        ident: &crate::shared::FragmentIdent<Self>,
715    ) -> std::fmt::Result {
716        M::compile_wwma_fragment_ident(f, ident)
717    }
718
719    fn compile_wmma_fragment_layout(
720        f: &mut std::fmt::Formatter<'_>,
721        layout: &crate::shared::FragmentLayout<Self>,
722    ) -> std::fmt::Result {
723        M::compile_wmma_fragment_layout(f, layout)
724    }
725
726    fn compile_wmma_fragment(
727        f: &mut std::fmt::Formatter<'_>,
728        fragment: &crate::shared::FragmentType<Self>,
729    ) -> std::fmt::Result {
730        M::compile_wmma_fragment(f, fragment)
731    }
732
733    fn compile_wmma_instruction(
734        f: &mut std::fmt::Formatter<'_>,
735        instruction: &crate::shared::WmmaInstruction<Self>,
736    ) -> std::fmt::Result {
737        M::compile_wmma_instruction(f, instruction)
738    }
739
740    fn compile_manual_mma(
741        f: &mut std::fmt::Formatter<'_>,
742        mma: ManualMma<Self>,
743    ) -> std::fmt::Result {
744        M::compile_manual_mma(f, mma)
745    }
746
747    fn compile_scaled_mma(
748        f: &mut std::fmt::Formatter<'_>,
749        mma: ManualMma<Self>,
750        scales_a: Value<Self>,
751        scales_b: Value<Self>,
752        scales_factor: u32,
753    ) -> std::fmt::Result {
754        M::compile_scaled_mma(f, mma, scales_a, scales_b, scales_factor)
755    }
756
757    fn supported_wmma_combinations(
758        arch: &CudaArchitecture,
759    ) -> crate::shared::SupportedMmaCombinations {
760        M::supported_wmma_combinations(arch)
761    }
762
763    fn supported_mma_combinations(arch: &CudaArchitecture) -> shared::SupportedMmaCombinations {
764        M::supported_mma_combinations(arch)
765    }
766
767    fn supported_scaled_mma_combinations(
768        arch: &CudaArchitecture,
769    ) -> shared::SupportedScaledMmaCombinations {
770        M::supported_scaled_mma_combinations(arch)
771    }
772}
773
774impl<M: DialectWmmaCompiler<Self>> DialectProcessors<Self> for CudaDialect<M> {
775    fn processors() -> Vec<Box<dyn Processor>> {
776        vec![
777            Box::new(CudaMmaProcessor),
778            Box::new(SaturatingArithmeticProcessor::new(false)),
779        ]
780    }
781}