1use std::{collections::HashSet, fmt::Debug};
2use std::{fmt::Display, hash::Hash};
3
4use cubecl_core::ir::Processor;
5
6use crate::shared::{
7 Builtin, FmtLeft, IndexedValue, MmaShape, SupportedMmaCombinations,
8 SupportedScaledMmaCombinations, reduce_comparison, reduce_exclusive, reduce_inclusive,
9 reduce_operator, reduce_quantifier,
10 unary::{Neg, Unary},
11};
12
13use super::{
14 Architecture, Body, Component, CubeIndexFlags, Elem, Flags, FragmentIdent, FragmentLayout,
15 FragmentType, Instruction, Item, KernelArg, SharedMemory, Value, WarpInstruction,
16 WmmaInstruction,
17};
18
19pub trait Dialect:
22 DialectIncludes<Self>
23 + DialectTypes<Self>
24 + DialectBindings<Self>
25 + DialectWarpReduceCompiler<Self>
26 + DialectCubeBuiltins<Self>
27 + DialectInstructions<Self>
28 + DialectWmmaCompiler<Self>
29 + DialectProcessors<Self>
30 + Default
31 + Clone
32 + Copy
33 + Debug
34 + Send
35 + Sync
36 + Eq
37 + Hash
38 + 'static
39{
40 type Architecture: Architecture;
41}
42
43pub trait DialectIncludes<D: Dialect> {
46 type Extension: Debug + Clone + Sync + Send;
47
48 fn compile_includes(f: &mut std::fmt::Formatter<'_>, flags: &Flags<D>) -> std::fmt::Result;
49 fn compile_extensions(
50 f: &mut std::fmt::Formatter<'_>,
51 extensions: &[Self::Extension],
52 ) -> std::fmt::Result;
53 fn register_instruction_extension(
54 extensions: &mut Vec<Self::Extension>,
55 instruction: &Instruction<D>,
56 );
57 fn register_warp_instruction_extension(
58 extensions: &mut Vec<Self::Extension>,
59 instruction: &WarpInstruction<D>,
60 );
61 #[allow(unused_variables)]
62 fn register_wmma_instruction_extension(
63 extensions: &mut Vec<Self::Extension>,
64 instruction: &WmmaInstruction<D>,
65 ) {
66 }
67}
68
69pub trait DialectTypes<D: Dialect> {
72 fn item_can_be_optimized() -> bool;
73 fn compile_elem(
74 f: &mut std::fmt::Formatter<'_>,
75 elem: &Elem<D>,
76 word: bool,
77 ) -> std::fmt::Result;
78
79 fn compile_item(f: &mut std::fmt::Formatter<'_>, item: &Item<D>) -> std::fmt::Result;
80 fn compile_type_definitions(
81 f: &mut std::fmt::Formatter<'_>,
82 items: &HashSet<Item<D>>,
83 scalars: &[(Elem<D>, usize)],
84 info: &cubecl_core::Info,
85 flags: &Flags<D>,
86 ) -> std::fmt::Result;
87 fn compile_local_memory_qualifier(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result;
88 fn compile_shared_memory_declaration(
89 f: &mut std::fmt::Formatter<'_>,
90 shared: &SharedMemory<D>,
91 ) -> std::fmt::Result {
92 let SharedMemory { ptr, offset, .. } = shared;
93 let ptr_ty = ptr.item();
94 let size_bytes = shared.size();
95 writeln!(f, "// Shared value size: {size_bytes} bytes")?;
96 writeln!(
97 f,
98 "{ptr_ty} {ptr} = reinterpret_cast<{ptr_ty}>(&dynamic_shared_mem[{offset}]);"
99 )
100 }
101 fn compile_polyfills(_f: &mut std::fmt::Formatter<'_>, _flags: &Flags<D>) -> std::fmt::Result {
102 Ok(())
103 }
104 fn address_space_for_value(_value: &Value<D>) -> String {
106 "".to_string()
107 }
108}
109
110pub trait DialectBindings<D: Dialect> {
113 fn compile_kernel_signature(
114 f: &mut std::fmt::Formatter<'_>,
115 kernel_name: &str,
116 tensor_maps: &[KernelArg<D>],
117 buffers: &[KernelArg<D>],
118 flags: &Flags<D>,
119 ) -> std::fmt::Result;
120 fn compile_bindings_body(
121 _f: &mut std::fmt::Formatter<'_>,
122 _body: &Body<D>,
123 ) -> std::fmt::Result {
124 Ok(())
125 }
126}
127
128pub trait DialectCubeBuiltins<D: Dialect> {
131 fn builtin_rules(flags: &CubeIndexFlags) -> CubeIndexFlags {
137 let unit_pos_plane = flags.unit_pos_plane;
138 let plane_dim_checked = flags.plane_dim_checked;
139 let plane_dim = flags.plane_dim || plane_dim_checked || unit_pos_plane;
140 let plane_pos = flags.plane_pos;
141 let absolute_pos = flags.absolute_pos || unit_pos_plane;
142 let absolute_pos_tuple = flags.absolute_pos_tuple || absolute_pos;
143 let cube_dim = flags.cube_dim;
144 let cube_dim_tuple = flags.cube_dim_tuple || cube_dim || absolute_pos || plane_dim_checked;
145 let unit_pos = flags.unit_pos;
146 let unit_pos_tuple = flags.unit_pos_tuple || unit_pos;
147 let cube_count = flags.cube_count;
148 let cube_count_tuple = flags.cube_count_tuple || absolute_pos;
149 let cube_pos = flags.cube_pos;
150 let cube_pos_tuple = flags.cube_pos_tuple || cube_pos;
151 let cluster_group = flags.cluster_pos;
152
153 CubeIndexFlags {
154 absolute_pos,
155 absolute_pos_tuple,
156 cube_count,
157 cube_count_tuple,
158 cube_dim,
159 cube_dim_tuple,
160 cube_pos,
161 cube_pos_tuple,
162 plane_dim,
163 plane_dim_checked,
164 plane_pos,
165 unit_pos_tuple,
166 unit_pos,
167 unit_pos_plane,
168 cluster_pos: cluster_group,
169 }
170 }
171
172 fn compile_absolute_pos_tuple_computation(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
173 let value = Builtin::<D>::AbsolutePosBaseName;
174 let ty = value.item();
175 let cube_pos_x = Builtin::<D>::CubePosX;
176 let cube_pos_y = Builtin::<D>::CubePosY;
177 let cube_pos_z = Builtin::<D>::CubePosZ;
178 let cube_dim_x = Builtin::<D>::CubeDimX;
179 let cube_dim_y = Builtin::<D>::CubeDimY;
180 let cube_dim_z = Builtin::<D>::CubeDimZ;
181 let unit_pos_x = Builtin::<D>::UnitPosX;
182 let unit_pos_y = Builtin::<D>::UnitPosY;
183 let unit_pos_z = Builtin::<D>::UnitPosZ;
184 writeln!(
185 f,
186 "{ty} {value} = make_{ty}(
187 {cube_pos_x} * {cube_dim_x} + {unit_pos_x},
188 {cube_pos_y} * {cube_dim_y} + {unit_pos_y},
189 {cube_pos_z} * {cube_dim_z} + {unit_pos_z}
190);"
191 )
192 }
193
194 fn compile_absolute_pos_base_name(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
195 f.write_str("absoluteIdx")
196 }
197
198 fn compile_absolute_pos(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
199 f.write_str("idxGlobal")
200 }
201
202 fn compile_absolute_pos_x(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
203 Self::compile_absolute_pos_base_name(f)?;
204 write!(f, ".x")
205 }
206
207 fn compile_absolute_pos_y(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
208 Self::compile_absolute_pos_base_name(f)?;
209 write!(f, ".y")
210 }
211
212 fn compile_absolute_pos_z(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
213 Self::compile_absolute_pos_base_name(f)?;
214 write!(f, ".z")
215 }
216
217 fn compile_cube_count_base_name(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
218 f.write_str("gridDim")
219 }
220
221 fn compile_cube_count(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
222 f.write_str("gridDimGlobal")
223 }
224
225 fn compile_cube_count_x(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
226 Self::compile_cube_count_base_name(f)?;
227 write!(f, ".x")
228 }
229
230 fn compile_cube_count_y(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
231 Self::compile_cube_count_base_name(f)?;
232 write!(f, ".y")
233 }
234
235 fn compile_cube_count_z(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
236 Self::compile_cube_count_base_name(f)?;
237 write!(f, ".z")
238 }
239
240 fn compile_cube_dim_base_name(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
241 f.write_str("blockDim")
242 }
243
244 fn compile_cube_dim(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
245 f.write_str("blockDimGlobal")
246 }
247
248 fn compile_cube_dim_x(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
249 Self::compile_cube_dim_base_name(f)?;
250 write!(f, ".x")
251 }
252
253 fn compile_cube_dim_y(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
254 Self::compile_cube_dim_base_name(f)?;
255 write!(f, ".y")
256 }
257
258 fn compile_cube_dim_z(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
259 Self::compile_cube_dim_base_name(f)?;
260 write!(f, ".z")
261 }
262
263 fn compile_cube_pos_base_name(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
264 f.write_str("blockIdx")
265 }
266
267 fn compile_cube_pos(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
268 f.write_str("blockIdxGlobal")
269 }
270
271 fn compile_cube_pos_x(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
272 Self::compile_cube_pos_base_name(f)?;
273 write!(f, ".x")
274 }
275
276 fn compile_cube_pos_y(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
277 Self::compile_cube_pos_base_name(f)?;
278 write!(f, ".y")
279 }
280
281 fn compile_cube_pos_z(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
282 Self::compile_cube_pos_base_name(f)?;
283 write!(f, ".z")
284 }
285
286 fn compile_unit_pos_computation(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
287 let value = Builtin::<D>::UnitPos;
288 let ty = value.item();
289 let cube_dim_x = Builtin::<D>::CubeDimX;
290 let cube_dim_y = Builtin::<D>::CubeDimY;
291 let unit_pos_x = Builtin::<D>::UnitPosX;
292 let unit_pos_y = Builtin::<D>::UnitPosY;
293 let unit_pos_z = Builtin::<D>::UnitPosZ;
294 writeln!(
295 f,
296 "{ty} {value} = {unit_pos_x} + {unit_pos_y} * {cube_dim_x} + {unit_pos_z} * ({cube_dim_x} * {cube_dim_y});"
297 )
298 }
299
300 fn compile_unit_pos(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
301 f.write_str("threadIdxGlobal")
302 }
303
304 fn compile_unit_pos_base_name(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
305 f.write_str("threadIdx")
306 }
307
308 fn compile_unit_pos_x(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
309 Self::compile_unit_pos_base_name(f)?;
310 write!(f, ".x")
311 }
312
313 fn compile_unit_pos_y(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
314 Self::compile_unit_pos_base_name(f)?;
315 write!(f, ".y")
316 }
317
318 fn compile_unit_pos_z(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
319 Self::compile_unit_pos_base_name(f)?;
320 write!(f, ".z")
321 }
322
323 fn compile_plane_dim(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
324 f.write_str("warpSize")
325 }
326
327 fn compile_plane_dim_checked(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
328 f.write_str("warpSizeChecked")
329 }
330
331 fn compile_plane_pos(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
332 let unit_pos_x = Builtin::<D>::UnitPosX;
333 let plane_dim = Builtin::<D>::PlaneDim;
334 write!(f, "{unit_pos_x} / {plane_dim}")
335 }
336
337 fn compile_unit_pos_plane(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
338 let absolute_pos = Builtin::<D>::AbsolutePos(Elem::U32);
339 let plane_dim = Builtin::<D>::PlaneDim;
340 let ty = Item::<D>::Scalar(Elem::U32);
341 write!(f, "{ty}({absolute_pos}) % {plane_dim}")
342 }
343
344 fn compile_cluster_pos(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
345 write!(f, "0")
346 }
347 fn compile_cluster_pos_x(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
348 write!(f, "0")
349 }
350 fn compile_cluster_pos_y(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
351 write!(f, "0")
352 }
353 fn compile_cluster_pos_z(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
354 write!(f, "0")
355 }
356}
357
358pub trait DialectInstructions<D: Dialect> {
361 fn compile_atomic_add(
363 f: &mut std::fmt::Formatter<'_>,
364 lhs: &Value<D>,
365 rhs: &Value<D>,
366 out: &Value<D>,
367 ) -> std::fmt::Result {
368 let rhs = rhs.ensure_lvalue(f)?;
369
370 let optimized = Value::optimized_args([*lhs, rhs, *out]);
371 let [lhs, rhs, out_optimized] = optimized.args;
372
373 let addr_space = D::address_space_for_value(out);
374 let out_item = out.item();
375 let out = out.fmt_left();
376
377 match out_optimized.item() {
378 Item::Scalar(Elem::I64) => writeln!(
379 f,
380 "{out} = atomicAdd(reinterpret_cast<{uint}*>({lhs}), {uint}({rhs}));",
381 uint = Elem::<D>::U64
382 ),
383 Item::Vector(inner, vectorization) if matches!(inner.elem(), Elem::F32) => {
384 let vec_ty = Item::NativeVector(*inner.elem(), vectorization);
385 let out_tmp = Value::tmp(out_optimized.item());
386 writeln!(
387 f,
388 "{vec_ty} {out_tmp} = atomicAdd(
389 reinterpret_cast<{addr_space}{vec_ty}*>({lhs}),
390 reinterpret_cast<const {addr_space}{vec_ty}&>({rhs}));",
391 )?;
392 writeln!(
393 f,
394 "{out} = reinterpret_cast<{addr_space}{out_item}&>({out_tmp});"
395 )
396 }
397 Item::Scalar(Elem::F16x2) | Item::Scalar(Elem::BF16x2) => {
398 let out_tmp = Value::tmp(out_optimized.item());
399 writeln!(
400 f,
401 "{} = atomicAdd(
402 reinterpret_cast<{}>({lhs}),
403 reinterpret_cast<const {addr_space}{}&>({rhs}));",
404 out_tmp.fmt_left(),
405 lhs.item(),
406 rhs.item()
407 )?;
408 writeln!(
409 f,
410 "{out} = reinterpret_cast<{addr_space}{out_item}&>({out_tmp});"
411 )
412 }
413 _ => writeln!(f, "{out} = atomicAdd({lhs}, {rhs});"),
414 }
415 }
416
417 fn compile_atomic_and(
418 f: &mut std::fmt::Formatter<'_>,
419 lhs: &Value<D>,
420 rhs: &Value<D>,
421 out: &Value<D>,
422 ) -> std::fmt::Result {
423 let out = out.fmt_left();
424 writeln!(f, "{out} = atomicAnd({lhs}, {rhs});")
425 }
426
427 fn compile_atomic_cas(
428 f: &mut std::fmt::Formatter<'_>,
429 input: &Value<D>,
430 cmp: &Value<D>,
431 val: &Value<D>,
432 out: &Value<D>,
433 ) -> std::fmt::Result {
434 let addr_space = D::address_space_for_value(out);
435 let out_item = out.item();
436 let out = out.fmt_left();
437
438 match val.item() {
439 Item::Vector(inner, 2) if matches!(inner.elem(), Elem::F32) => {
441 let cmp = cmp.ensure_lvalue(f)?;
442 let val = val.ensure_lvalue(f)?;
443 let u64 = Item::Scalar(Elem::<D>::U64);
444 let out_tmp = Value::tmp(u64);
445 writeln!(
446 f,
447 "{} = atomicCAS(
448 reinterpret_cast<{addr_space}{u64}*>({input}),
449 reinterpret_cast<{u64}&>({cmp}),
450 reinterpret_cast<{u64}&>({val}));",
451 out_tmp.fmt_left()
452 )?;
453 writeln!(f, "{out} = reinterpret_cast<{out_item}&>({out_tmp});")
454 }
455 Item::Vector(inner, 2) if matches!(inner.elem(), Elem::F16 | Elem::BF16) => {
456 let cmp = cmp.ensure_lvalue(f)?;
457 let val = val.ensure_lvalue(f)?;
458 let u32 = Item::Scalar(Elem::<D>::U32);
459 let out_tmp = Value::tmp(u32);
460 writeln!(
461 f,
462 "{} = atomicCAS(
463 reinterpret_cast<{addr_space}{u32}*>({input}),
464 reinterpret_cast<{u32}&>({cmp}),
465 reinterpret_cast<{u32}&>({val}));",
466 out_tmp.fmt_left()
467 )?;
468 writeln!(f, "{out} = reinterpret_cast<{out_item}&>({out_tmp});")
469 }
470 _ => writeln!(f, "{out} = atomicCAS({input}, {cmp}, {val});"),
471 }
472 }
473
474 fn compile_atomic_load(
475 f: &mut std::fmt::Formatter<'_>,
476 input: &Value<D>,
477 out: &Value<D>,
478 ) -> std::fmt::Result {
479 let out_item = out.item();
480 let out = out.fmt_left();
481
482 let Item::Pointer(_, class) = input.item() else {
483 unreachable!()
484 };
485
486 let unsigned_ty = match out_item.size() {
487 1 => Item::Scalar(Elem::<D>::U8),
488 2 => Item::Scalar(Elem::<D>::U16),
489 4 => Item::Scalar(Elem::<D>::U32),
490 8 => Item::Scalar(Elem::<D>::U64),
491 16 => {
494 let out_tmp = Value::tmp(out_item);
495 writeln!(f, "{};", out_tmp.fmt_left())?;
496 writeln!(
497 f,
498 "__nv_atomic_load({input}, &{out_tmp}, __NV_ATOMIC_RELAXED);"
499 )?;
500 return writeln!(f, "{out} = {out_tmp};");
501 }
502 _ => unreachable!(),
503 };
504 let unsigned_ptr_ty = Item::Pointer(unsigned_ty.intern(), class);
505
506 let ptr_tmp = Value::tmp(unsigned_ptr_ty);
507 let out_tmp = Value::tmp(unsigned_ty);
508 writeln!(
509 f,
510 "volatile {} = reinterpret_cast<volatile {unsigned_ptr_ty}>({input});",
511 ptr_tmp.fmt_left()
512 )?;
513 writeln!(f, "{} = *{ptr_tmp};", out_tmp.fmt_left())?;
514 writeln!(f, "{out} = reinterpret_cast<const {out_item}&>({out_tmp});")
515 }
516
517 fn compile_atomic_max(
518 f: &mut std::fmt::Formatter<'_>,
519 lhs: &Value<D>,
520 rhs: &Value<D>,
521 out: &Value<D>,
522 ) -> std::fmt::Result {
523 let out = out.fmt_left();
524 writeln!(f, "{out} = atomicMax({lhs}, {rhs});")
525 }
526
527 fn compile_atomic_min(
528 f: &mut std::fmt::Formatter<'_>,
529 lhs: &Value<D>,
530 rhs: &Value<D>,
531 out: &Value<D>,
532 ) -> std::fmt::Result {
533 let out = out.fmt_left();
534 writeln!(f, "{out} = atomicMin({lhs}, {rhs});")
535 }
536
537 fn compile_atomic_or(
538 f: &mut std::fmt::Formatter<'_>,
539 lhs: &Value<D>,
540 rhs: &Value<D>,
541 out: &Value<D>,
542 ) -> std::fmt::Result {
543 let out = out.fmt_left();
544 writeln!(f, "{out} = atomicOr({lhs}, {rhs});")
545 }
546
547 fn compile_atomic_store(
548 f: &mut std::fmt::Formatter<'_>,
549 input: &Value<D>,
550 out: &Value<D>,
551 ) -> std::fmt::Result {
552 let tmp = Value::tmp(input.item());
553 Self::compile_atomic_swap(f, out, input, &tmp)
554 }
555
556 fn compile_atomic_sub(
557 f: &mut std::fmt::Formatter<'_>,
558 lhs: &Value<D>,
559 rhs: &Value<D>,
560 out: &Value<D>,
561 ) -> std::fmt::Result {
562 let tmp = Value::tmp(rhs.item());
563 Neg::format(f, rhs, &tmp)?;
564 Self::compile_atomic_add(f, lhs, &tmp, out)
565 }
566
567 fn compile_atomic_swap(
568 f: &mut std::fmt::Formatter<'_>,
569 lhs: &Value<D>,
570 rhs: &Value<D>,
571 out: &Value<D>,
572 ) -> std::fmt::Result {
573 let out_item = out.item();
574 let out = out.fmt_left();
575
576 let unsigned_elem = match rhs.item().size() {
577 1 => Elem::<D>::U8,
578 2 => Elem::<D>::U16,
579 4 => Elem::<D>::U32,
580 8 => Elem::<D>::U64,
581 _ => return writeln!(f, "{out} = atomicExch({lhs}, {rhs});"),
583 };
584
585 let rhs = rhs.ensure_lvalue(f)?;
586 let Item::Pointer(_, class) = lhs.item() else {
587 unreachable!()
588 };
589 let unsigned_ty = Item::Scalar(unsigned_elem);
590 let unsigned_ptr_ty = Item::Pointer(unsigned_ty.intern(), class);
591
592 let out_tmp = Value::tmp(unsigned_ty);
593 writeln!(
594 f,
595 "{} = atomicExch(
596 reinterpret_cast<{unsigned_ptr_ty}>({lhs}),
597 reinterpret_cast<const {unsigned_ty}&>({rhs}));",
598 out_tmp.fmt_left()
599 )?;
600 writeln!(f, "{out} = reinterpret_cast<const {out_item}&>({out_tmp});")
601 }
602
603 fn compile_atomic_xor(
604 f: &mut std::fmt::Formatter<'_>,
605 lhs: &Value<D>,
606 rhs: &Value<D>,
607 out: &Value<D>,
608 ) -> std::fmt::Result {
609 let out = out.fmt_left();
610 writeln!(f, "{out} = atomicXor({lhs}, {rhs});")
611 }
612
613 fn compile_saturating_add(
614 f: &mut std::fmt::Formatter<'_>,
615 lhs: impl Display,
616 rhs: impl Display,
617 item: Item<D>,
618 ) -> std::fmt::Result;
619
620 fn compile_saturating_sub(
621 f: &mut std::fmt::Formatter<'_>,
622 lhs: impl Display,
623 rhs: impl Display,
624 item: Item<D>,
625 ) -> std::fmt::Result;
626
627 fn compile_instruction_printf(
629 f: &mut std::fmt::Formatter<'_>,
630 format_string: &str,
631 args: &[Value<D>],
632 ) -> std::fmt::Result {
633 let args = args.iter().map(|arg| format!("{arg}")).collect::<Vec<_>>();
634 let args = match args.is_empty() {
635 true => "".to_string(),
636 false => format!(", {}", args.join(",")),
637 };
638 writeln!(f, "printf({format_string:?}{args});")
639 }
640
641 fn compile_instruction_log1p_scalar<T: Component<D>>(
643 f: &mut std::fmt::Formatter<'_>,
644 input: T,
645 ) -> std::fmt::Result {
646 let elem = input.elem();
647 match elem {
648 Elem::F16 | Elem::F16x2 | Elem::BF16 | Elem::BF16x2 => {
649 write!(f, "{elem}(log1p(float({input})))")
650 }
651 _ => write!(f, "log1p({input})"),
652 }
653 }
654
655 fn compile_instruction_expm1_scalar<T: Component<D>>(
657 f: &mut std::fmt::Formatter<'_>,
658 input: T,
659 ) -> std::fmt::Result {
660 let elem = input.elem();
661 match elem {
662 Elem::F16 | Elem::F16x2 | Elem::BF16 | Elem::BF16x2 => {
663 write!(f, "{elem}(expm1(float({input})))")
664 }
665 _ => write!(f, "expm1({input})"),
666 }
667 }
668
669 fn compile_instruction_sync_threads(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result;
671 fn compile_instruction_sync_warp(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result;
672 fn compile_instruction_thread_fence(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result;
673
674 fn compile_instruction_tanh_scalar<T: Component<D>>(
676 f: &mut std::fmt::Formatter<'_>,
677 input: T,
678 ) -> std::fmt::Result {
679 let elem = input.elem();
680 match elem {
681 Elem::F16 | Elem::F16x2 | Elem::BF16 | Elem::BF16x2 => {
682 write!(f, "{elem}(tanh(float({input})))")
683 }
684 _ => write!(f, "tanh({input})"),
685 }
686 }
687
688 fn compile_instruction_find_first_set<T: Component<D>>(
690 f: &mut std::fmt::Formatter<'_>,
691 input: T,
692 out_elem: Elem<D>,
693 ) -> std::fmt::Result;
694 fn compile_instruction_leading_zeros_scalar<T: Component<D>>(
695 f: &mut std::fmt::Formatter<'_>,
696 input: T,
697 out_elem: Elem<D>,
698 ) -> std::fmt::Result;
699
700 fn compile_instruction_trailing_zeros_scalar<T: Component<D>>(
701 f: &mut std::fmt::Formatter<'_>,
702 input: T,
703 out_elem: Elem<D>,
704 ) -> std::fmt::Result;
705
706 fn compile_instruction_popcount_scalar<T: Component<D>>(
707 f: &mut std::fmt::Formatter<'_>,
708 input: T,
709 out_elem: Elem<D>,
710 ) -> std::fmt::Result {
711 write!(f, "{out_elem}(")?;
712 match input.elem() {
713 Elem::I32 => write!(f, "__popc({}({input}))", Elem::<D>::U32),
714 Elem::U32 => write!(f, "__popc({input})"),
715 Elem::I64 => write!(f, "__popcll({}({input}))", Elem::<D>::U64),
716 Elem::U64 => write!(f, "__popcll({input})"),
717 _ => write!(f, "__popc({})", super::unary::zero_extend(input)),
718 }?;
719 write!(f, ")")
720 }
721
722 fn compile_instruction_reverse_bits_scalar<T: Component<D>>(
723 f: &mut std::fmt::Formatter<'_>,
724 input: T,
725 out_elem: Elem<D>,
726 ) -> std::fmt::Result {
727 write!(f, "{out_elem}(")?;
728 match out_elem {
729 Elem::I32 => write!(f, "__brev({}({input}))", Elem::<D>::U32),
730 Elem::U32 => write!(f, "__brev({input})"),
731 Elem::I64 => write!(f, "__brevll({}({input}))", Elem::<D>::U64),
732 Elem::U64 => write!(f, "__brevll({input})"),
733 _ => write!(
734 f,
735 "__brev({}) >> {}",
736 super::unary::zero_extend(input),
737 (size_of::<u32>() - out_elem.size()) * 8
738 ),
739 }?;
740 write!(f, ")")
741 }
742
743 fn compile_instruction_max_function_name(
745 f: &mut std::fmt::Formatter<'_>,
746 item: Item<D>,
747 ) -> std::fmt::Result;
748
749 fn compile_instruction_min_function_name(
750 f: &mut std::fmt::Formatter<'_>,
751 item: Item<D>,
752 ) -> std::fmt::Result;
753
754 fn compile_instruction_powf(
755 f: &mut std::fmt::Formatter<'_>,
756 lhs: &str,
757 rhs: &str,
758 elem: Elem<D>,
759 ) -> std::fmt::Result {
760 match elem {
761 Elem::F32 => write!(f, "powf({lhs}, {rhs})"),
762 Elem::F64 => write!(f, "pow({lhs}, {rhs})"),
763 _ => write!(f, "#error Unsupported type for powf: {elem}"),
764 }
765 }
766
767 fn compile_instruction_hypot(
768 f: &mut std::fmt::Formatter<'_>,
769 lhs: &str,
770 rhs: &str,
771 elem: Elem<D>,
772 ) -> std::fmt::Result {
773 match elem {
774 Elem::F32 => write!(f, "hypotf({lhs}, {rhs})"),
775 Elem::F64 => write!(f, "hypot({lhs}, {rhs})"),
776 _ => write!(f, "#error Unsupported type for hypot: {elem}"),
777 }
778 }
779
780 fn compile_instruction_rhypot(
781 f: &mut std::fmt::Formatter<'_>,
782 lhs: &str,
783 rhs: &str,
784 elem: Elem<D>,
785 ) -> std::fmt::Result {
786 match elem {
787 Elem::F32 => write!(f, "rhypotf({lhs}, {rhs})"),
788 Elem::F64 => write!(f, "rhypot({lhs}, {rhs})"),
789 _ => write!(f, "#error Unsupported type for rhypot: {elem}"),
790 }
791 }
792
793 fn compile_instruction_half_function_name_prefix() -> &'static str {
794 "h"
795 }
796
797 fn compile_instruction_half2_function_name_prefix() -> &'static str {
798 "h2"
799 }
800
801 fn compile_fast_math_function_name(name: &'static str) -> &'static str {
804 name
805 }
806
807 fn compile_warp_shuffle(
809 f: &mut std::fmt::Formatter<'_>,
810 val: &str,
811 elem: &Elem<D>,
812 source: &str,
813 ) -> std::fmt::Result;
814 fn compile_warp_shuffle_xor(
815 f: &mut std::fmt::Formatter<'_>,
816 val: &str,
817 elem: &Elem<D>,
818 offset: &str,
819 ) -> std::fmt::Result;
820 fn compile_warp_shuffle_up(
821 f: &mut std::fmt::Formatter<'_>,
822 val: &str,
823 elem: &Elem<D>,
824 offset: &str,
825 ) -> std::fmt::Result;
826 fn compile_warp_shuffle_down(
827 f: &mut std::fmt::Formatter<'_>,
828 val: &str,
829 elem: &Elem<D>,
830 offset: &str,
831 ) -> std::fmt::Result;
832 fn compile_warp_all<T: Component<D>>(
833 f: &mut std::fmt::Formatter<'_>,
834 input: &T,
835 ) -> std::fmt::Result;
836 fn compile_warp_any<T: Component<D>>(
837 f: &mut std::fmt::Formatter<'_>,
838 input: &T,
839 ) -> std::fmt::Result;
840 fn compile_warp_ballot(
841 f: &mut std::fmt::Formatter<'_>,
842 input: &Value<D>,
843 out_elem: &Elem<D>,
844 ) -> std::fmt::Result;
845 fn compile_warp_elect(f: &mut std::fmt::Formatter<'_>, out: &str) -> std::fmt::Result {
846 write!(
847 f,
848 "
849unsigned int mask = __activemask();
850unsigned int leader = __ffs(mask) - 1;
851{out} = threadIdx.x % warpSize == leader;
852 "
853 )
854 }
855 fn compile_unreachable(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result;
856}
857
858#[derive(Debug, Clone, Copy, new)]
859pub struct ManualMma<'a, D: Dialect> {
860 pub shape: MmaShape<D>,
861 pub frag_a: &'a Value<D>,
862 pub frag_b: &'a Value<D>,
863 pub frag_c: &'a Value<D>,
864 pub frag_d: &'a Value<D>,
865}
866
867pub trait DialectWarpReduceCompiler<D: Dialect>:
868 Default + Clone + Copy + Debug + Send + Sync + Eq + Hash + 'static
869{
870 fn warp_reduce_sum(
871 f: &mut core::fmt::Formatter<'_>,
872 input: &Value<D>,
873 out: &Value<D>,
874 ) -> core::fmt::Result {
875 reduce_operator(f, input, out, "+=")
876 }
877 fn warp_reduce_prod(
878 f: &mut core::fmt::Formatter<'_>,
879 input: &Value<D>,
880 out: &Value<D>,
881 ) -> core::fmt::Result {
882 reduce_operator(f, input, out, "*=")
883 }
884 fn warp_reduce_max(
885 f: &mut core::fmt::Formatter<'_>,
886 input: &Value<D>,
887 out: &Value<D>,
888 ) -> core::fmt::Result {
889 reduce_comparison(f, input, out, D::compile_instruction_max_function_name)
890 }
891 fn warp_reduce_min(
892 f: &mut core::fmt::Formatter<'_>,
893 input: &Value<D>,
894 out: &Value<D>,
895 ) -> core::fmt::Result {
896 reduce_comparison(f, input, out, D::compile_instruction_min_function_name)
897 }
898 fn warp_reduce_all(
899 f: &mut core::fmt::Formatter<'_>,
900 input: &Value<D>,
901 out: &Value<D>,
902 ) -> core::fmt::Result {
903 reduce_quantifier(f, input, out, D::compile_warp_all::<IndexedValue<D>>)
904 }
905 fn warp_reduce_any(
906 f: &mut core::fmt::Formatter<'_>,
907 input: &Value<D>,
908 out: &Value<D>,
909 ) -> core::fmt::Result {
910 reduce_quantifier(f, input, out, D::compile_warp_any::<IndexedValue<D>>)
911 }
912 fn warp_reduce_sum_inclusive(
913 f: &mut core::fmt::Formatter<'_>,
914 input: &Value<D>,
915 out: &Value<D>,
916 ) -> core::fmt::Result {
917 reduce_inclusive(f, input, out, "+=")
918 }
919 fn warp_reduce_prod_inclusive(
920 f: &mut core::fmt::Formatter<'_>,
921 input: &Value<D>,
922 out: &Value<D>,
923 ) -> core::fmt::Result {
924 reduce_inclusive(f, input, out, "*=")
925 }
926 fn warp_reduce_sum_exclusive(
927 f: &mut core::fmt::Formatter<'_>,
928 input: &Value<D>,
929 out: &Value<D>,
930 ) -> core::fmt::Result {
931 reduce_exclusive(f, input, out, "+=", "0")
932 }
933 fn warp_reduce_prod_exclusive(
934 f: &mut core::fmt::Formatter<'_>,
935 input: &Value<D>,
936 out: &Value<D>,
937 ) -> core::fmt::Result {
938 reduce_exclusive(f, input, out, "*=", "1")
939 }
940}
941
942pub trait DialectWmmaCompiler<D: Dialect>:
943 Default + Clone + Copy + Debug + Send + Sync + Eq + Hash + 'static
944{
945 #[allow(unused_variables)]
946 fn compile_wmma_includes(
947 f: &mut std::fmt::Formatter<'_>,
948 flags: &Flags<D>,
949 ) -> std::fmt::Result {
950 Ok(())
951 }
952 #[allow(unused_variables)]
953 fn compile_wmma_type_definitions(
954 f: &mut std::fmt::Formatter<'_>,
955 flags: &Flags<D>,
956 ) -> std::fmt::Result {
957 Ok(())
958 }
959 #[allow(unused_variables)]
960 fn compile_wmma_local_variables(f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
961 Ok(())
962 }
963 #[allow(unused_variables)]
964 fn compile_wwma_fragment_ident(
965 f: &mut std::fmt::Formatter<'_>,
966 ident: &FragmentIdent<D>,
967 ) -> std::fmt::Result {
968 Ok(())
969 }
970 #[allow(unused_variables)]
971 fn compile_wmma_fragment_layout(
972 f: &mut std::fmt::Formatter<'_>,
973 layout: &FragmentLayout<D>,
974 ) -> std::fmt::Result {
975 Ok(())
976 }
977 #[allow(unused_variables)]
978 fn compile_wmma_fragment(
979 f: &mut std::fmt::Formatter<'_>,
980 fragment: &FragmentType<D>,
981 ) -> std::fmt::Result {
982 Ok(())
983 }
984
985 fn compile_wmma_fragment_declaration(
986 f: &mut std::fmt::Formatter<'_>,
987 val: &Value<D>,
988 value_ty: &Item<D>,
989 ) -> std::fmt::Result;
990
991 fn compile_wmma_instruction(
992 f: &mut std::fmt::Formatter<'_>,
993 instruction: &WmmaInstruction<D>,
994 ) -> std::fmt::Result;
995 fn compile_manual_mma(f: &mut std::fmt::Formatter<'_>, mma: ManualMma<D>) -> std::fmt::Result;
996 fn compile_scaled_mma(
997 f: &mut std::fmt::Formatter<'_>,
998 mma: ManualMma<D>,
999 scales_a: Value<D>,
1000 scales_b: Value<D>,
1001 scales_factor: u32,
1002 ) -> std::fmt::Result;
1003 fn supported_wmma_combinations(arch: &D::Architecture) -> SupportedMmaCombinations;
1004 fn supported_mma_combinations(arch: &D::Architecture) -> SupportedMmaCombinations;
1005 fn supported_scaled_mma_combinations(
1006 _arch: &D::Architecture,
1007 ) -> SupportedScaledMmaCombinations {
1008 Vec::new()
1009 }
1010}
1011
1012pub trait DialectProcessors<D: Dialect> {
1015 fn processors() -> Vec<Box<dyn Processor>>;
1016}