1use cubecl_core::ir::Id;
2
3use crate::shared::{Builtin, FmtLeft};
4
5use super::{
6 Component, Dialect, Elem, Item, Value, WarpInstruction, WmmaInstruction, barrier::BarrierOps,
7 binary::*, unary::*,
8};
9use std::{
10 borrow::Cow,
11 fmt::{Display, Formatter, Write},
12 marker::PhantomData,
13};
14
15pub(crate) const INFO_NAME: &str = "info";
16pub(crate) const DYNAMIC_META_NAME: &str = "dynamic_meta";
17pub(crate) const STATIC_META_NAME: &str = "info.static_meta";
18
19#[derive(Debug, Clone, Copy)]
20pub struct BinaryInstruction<D: Dialect> {
21 pub lhs: Value<D>,
22 pub rhs: Value<D>,
23 pub out: Value<D>,
24}
25
26#[derive(Debug, Clone)]
27pub struct IndexInstruction<D: Dialect> {
28 pub list: Value<D>,
29 pub index: Value<D>,
30 pub out: Value<D>,
31}
32
33#[derive(Debug, Clone, Copy)]
34pub struct UnaryInstruction<D: Dialect> {
35 pub input: Value<D>,
36 pub out: Value<D>,
37}
38
39#[derive(Debug, Clone)]
40pub enum Instruction<D: Dialect> {
41 Metadata {
42 info_offset: Value<D>,
43 out: Value<D>,
44 },
45 ExtendedMetadata {
46 info_offset: Value<D>,
47 dim: Value<D>,
48 out: Value<D>,
49 },
50 ConstLength {
51 length: usize,
52 out: Value<D>,
53 },
54 SliceLength {
55 input: Value<D>,
56 out: Value<D>,
57 },
58 DeclareVariable {
59 val: Value<D>,
60 value_ty: Item<D>,
61 },
62 Add(BinaryInstruction<D>),
63 SaturatingAdd(BinaryInstruction<D>),
64 Fma {
65 a: Value<D>,
66 b: Value<D>,
67 c: Value<D>,
68 out: Value<D>,
69 },
70 Div(BinaryInstruction<D>),
71 Rem(BinaryInstruction<D>),
72 ModFloor(BinaryInstruction<D>),
73 FastDiv(BinaryInstruction<D>),
74 FastRecip(UnaryInstruction<D>),
75 Mul(BinaryInstruction<D>),
76 Sub(BinaryInstruction<D>),
77 SaturatingSub(BinaryInstruction<D>),
78 HiMul(BinaryInstruction<D>),
79 Index(IndexInstruction<D>),
80 Assign(UnaryInstruction<D>),
81 ReadBuiltin {
82 builtin: Builtin<D>,
83 out: Value<D>,
84 },
85 ReadScalar {
86 id: Id,
87 out: Value<D>,
88 },
89 Store(UnaryInstruction<D>),
90 Load(UnaryInstruction<D>),
91 SpecialCast(UnaryInstruction<D>),
92 RangeLoop {
93 i: Value<D>,
94 start: Value<D>,
95 end: Value<D>,
96 step: Option<Value<D>>,
97 inclusive: bool,
98 instructions: Vec<Self>,
99 },
100 VecInit {
101 inputs: Vec<Value<D>>,
102 out: Value<D>,
103 },
104 InsertComponent {
105 vector: Value<D>,
106 index: Value<D>,
107 value: Value<D>,
108 out: Value<D>,
109 },
110 ExtractComponent(BinaryInstruction<D>),
111 Loop {
112 instructions: Vec<Self>,
113 },
114 If {
115 cond: Value<D>,
116 instructions: Vec<Self>,
117 },
118 IfElse {
119 cond: Value<D>,
120 instructions_if: Vec<Self>,
121 instructions_else: Vec<Self>,
122 },
123 Select {
124 cond: Value<D>,
125 then: Value<D>,
126 or_else: Value<D>,
127 out: Value<D>,
128 },
129 Switch {
130 value: Value<D>,
131 instructions_default: Vec<Self>,
132 instructions_cases: Vec<(Value<D>, Vec<Self>)>,
133 },
134 Slice {
135 input: Value<D>,
136 start: Value<D>,
137 end: Value<D>,
138 out: Value<D>,
139 },
140 CheckedSlice {
141 input: Value<D>,
142 start: Value<D>,
143 end: Value<D>,
144 out: Value<D>,
145 len: Value<D>,
146 },
147 ReinterpretSlice {
148 input: Value<D>,
149 vector_size: u32,
150 out: Value<D>,
151 },
152 Return,
153 Break,
154 Unreachable,
155 Equal(BinaryInstruction<D>),
156 NotEqual(BinaryInstruction<D>),
157 Lower(BinaryInstruction<D>),
158 Greater(BinaryInstruction<D>),
159 LowerEqual(BinaryInstruction<D>),
160 GreaterEqual(BinaryInstruction<D>),
161 Erf(UnaryInstruction<D>),
162 BitwiseOr(BinaryInstruction<D>),
163 BitwiseAnd(BinaryInstruction<D>),
164 BitwiseXor(BinaryInstruction<D>),
165 CountBits(UnaryInstruction<D>),
166 ReverseBits(UnaryInstruction<D>),
167 ShiftLeft(BinaryInstruction<D>),
168 ShiftRight(BinaryInstruction<D>),
169 BitwiseNot(UnaryInstruction<D>),
170 LeadingZeros(UnaryInstruction<D>),
171 TrailingZeros(UnaryInstruction<D>),
172 FindFirstSet(UnaryInstruction<D>),
173 Abs(UnaryInstruction<D>),
174 Exp(UnaryInstruction<D>),
175 FastExp(UnaryInstruction<D>),
176 Log(UnaryInstruction<D>),
177 FastLog(UnaryInstruction<D>),
178 Log1p(UnaryInstruction<D>),
179 Expm1(UnaryInstruction<D>),
180 Cos(UnaryInstruction<D>),
181 Sin(UnaryInstruction<D>),
182 Tan(UnaryInstruction<D>),
183 Tanh(UnaryInstruction<D>),
184 Sinh(UnaryInstruction<D>),
185 Cosh(UnaryInstruction<D>),
186 ArcCos(UnaryInstruction<D>),
187 ArcSin(UnaryInstruction<D>),
188 ArcTan(UnaryInstruction<D>),
189 ArcSinh(UnaryInstruction<D>),
190 ArcCosh(UnaryInstruction<D>),
191 ArcTanh(UnaryInstruction<D>),
192 Degrees(UnaryInstruction<D>),
193 Radians(UnaryInstruction<D>),
194 ArcTan2(BinaryInstruction<D>),
195 FastSin(UnaryInstruction<D>),
196 FastCos(UnaryInstruction<D>),
197 FastTanh(UnaryInstruction<D>),
198 Powf(BinaryInstruction<D>),
199 FastPowf(BinaryInstruction<D>),
200 Powi(BinaryInstruction<D>),
201 Hypot(BinaryInstruction<D>),
202 Rhypot(BinaryInstruction<D>),
203 Sqrt(UnaryInstruction<D>),
204 FastSqrt(UnaryInstruction<D>),
205 InverseSqrt(UnaryInstruction<D>),
206 FastInverseSqrt(UnaryInstruction<D>),
207 Min(BinaryInstruction<D>),
208 Max(BinaryInstruction<D>),
209 Not(UnaryInstruction<D>),
210 Or(BinaryInstruction<D>),
211 And(BinaryInstruction<D>),
212 Clamp {
213 input: Value<D>,
214 min_value: Value<D>,
215 max_value: Value<D>,
216 out: Value<D>,
217 },
218 IsNan(UnaryInstruction<D>),
219 IsInf(UnaryInstruction<D>),
220 SyncThreads,
221 SyncWarp,
222 ThreadFence,
223 ProxyAsyncToSharedFence,
224 BulkCommitGroup,
225 BulkWaitGroup {
226 max_pending: u32,
227 },
228 BulkWaitGroupRead {
229 max_pending: u32,
230 },
231 TmaReplacePointer {
232 buffer: Value<D>,
233 offset: Value<D>,
234 tensor_map: Value<D>,
235 out: Value<D>,
236 },
237 Round(UnaryInstruction<D>),
238 Ceil(UnaryInstruction<D>),
239 Trunc(UnaryInstruction<D>),
240 Floor(UnaryInstruction<D>),
241 Warp(WarpInstruction<D>),
242 Wmma(WmmaInstruction<D>),
243 Bitcast(UnaryInstruction<D>),
244 AtomicLoad(UnaryInstruction<D>),
245 AtomicStore(UnaryInstruction<D>),
246 AtomicSwap(BinaryInstruction<D>),
247 AtomicAdd(BinaryInstruction<D>),
248 AtomicSub(BinaryInstruction<D>),
249 AtomicMax(BinaryInstruction<D>),
250 AtomicMin(BinaryInstruction<D>),
251 AtomicAnd(BinaryInstruction<D>),
252 AtomicOr(BinaryInstruction<D>),
253 AtomicXor(BinaryInstruction<D>),
254 AtomicCAS {
255 input: Value<D>,
256 cmp: Value<D>,
257 val: Value<D>,
258 out: Value<D>,
259 },
260 Neg(UnaryInstruction<D>),
261 Magnitude(UnaryInstruction<D>),
262 FastMagnitude(UnaryInstruction<D>),
263 Normalize(UnaryInstruction<D>),
264 FastNormalize(UnaryInstruction<D>),
265 Dot(BinaryInstruction<D>),
266 VectorSum(UnaryInstruction<D>),
267 Copy {
268 source: Value<D>,
269 dest: Value<D>,
270 len: u32,
271 },
272 Printf {
273 format_string: String,
274 args: Vec<Value<D>>,
275 },
276 Comment {
277 content: String,
278 },
279 Barrier(BarrierOps<D>),
280 MemCopyAsyncTensorSharedToGlobal {
281 smem_buffer: Value<D>,
282 tensor_map: Value<D>,
283 indices: Vec<Value<D>>,
284 },
285 Line {
286 file: Cow<'static, str>,
287 line: u32,
288 },
289}
290
291impl<D: Dialect> Display for Instruction<D> {
292 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
293 match self {
294 Instruction::Return => f.write_str("return;"),
295 Instruction::Break => f.write_str("break;"),
296 Instruction::Unreachable => D::compile_unreachable(f),
297 Instruction::DeclareVariable { val, value_ty } => {
298 match value_ty {
299 Item::Fragment(_) => {
300 D::compile_wmma_fragment_declaration(f, val, value_ty)?;
301 }
302 item => {
303 writeln!(f, "{item} {val}_store;")?;
304 }
305 };
306 writeln!(f, "{} {val} = &{val}_store;", val.item())
307 }
308 Instruction::Add(it) => Add::format(f, &it.lhs, &it.rhs, &it.out),
309 Instruction::SaturatingAdd(it) => SaturatingAdd::format(f, &it.lhs, &it.rhs, &it.out),
310 Instruction::Slice {
311 input,
312 start,
313 end,
314 out,
315 } => {
316 let item = out.item();
317 let addr_space = D::address_space_for_value(input);
318 writeln!(f, "const uint {out}_length = {end} - {start};")?;
319 writeln!(f, "{addr_space}{item} *{out} = {input} + {start};")
320 }
321 Instruction::CheckedSlice {
322 input,
323 start,
324 end,
325 out,
326 len,
327 } => {
328 let item = out.item();
329 let addr_space = D::address_space_for_value(input);
330 writeln!(f, "const uint {out}_length = min({len}, {end}) - {start};")?;
331 writeln!(f, "{addr_space}{item} *{out} = {input} + {start};")
332 }
333 Instruction::ReinterpretSlice {
334 input,
335 vector_size,
336 out,
337 } => {
338 let item = Item::new(out.elem(), *vector_size as usize);
339 let addr_space = D::address_space_for_value(input);
340
341 writeln!(
342 f,
343 "{addr_space}{item} *{out} = reinterpret_cast<{item}*>({input});"
344 )
345 }
346 Instruction::Mul(it) => Mul::format(f, &it.lhs, &it.rhs, &it.out),
347 Instruction::Div(it) => Div::format(f, &it.lhs, &it.rhs, &it.out),
348 Instruction::FastDiv(it) => FastDiv::format(f, &it.lhs, &it.rhs, &it.out),
349 Instruction::FastRecip(it) => FastRecip::format(f, &it.input, &it.out),
350 Instruction::Sub(it) => Sub::format(f, &it.lhs, &it.rhs, &it.out),
351 Instruction::SaturatingSub(it) => SaturatingSub::format(f, &it.lhs, &it.rhs, &it.out),
352 Instruction::HiMul(it) => HiMul::format(f, &it.lhs, &it.rhs, &it.out),
353 Instruction::ModFloor(inst) => ModFloor::format(f, &inst.lhs, &inst.rhs, &inst.out),
354 Instruction::BitwiseOr(it) => BitwiseOr::format(f, &it.lhs, &it.rhs, &it.out),
355 Instruction::BitwiseAnd(it) => BitwiseAnd::format(f, &it.lhs, &it.rhs, &it.out),
356 Instruction::BitwiseXor(it) => BitwiseXor::format(f, &it.lhs, &it.rhs, &it.out),
357 Instruction::CountBits(it) => CountBits::format(f, &it.input, &it.out),
358 Instruction::ReverseBits(it) => ReverseBits::format(f, &it.input, &it.out),
359 Instruction::LeadingZeros(it) => LeadingZeros::format(f, &it.input, &it.out),
360 Instruction::TrailingZeros(it) => TrailingZeros::format(f, &it.input, &it.out),
361 Instruction::FindFirstSet(it) => FindFirstSet::format(f, &it.input, &it.out),
362 Instruction::ShiftLeft(it) => ShiftLeft::format(f, &it.lhs, &it.rhs, &it.out),
363 Instruction::ShiftRight(it) => ShiftRight::format(f, &it.lhs, &it.rhs, &it.out),
364 Instruction::Index(it) => Index::format(f, &it.list, &it.index, &it.out),
365 Instruction::Copy { source, dest, len } => {
366 for i in 0..*len {
367 writeln!(f, "*({dest} + {i}) = *({source} + {i});")?;
368 }
369 Ok(())
370 }
371 Instruction::Assign(it) => Assign::format(f, &it.input, &it.out),
372 Instruction::Store(it) => {
373 writeln!(f, "*{} = {};", it.out, it.input)
374 }
375 Instruction::Load(it) => {
376 let out = it.out.fmt_left();
377 writeln!(f, "{out} = *{};", it.input)
378 }
379 Instruction::RangeLoop {
380 i,
381 start,
382 end,
383 step,
384 inclusive,
385 instructions,
386 } => {
387 let increment = step
388 .map(|step| format!("*{i} += {step}"))
389 .unwrap_or_else(|| format!("++*{i}"));
390 let cmp = if *inclusive { "<=" } else { "<" };
391 write!(
392 f,
393 "
394for (*{i} = {start}; *{i} {cmp} {end}; {increment}) {{
395"
396 )?;
397 for instruction in instructions {
398 write!(f, "{instruction}")?;
399 }
400
401 f.write_str("}\n")
402 }
403 Instruction::Loop { instructions } => {
404 writeln!(f, "while (true) {{")?;
405 for i in instructions {
406 write!(f, "{i}")?;
407 }
408 f.write_str("}\n")
409 }
410 Instruction::If { cond, instructions } => {
411 writeln!(f, "if ({cond}) {{")?;
412 for i in instructions {
413 write!(f, "{i}")?;
414 }
415 f.write_str("}\n")
416 }
417 Instruction::IfElse {
418 cond,
419 instructions_if,
420 instructions_else,
421 } => {
422 writeln!(f, "if ({cond}) {{")?;
423 for i in instructions_if {
424 write!(f, "{i}")?;
425 }
426 f.write_str("} else {\n")?;
427 for i in instructions_else {
428 write!(f, "{i}")?;
429 }
430 f.write_str("}\n")
431 }
432 Instruction::Select {
433 cond,
434 then,
435 or_else,
436 out,
437 } => {
438 let item_or_else = or_else.item();
439 let item_then = then.item();
440 let item_out = out.item();
441
442 let vf_then = item_then.vectorization();
443 let vf_or_else = item_or_else.vectorization();
444 let vf_out = item_out.vectorization();
445 let vf_cond = cond.item().vectorization();
446
447 let item_out = out.item();
448 let cond_elem = cond.elem();
449 let out = out.fmt_left();
450
451 let vf = usize::max(vf_cond, vf_out);
456 let vf = usize::max(vf, vf_then);
457 let vf = usize::max(vf, vf_or_else);
458 let should_broadcast = vf > 1;
459
460 if should_broadcast {
466 writeln!(f, "{out} = {item_out} {{")?;
467 for i in 0..vf {
468 let theni = then.index(i);
469 let or_elsei = or_else.index(i);
470 let condi = cond.index(i);
471 let condi = EnsureBoolArg {
472 val: &condi,
473 elem: &cond_elem,
474 };
475
476 writeln!(f, "({condi}) ? {theni} : {or_elsei},")?;
477 }
478
479 writeln!(f, "}};")
480 } else {
481 let cond = EnsureBoolArg {
482 val: &cond,
483 elem: &cond_elem,
484 };
485 writeln!(f, "{out} = ({cond}) ? {then} : {or_else};")
486 }
487 }
488 Instruction::Switch {
489 value,
490 instructions_default,
491 instructions_cases,
492 } => {
493 writeln!(f, "switch({value}) {{")?;
494 for (value, block) in instructions_cases {
495 write!(f, "case {value}:\n{{\n")?;
496 for i in block {
497 i.fmt(f)?;
498 }
499 f.write_str("break;\n}\n")?;
500 }
501 f.write_str("default:\n{")?;
502 for i in instructions_default {
503 i.fmt(f)?;
504 }
505 f.write_str("break;\n}\n}\n")
506 }
507 Instruction::Metadata { info_offset, out } => {
508 let out = out.fmt_left();
509 writeln!(f, "{out} = {STATIC_META_NAME}[{info_offset}];")
510 }
511 Instruction::ExtendedMetadata {
512 info_offset,
513 dim,
514 out,
515 } => {
516 let out = out.fmt_left();
517 writeln!(
518 f,
519 "{out} = {DYNAMIC_META_NAME}[{STATIC_META_NAME}[{info_offset}] + {dim}];"
520 )
521 }
522 Instruction::Equal(it) => Equal::format(f, &it.lhs, &it.rhs, &it.out),
523 Instruction::NotEqual(it) => NotEqual::format(f, &it.lhs, &it.rhs, &it.out),
524 Instruction::Lower(it) => Lower::format(f, &it.lhs, &it.rhs, &it.out),
525 Instruction::Greater(it) => Greater::format(f, &it.lhs, &it.rhs, &it.out),
526 Instruction::LowerEqual(it) => LowerEqual::format(f, &it.lhs, &it.rhs, &it.out),
527 Instruction::GreaterEqual(it) => GreaterEqual::format(f, &it.lhs, &it.rhs, &it.out),
528 Instruction::Erf(it) => Erf::format(f, &it.input, &it.out),
529 Instruction::Abs(it) => Abs::format(f, &it.input, &it.out),
530 Instruction::Exp(it) => Exp::format(f, &it.input, &it.out),
531 Instruction::FastExp(it) => FastExp::format(f, &it.input, &it.out),
532 Instruction::Log(it) => Log::format(f, &it.input, &it.out),
533 Instruction::FastLog(it) => FastLog::format(f, &it.input, &it.out),
534 Instruction::Log1p(it) => Log1p::format(f, &it.input, &it.out),
535 Instruction::Expm1(it) => Expm1::format(f, &it.input, &it.out),
536 Instruction::Cos(it) => Cos::format(f, &it.input, &it.out),
537 Instruction::FastCos(it) => FastCos::format(f, &it.input, &it.out),
538 Instruction::Sin(it) => Sin::format(f, &it.input, &it.out),
539 Instruction::Tan(it) => Tan::format(f, &it.input, &it.out),
540 Instruction::Tanh(it) => Tanh::format(f, &it.input, &it.out),
541 Instruction::Sinh(it) => Sinh::format(f, &it.input, &it.out),
542 Instruction::Cosh(it) => Cosh::format(f, &it.input, &it.out),
543 Instruction::ArcCos(it) => ArcCos::format(f, &it.input, &it.out),
544 Instruction::ArcSin(it) => ArcSin::format(f, &it.input, &it.out),
545 Instruction::ArcTan(it) => ArcTan::format(f, &it.input, &it.out),
546 Instruction::ArcSinh(it) => ArcSinh::format(f, &it.input, &it.out),
547 Instruction::ArcCosh(it) => ArcCosh::format(f, &it.input, &it.out),
548 Instruction::ArcTanh(it) => ArcTanh::format(f, &it.input, &it.out),
549 Instruction::Degrees(it) => Degrees::format(f, &it.input, &it.out),
550 Instruction::Radians(it) => Radians::format(f, &it.input, &it.out),
551 Instruction::ArcTan2(it) => ArcTan2::format(f, &it.lhs, &it.rhs, &it.out),
552 Instruction::FastSin(it) => FastSin::format(f, &it.input, &it.out),
553 Instruction::FastTanh(it) => FastTanh::format(f, &it.input, &it.out),
554 Instruction::Powf(it) => Powf::format(f, &it.lhs, &it.rhs, &it.out),
555 Instruction::FastPowf(it) => FastPowf::format(f, &it.lhs, &it.rhs, &it.out),
556 Instruction::Powi(it) => Powi::format(f, &it.lhs, &it.rhs, &it.out),
557 Instruction::Hypot(it) => Hypot::format(f, &it.lhs, &it.rhs, &it.out),
558 Instruction::Rhypot(it) => Rhypot::format(f, &it.lhs, &it.rhs, &it.out),
559 Instruction::Sqrt(it) => Sqrt::format(f, &it.input, &it.out),
560 Instruction::FastSqrt(it) => FastSqrt::format(f, &it.input, &it.out),
561 Instruction::InverseSqrt(it) => InverseSqrt::format(f, &it.input, &it.out),
562 Instruction::FastInverseSqrt(it) => FastInverseSqrt::format(f, &it.input, &it.out),
563 Instruction::Max(it) => Max::format(f, &it.lhs, &it.rhs, &it.out),
564 Instruction::Min(it) => Min::format(f, &it.lhs, &it.rhs, &it.out),
565 Instruction::Not(it) => Not::format(f, &it.input, &it.out),
566 Instruction::BitwiseNot(it) => BitwiseNot::format(f, &it.input, &it.out),
567 Instruction::Or(it) => Or::format(f, &it.lhs, &it.rhs, &it.out),
568 Instruction::And(it) => And::format(f, &it.lhs, &it.rhs, &it.out),
569 Instruction::Clamp {
570 input,
571 min_value,
572 max_value,
573 out,
574 } => Clamp::format(f, input, min_value, max_value, out),
575 Instruction::IsNan(it) => IsNan::format(f, &it.input, &it.out),
576 Instruction::IsInf(it) => IsInf::format(f, &it.input, &it.out),
577 Instruction::SyncThreads => D::compile_instruction_sync_threads(f),
578 Instruction::SyncWarp => D::compile_instruction_sync_warp(f),
579 Instruction::ThreadFence => f.write_str("__threadfence();\n"),
580 Instruction::Round(it) => Round::format(f, &it.input, &it.out),
581 Instruction::Ceil(it) => Ceil::format(f, &it.input, &it.out),
582 Instruction::Trunc(it) => Trunc::format(f, &it.input, &it.out),
583 Instruction::Floor(it) => Floor::format(f, &it.input, &it.out),
584 Instruction::SliceLength { input, out } => {
585 let out = out.fmt_left();
586 writeln!(f, "{out} = {input}_length;")
587 }
588 Instruction::ConstLength { length, out } => {
589 let out = out.fmt_left();
590 writeln!(f, "{out} = {length};")
591 }
592 Instruction::Warp(it) => write!(f, "{it}"),
593 Instruction::Fma { a, b, c, out } => Fma::format(f, a, b, c, out),
594 Instruction::Wmma(it) => write!(f, "{it}"),
595 Instruction::Bitcast(UnaryInstruction { input, out }) => {
596 let qualifier = out.const_qualifier();
597 let input_item = input.item();
598 let out_item = out.item();
599
600 if out_item.size() != input_item.size() {
601 panic!("Unsupported type for bitcasting {out_item:?} from {input_item:?}");
602 } else {
603 let out = out.fmt_left();
604 let addr_space = D::address_space_for_value(input);
605 let input = match input {
606 Value::Constant(..) => input.ensure_lvalue(f)?,
607 _ => *input,
608 };
609 writeln!(
610 f,
611 "{out} = reinterpret_cast<{addr_space}{out_item}{qualifier}&>({input});"
612 )
613 }
614 }
615 Instruction::AtomicAdd(BinaryInstruction { lhs, rhs, out }) => {
616 D::compile_atomic_add(f, lhs, rhs, out)
617 }
618 Instruction::AtomicAnd(BinaryInstruction { lhs, rhs, out }) => {
619 D::compile_atomic_and(f, lhs, rhs, out)
620 }
621 Instruction::AtomicCAS {
622 input,
623 cmp,
624 val,
625 out,
626 } => D::compile_atomic_cas(f, input, cmp, val, out),
627 Instruction::AtomicLoad(UnaryInstruction { input, out }) => {
628 D::compile_atomic_load(f, input, out)
629 }
630 Instruction::AtomicMax(BinaryInstruction { lhs, rhs, out }) => {
631 D::compile_atomic_max(f, lhs, rhs, out)
632 }
633 Instruction::AtomicMin(BinaryInstruction { lhs, rhs, out }) => {
634 D::compile_atomic_min(f, lhs, rhs, out)
635 }
636 Instruction::AtomicOr(BinaryInstruction { lhs, rhs, out }) => {
637 D::compile_atomic_or(f, lhs, rhs, out)
638 }
639 Instruction::AtomicStore(UnaryInstruction { input, out }) => {
640 D::compile_atomic_store(f, input, out)
641 }
642 Instruction::AtomicSub(BinaryInstruction { lhs, rhs, out }) => {
643 D::compile_atomic_sub(f, lhs, rhs, out)
644 }
645 Instruction::AtomicSwap(BinaryInstruction { lhs, rhs, out }) => {
646 D::compile_atomic_swap(f, lhs, rhs, out)
647 }
648 Instruction::AtomicXor(BinaryInstruction { lhs, rhs, out }) => {
649 D::compile_atomic_xor(f, lhs, rhs, out)
650 }
651 Instruction::Rem(inst) => Remainder::format(f, &inst.lhs, &inst.rhs, &inst.out),
652 Instruction::Neg(UnaryInstruction { input, out }) => Neg::format(f, input, out),
653 Instruction::Normalize(inst) => {
654 Normalize::<D, InverseSqrt>::format(f, &inst.input, &inst.out)
655 }
656 Instruction::FastNormalize(inst) => {
657 Normalize::<D, FastInverseSqrt>::format(f, &inst.input, &inst.out)
658 }
659 Instruction::Magnitude(inst) => Magnitude::<D, Sqrt>::format(f, &inst.input, &inst.out),
660 Instruction::FastMagnitude(inst) => {
661 Magnitude::<D, FastSqrt>::format(f, &inst.input, &inst.out)
662 }
663 Instruction::Dot(inst) => Dot::format(f, &inst.lhs, &inst.rhs, &inst.out),
664 Instruction::VectorSum(inst) => VectorSumFmt::<D>::format(f, &inst.input, &inst.out),
665 Instruction::VecInit { inputs, out } => {
666 let item = out.item();
667 let inputs = inputs
668 .iter()
669 .map(|input| format!("{input}"))
670 .collect::<Vec<_>>();
671 let out = out.fmt_left();
672 writeln!(f, "{out} = {item}{{{}}};", inputs.join(","))
673 }
674 Instruction::InsertComponent {
675 vector,
676 index,
677 value,
678 out,
679 } => InsertComponent::format(f, vector, index, value, out),
680 Instruction::ExtractComponent(inst) => {
681 ExtractComponent::format(f, &inst.lhs, &inst.rhs, &inst.out)
682 }
683 Instruction::Printf {
684 format_string,
685 args,
686 } => D::compile_instruction_printf(f, format_string, args),
687 Instruction::Comment { content } => {
688 if content.contains('\n') {
689 writeln!(f, "/* {content} */")
690 } else {
691 writeln!(f, "// {content}")
692 }
693 }
694 Instruction::Barrier(barrier_ops) => write!(f, "{barrier_ops}"),
695 Instruction::Line { file, line } => writeln!(f, "#line {line} \"{file}\""),
696 Instruction::ProxyAsyncToSharedFence => {
697 writeln!(
698 f,
699 "cuda::device::experimental::fence_proxy_async_shared_cta();"
700 )
701 }
702 Instruction::BulkCommitGroup => writeln!(
703 f,
704 "cuda::device::experimental::cp_async_bulk_commit_group();"
705 ),
706 Instruction::BulkWaitGroup { max_pending } => writeln!(
707 f,
708 "cuda::device::experimental::cp_async_bulk_wait_group<{max_pending}>();"
709 ),
710 Instruction::BulkWaitGroupRead { max_pending } => writeln!(
711 f,
712 "cuda::device::experimental::cp_async_bulk_wait_group_read<{max_pending}>();"
713 ),
714 Instruction::TmaReplacePointer {
715 buffer,
716 offset,
717 tensor_map,
718 out,
719 } => {
720 let pos = Builtin::<D>::UnitPos;
721 writeln!(f, "__shared__ alignas(128) CUtensorMap {out};")?;
722 writeln!(
723 f,
724 "
725if({pos} == 0) {{
726 {out} = {tensor_map};
727 tensormap_replace_global_address({out}, &{buffer}[{offset}]);
728}}"
729 )?;
730 writeln!(f, "__syncthreads();")
731 }
732 Instruction::MemCopyAsyncTensorSharedToGlobal {
733 smem_buffer,
734 tensor_map,
735 indices,
736 } => {
737 let rank = indices.len();
738 let smem_ptr = smem_buffer.fmt_ptr();
739 let indices = indices.iter().rev().fold(String::new(), |mut s, it| {
740 let _ = write!(s, "{it}, ");
741 s
742 });
743 writeln!(
744 f,
745 "cuda::device::experimental::cp_async_bulk_tensor_{rank}d_shared_to_global(&{tensor_map}, {indices} {smem_ptr});"
746 )
747 }
748 Instruction::SpecialCast(UnaryInstruction { input, out }) => {
749 #[cfg(not(feature = "cuda"))]
751 {
752 let _ = (input, out);
753 writeln!(
754 f,
755 "#error FP8/FP6/FP4 casting isn't supported outside of CUDA"
756 )
757 }
758 #[cfg(feature = "cuda")]
759 crate::cuda::convert::special_cast::<D>(f, input, out)
760 }
761 Instruction::ReadBuiltin { builtin, out } => {
762 writeln!(f, "{} = {builtin};", out.fmt_left())
763 }
764 Instruction::ReadScalar { id, out } => {
765 let elem = *out.item().elem();
766 writeln!(f, "{} = info.scalars_{elem}[{id}];", out.fmt_left())
767 }
768 }
769 }
770}
771
772struct Fma<D: Dialect> {
773 _dialect: PhantomData<D>,
774}
775
776impl<D: Dialect> Fma<D> {
777 fn format(
778 f: &mut core::fmt::Formatter<'_>,
779 a: &Value<D>,
780 b: &Value<D>,
781 c: &Value<D>,
782 out: &Value<D>,
783 ) -> core::fmt::Result {
784 let out_item = out.item();
785
786 let out = out.fmt_left();
787 if let Item::Vector(_, num) = out_item {
788 writeln!(f, "{out} = {out_item}{{")?;
789
790 for i in 0..num {
791 let ai = a.index(i);
792 let bi = b.index(i);
793 let ci = c.index(i);
794
795 writeln!(f, "fma({ai}, {bi}, {ci}),")?;
796 }
797 f.write_str("};\n")
798 } else {
799 writeln!(f, "{out} = fma({a}, {b}, {c});")
800 }
801 }
802}
803
804struct Clamp<D: Dialect> {
805 _dialect: PhantomData<D>,
806}
807
808impl<D: Dialect> Clamp<D> {
809 fn format(
810 f: &mut core::fmt::Formatter<'_>,
811 input: &Value<D>,
812 min_value: &Value<D>,
813 max_value: &Value<D>,
814 out: &Value<D>,
815 ) -> core::fmt::Result {
816 let out_item = out.item();
817 if let Item::Vector(..) = out_item {
818 Self::unroll_vec(f, input, min_value, max_value, out)
819 } else {
820 let out = out.fmt_left();
821 write!(f, "{out} = ")?;
822 Self::format_scalar(f, *input, *min_value, *max_value, out_item)?;
823 f.write_str(";\n")
824 }
825 }
826
827 fn format_scalar(
828 f: &mut Formatter<'_>,
829 input: impl Component<D>,
830 min_value: impl Component<D>,
831 max_value: impl Component<D>,
832 item: Item<D>,
833 ) -> std::fmt::Result {
834 D::compile_instruction_max_function_name(f, item)?;
835 write!(f, "({min_value}, ")?;
836 D::compile_instruction_min_function_name(f, item)?;
837 write!(f, "({max_value}, {input}))")
838 }
839
840 fn unroll_vec(
841 f: &mut core::fmt::Formatter<'_>,
842 input: &Value<D>,
843 min_value: &Value<D>,
844 max_value: &Value<D>,
845 out: &Value<D>,
846 ) -> std::fmt::Result {
847 let optimized = Value::optimized_args([*input, *min_value, *max_value, *out]);
848 let [input, min_value, max_value, out_optimized] = optimized.args;
849
850 let item_out_original = out.item();
851 let item_out_optimized = out_optimized.item();
852
853 let index = match item_out_optimized {
854 Item::Vector(_, index) => index,
855 _ => 1,
856 };
857
858 let mut write_op = |input: &Value<D>,
859 min_value: &Value<D>,
860 max_value: &Value<D>,
861 out: &Value<D>,
862 item_out: Item<D>| {
863 let out = out.fmt_left();
864 writeln!(f, "{out} = {item_out}{{")?;
865 for i in 0..index {
866 let inputi = input.index(i);
867 let min_valuei = min_value.index(i);
868 let max_valuei = max_value.index(i);
869
870 Self::format_scalar(f, inputi, min_valuei, max_valuei, item_out)?;
871 f.write_str(", ")?;
872 }
873
874 f.write_str("};\n")
875 };
876
877 if item_out_original == item_out_optimized {
878 write_op(&input, &min_value, &max_value, out, item_out_optimized)
879 } else {
880 let out_tmp = Value::tmp(item_out_optimized);
881 write_op(&input, &min_value, &max_value, &out_tmp, item_out_optimized)?;
882 let addr_space = D::address_space_for_value(out);
883 let out = out.fmt_left();
884
885 writeln!(
886 f,
887 "{out} = reinterpret_cast<{addr_space}{item_out_original}&>({out_tmp});\n"
888 )?;
889
890 Ok(())
891 }
892 }
893}
894
895struct Magnitude<D: Dialect, S: FunctionFmt<D>> {
896 _dialect: PhantomData<D>,
897 _sqrt: PhantomData<S>,
898}
899
900impl<D: Dialect, S: FunctionFmt<D>> Magnitude<D, S> {
901 fn format(
902 f: &mut core::fmt::Formatter<'_>,
903 input: &Value<D>,
904 out: &Value<D>,
905 ) -> core::fmt::Result {
906 let num = match input.item() {
907 Item::Vector(_, vectorization) => vectorization,
908 _ => 1,
909 };
910 let elem = input.elem();
911
912 let mag = format!("{out}_mag");
913
914 writeln!(f, "{} {mag} = {}(0.0);", out.item(), out.item())?;
916
917 for i in 0..num {
918 let input_i = input.index(i);
919 writeln!(f, "{mag} += {input_i} * {input_i};")?;
920 }
921
922 let out = out.fmt_left();
923 write!(f, "{out} = ")?;
924 S::format_unary(f, &mag, elem)?;
925 f.write_str(";\n")
926 }
927}
928
929struct Normalize<D: Dialect, InvS: FunctionFmt<D>> {
930 _dialect: PhantomData<D>,
931 _rsqrt: PhantomData<InvS>,
932}
933
934impl<D: Dialect, InvS: FunctionFmt<D>> Normalize<D, InvS> {
935 fn format(
936 f: &mut core::fmt::Formatter<'_>,
937 input: &Value<D>,
938 out: &Value<D>,
939 ) -> core::fmt::Result {
940 let num = match input.item() {
941 Item::Vector(_, vectorization) => vectorization,
942 _ => 1,
943 };
944 let elem = input.elem();
945 let norm = format!("{out}_norm");
946
947 let out_item = out.item();
948 let out = out.fmt_left();
949 writeln!(f, "{elem} {norm} = {elem}(0.0);")?;
951
952 for i in 0..num {
953 let input_i = input.index(i);
954 writeln!(f, "{norm} += {input_i} * {input_i};")?;
955 }
956
957 write!(f, "{norm} = ")?;
958 InvS::format_unary(f, &norm, elem)?;
959 f.write_str(";\n")?;
960
961 if num == 1 {
962 writeln!(f, "{out} = {input} * {norm};")
963 } else {
964 write!(f, "{out} = {out_item}{{")?;
965 for i in 0..num {
966 let input_i = input.index(i);
967
968 writeln!(f, "{input_i} * {norm},")?;
969 }
970
971 f.write_str("};\n")
972 }
973 }
974}
975
976struct Dot<D: Dialect> {
977 _dialect: PhantomData<D>,
978}
979
980impl<D: Dialect> Dot<D> {
981 fn format(
982 f: &mut core::fmt::Formatter<'_>,
983 lhs: &Value<D>,
984 rhs: &Value<D>,
985 out: &Value<D>,
986 ) -> core::fmt::Result {
987 let num = match lhs.item() {
988 Item::Vector(_, vectorization) => vectorization,
989 _ => 1,
990 };
991
992 let muls = (0..num)
993 .map(|i| {
994 let lhs_i = lhs.index(i);
995 let rhs_i = rhs.index(i);
996 format!("{lhs_i} * {rhs_i}")
997 })
998 .collect::<Vec<_>>();
999
1000 let value = muls.join(" + ");
1001 if out.declare_local_ptr_backing(f)? {
1002 writeln!(f, "*{out} = {value};")
1003 } else {
1004 writeln!(f, "{} = {value};", out.fmt_left())
1005 }
1006 }
1007}
1008
1009struct VectorSumFmt<D: Dialect> {
1010 _dialect: PhantomData<D>,
1011}
1012
1013impl<D: Dialect> VectorSumFmt<D> {
1014 fn format(
1015 f: &mut core::fmt::Formatter<'_>,
1016 input: &Value<D>,
1017 out: &Value<D>,
1018 ) -> core::fmt::Result {
1019 let num = input.item().vectorization();
1020
1021 let elems = (0..num)
1022 .map(|i| format!("{}", input.index(i)))
1023 .collect::<Vec<_>>();
1024
1025 let value = elems.join(" + ");
1026 if out.declare_local_ptr_backing(f)? {
1027 writeln!(f, "*{out} = {value};")
1028 } else {
1029 writeln!(f, "{} = {value};", out.fmt_left())
1030 }
1031 }
1032}
1033
1034struct EnsureBoolArg<'a, V: Display, D: Dialect> {
1035 val: &'a V,
1036 elem: &'a Elem<D>,
1037}
1038
1039impl<V: Display, D: Dialect> Display for EnsureBoolArg<'_, V, D> {
1040 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1041 if self.elem != &Elem::Bool {
1042 write!(f, "bool({})", self.val)
1043 } else {
1044 write!(f, "{}", self.val)
1045 }
1046 }
1047}
1048
1049#[cfg(all(test, feature = "metal"))]
1050mod tests {
1051 use cubecl_core::ir::ConstantValue;
1052
1053 use crate::metal::MslDialect;
1054
1055 use super::*;
1056
1057 #[test]
1058 fn bitcast_constant_uses_lvalue() {
1059 let instruction = Instruction::<MslDialect>::Bitcast(UnaryInstruction {
1060 input: Value::Constant(ConstantValue::UInt(0x7f80_0000), Item::Scalar(Elem::U32)),
1061 out: Value::Value {
1062 id: 0,
1063 item: Item::Scalar(Elem::F32),
1064 },
1065 });
1066
1067 let source = instruction.to_string();
1068
1069 assert!(source.contains("uint _tmp_"), "{source}");
1070 assert!(
1071 source.contains("reinterpret_cast<thread float const&>(_tmp_"),
1072 "{source}"
1073 );
1074 }
1075}