1#![allow(unused)]
3
4use core::fmt;
5
6use crate::{
7 Dialect,
8 shared::{Component, Elem, FP8Kind, FmtLeft, Instruction, Item, UnaryInstruction, Value},
9};
10
11pub(crate) fn special_cast<D: Dialect>(
35 f: &mut std::fmt::Formatter,
36 input: &Value<D>,
37 out: &Value<D>,
38) -> fmt::Result {
39 let mut current_in = *input;
40
41 if matches!(
42 input.elem().unpacked(),
43 Elem::FP4(_) | Elem::FP6(_) | Elem::FP8(_)
44 ) {
45 let item = out.item().with_elem(match input.elem().unpacked() {
46 Elem::FP8(FP8Kind::UE8M0) => Elem::BF16,
47 _ => Elem::F16,
48 });
49 let out_var = if item == out.item() {
50 *out
51 } else {
52 Value::tmp(item)
53 };
54 if *item.elem() == Elem::F16 {
55 cast_minifloat_to_half(f, current_in, out_var)?;
56 } else {
57 cast_scale_to_bfloat(f, current_in, out_var)?;
58 }
59 current_in = out_var;
60 }
61
62 let in_vec = match current_in.item() {
63 Item::Scalar(_) => 1,
64 Item::Vector(_, vectorization) | Item::NativeVector(_, vectorization) => vectorization,
65 _ => panic!("Invalid input item for special cast"),
66 };
67
68 if out.item().packing_factor() > 1 && in_vec == 1 {
70 let tmp = Value::tmp(Item::new(input.elem(), out.item().packing_factor()));
71 let assign = Instruction::Assign(UnaryInstruction {
72 input: current_in,
73 out: tmp,
74 });
75 writeln!(f, "{assign}")?;
76 current_in = tmp;
77 }
78
79 let in_vec = match current_in.item() {
80 Item::Scalar(_) => 1,
81 Item::Vector(_, vectorization) | Item::NativeVector(_, vectorization) => vectorization,
82 _ => panic!("Invalid input item for special cast"),
83 };
84
85 if matches!(
86 current_in.elem(),
87 Elem::U8
88 | Elem::U16
89 | Elem::U32
90 | Elem::U64
91 | Elem::I8
92 | Elem::I16
93 | Elem::I32
94 | Elem::I64
95 | Elem::Bool
96 ) {
97 let tmp = Value::tmp(Item::new(Elem::BF16, in_vec));
99 let assign = Instruction::Assign(UnaryInstruction {
100 input: current_in,
101 out: tmp,
102 });
103 writeln!(f, "{assign}")?;
104 current_in = tmp;
105 }
106
107 if matches!(out.elem().unpacked(), Elem::FP4(_) | Elem::FP6(_)) {
108 return cast_to_fp4_fp6(f, current_in, *out);
109 }
110
111 if matches!(out.elem().unpacked(), Elem::FP8(FP8Kind::UE8M0)) {
112 if matches!(current_in.elem(), Elem::F16) {
114 let item = current_in.item().with_elem(Elem::BF16);
115 let tmp = Value::tmp(item);
116 let assign = Instruction::Assign(UnaryInstruction {
117 input: current_in,
118 out: tmp,
119 });
120 writeln!(f, "{assign}")?;
121 current_in = tmp;
122 }
123 return cast_to_scale(f, current_in, *out);
124 }
125
126 if matches!(out.elem().unpacked(), Elem::FP8(_)) {
127 return cast_to_fp8(f, current_in, *out);
128 }
129
130 if current_in.item() != out.item() {
131 let assign = Instruction::Assign(UnaryInstruction {
132 input: current_in,
133 out: *out,
134 });
135 writeln!(f, "{assign}")?;
136 }
137
138 Ok(())
139}
140
141fn cast_to_fp4_fp6<D: Dialect>(
143 f: &mut fmt::Formatter,
144 input: Value<D>,
145 out: Value<D>,
146) -> fmt::Result {
147 let out_opt = out.optimized();
148 let packing = out_opt.item().packing_factor();
149 let packed = packing == 2;
150 let pack_suffix = if packed { "2" } else { "" };
151
152 let (out_ty, interpretation) = match out_opt.elem() {
153 Elem::FP4(kind) => ("fp4", format!("{kind:?}")),
154 Elem::FP4x2(kind) => ("fp4x2", format!("{kind:?}")),
155 Elem::FP6(kind) => ("fp6", format!("{kind:?}")),
156 Elem::FP6x2(kind) => ("fp6x2", format!("{kind:?}")),
157 _ => unreachable!("Must be fp4 or fp6"),
158 };
159
160 let in_ty = match input.elem().unpacked() {
161 Elem::F64 => format!("double{pack_suffix}"),
162 Elem::TF32 | Elem::F32 => format!("float{pack_suffix}"),
163 Elem::F16 => format!("halfraw{pack_suffix}"),
164 Elem::BF16 => format!("bfloat16raw{pack_suffix}"),
165 _ => unreachable!(),
166 };
167
168 let input = input.optimized();
169
170 handle_unroll(f, out, |f, i| {
171 let in_value = float_to_packed(input, i, packing);
172
173 write!(
174 f,
175 "__nv_cvt_{in_ty}_to_{out_ty}({in_value}, __NV_{interpretation}, cudaRoundNearest)",
176 )
177 })
178}
179
180fn cast_to_scale<D: Dialect>(
182 f: &mut fmt::Formatter,
183 input: Value<D>,
184 out: Value<D>,
185) -> fmt::Result {
186 let out_opt = out.optimized();
187 let packing = out_opt.item().packing_factor();
188 let packed = packing > 1;
189 let pack_suffix = if packed { "2" } else { "" };
190
191 let out_ty = match out_opt.elem() {
192 Elem::FP8(_) => "e8m0",
193 Elem::FP8x2(_) => "e8m0x2",
194 _ => unreachable!("Must be scale factor"),
195 };
196
197 let in_ty = match input.elem() {
198 Elem::F64 => format!("double{pack_suffix}"),
199 Elem::TF32 | Elem::F32 => format!("float{pack_suffix}"),
200 Elem::BF16 => format!("bfloat16{pack_suffix}raw"),
201 _ => unreachable!(),
202 };
203
204 let input = input.optimized();
205
206 handle_unroll(f, out, |f, i| {
207 let in_value = float_to_packed(input, i, packing);
208
209 write!(
210 f,
211 "__nv_cvt_{in_ty}_to_{out_ty}({in_value}, __NV_NOSAT, cudaRoundPosInf)",
212 )
213 })
214}
215
216fn cast_to_fp8<D: Dialect>(f: &mut fmt::Formatter, input: Value<D>, out: Value<D>) -> fmt::Result {
218 let out_opt = out.optimized();
219 let packing = out_opt.item().packing_factor();
220 let packed = packing > 1;
221 let pack_suffix = if packed { "2" } else { "" };
222
223 let (out_ty, interpretation) = match out_opt.elem() {
224 Elem::FP8(kind) => ("fp8", format!("{kind:?}")),
225 Elem::FP8x2(kind) => ("fp8x2", format!("{kind:?}")),
226 _ => unreachable!("Must be fp8"),
227 };
228
229 let in_ty = match input.elem() {
230 Elem::F64 => format!("double{pack_suffix}"),
231 Elem::TF32 | Elem::F32 => format!("float{pack_suffix}"),
232 Elem::BF16 => format!("bfloat16raw{pack_suffix}"),
233 Elem::F16 => format!("halfraw{pack_suffix}"),
234 _ => unreachable!(),
235 };
236
237 let input = input.optimized();
238
239 handle_unroll(f, out, |f, i| {
240 let in_value = float_to_packed(input, i, packing);
241
242 write!(
243 f,
244 "__nv_cvt_{in_ty}_to_{out_ty}({in_value}, __NV_NOSAT, __NV_{interpretation})",
245 )
246 })
247}
248
249fn float_to_packed<D: Dialect>(input: Value<D>, i: usize, packing: usize) -> String {
251 match input.elem() {
252 Elem::TF32 | Elem::F32 => {
253 let i = i * packing;
254 if packing > 1 {
255 format!("float2 {{ {}, {} }}", input.index(i), input.index(i + 1))
256 } else {
257 format!("{}", input.index(i))
258 }
259 }
260 Elem::F64 => {
261 let i = i * packing;
262 if packing > 1 {
263 format!("double2 {{ {}, {} }}", input.index(i), input.index(i + 1))
264 } else {
265 format!("{}", input.index(i))
266 }
267 }
268 Elem::F16 | Elem::F16x2 | Elem::BF16 | Elem::BF16x2 => format!("{}", input.index(i)),
269 _ => unreachable!(),
270 }
271}
272
273fn cast_minifloat_to_half<D: Dialect>(
275 f: &mut fmt::Formatter,
276 input: Value<D>,
277 out: Value<D>,
278) -> fmt::Result {
279 let in_opt = input.optimized();
280 let out_opt = out.optimized().item();
281
282 let (in_ty, interpretation) = match in_opt.elem() {
283 Elem::FP4(kind) => ("fp4", format!("{kind:?}")),
284 Elem::FP4x2(kind) => ("fp4x2", format!("{kind:?}")),
285 Elem::FP6(kind) => ("fp6", format!("{kind:?}")),
286 Elem::FP6x2(kind) => ("fp6x2", format!("{kind:?}")),
287 Elem::FP8(kind) => ("fp8", format!("{kind:?}")),
288 Elem::FP8x2(kind) => ("fp8x2", format!("{kind:?}")),
289 _ => unreachable!("can only cast minifloat"),
290 };
291
292 let out_ty = match out_opt.elem() {
293 Elem::F16 => "halfraw",
294 Elem::F16x2 => "halfraw2",
295 _ => unreachable!("out type must be half"),
296 };
297
298 handle_unroll(f, out, |f, i| {
299 let input = in_opt.index(i);
300 write!(
301 f,
302 "{}(__nv_cvt_{in_ty}_to_{out_ty}({input}, __NV_{interpretation}))",
303 out_opt.elem()
304 )
305 })
306}
307
308fn cast_scale_to_bfloat<D: Dialect>(
310 f: &mut fmt::Formatter,
311 input: Value<D>,
312 out: Value<D>,
313) -> fmt::Result {
314 let in_opt = input.optimized();
315 let out_opt = out.optimized().item();
316
317 let in_ty = match in_opt.elem() {
318 Elem::FP8(_) => "e8m0",
319 Elem::FP8x2(_) => "e8m0x2",
320 _ => unreachable!("must be scaling factor in e8m0 format"),
321 };
322
323 let out_ty = match out_opt.elem() {
324 Elem::BF16 => "bf16raw",
325 Elem::BF16x2 => "bf162raw",
326 _ => unreachable!("out type must be half"),
327 };
328
329 handle_unroll(f, out, |f, i| {
330 let input = in_opt.index(i);
331 write!(
332 f,
333 "{}(__nv_cvt_{in_ty}_to_{out_ty}({input}))",
334 out_opt.elem()
335 )
336 })
337}
338
339fn handle_unroll<D: Dialect>(
340 f: &mut fmt::Formatter,
341 out: Value<D>,
342 mut op: impl FnMut(&mut fmt::Formatter, usize) -> fmt::Result,
343) -> fmt::Result {
344 let out_opt = out.item().optimized();
345 let vec = match out_opt {
346 Item::Scalar(_) => 1,
347 Item::Vector(_, vectorization) | Item::NativeVector(_, vectorization) => vectorization,
348 _ => panic!("Invalid input item for special cast"),
349 };
350 let out_var = if out.item() != out_opt {
351 Value::tmp(out_opt)
352 } else {
353 out
354 };
355 write!(f, "{} = ", out_var.fmt_left())?;
356 if vec > 1 {
357 writeln!(f, "{out_opt} {{")?;
358 }
359 for i in 0..vec {
360 op(f, i)?;
361 if i + 1 < vec {
362 f.write_str(",\n")?;
363 }
364 }
365 if vec > 1 {
366 write!(f, "\n}}")?;
367 }
368 f.write_str(";\n")?;
369
370 if out.item() != out_opt {
371 writeln!(
372 f,
373 "{} = reinterpret_cast<{}&>({out_var});",
374 out.fmt_left(),
375 out.item()
376 )?;
377 }
378 Ok(())
379}