1use cubecl_core::{
2 e2m1, e2m1x2, e4m3, e5m2,
3 ir::{BarrierLevel, ConstantValue, Id},
4 ue8m0,
5};
6use std::fmt::{Display, Formatter};
7
8use crate::shared::{FP4Kind, FP8Kind};
9
10use super::{COUNTER_TMP_VAR, Dialect, Elem, Fragment, FragmentIdent, Item};
11
12pub trait Component<D: Dialect>: Display + FmtLeft {
13 fn item(&self) -> Item<D>;
14 fn is_const(&self) -> bool;
15 fn index(&self, index: usize) -> IndexedVariable<D>;
16 fn elem(&self) -> Elem<D> {
17 *self.item().elem()
18 }
19}
20
21pub trait FmtLeft: Display {
22 fn fmt_left(&self) -> String;
23}
24
25#[derive(new)]
26pub struct OptimizedArgs<const N: usize, D: Dialect> {
27 pub args: [Variable<D>; N],
28 pub optimization_factor: Option<usize>,
29}
30
31#[derive(Debug, Clone, Copy, PartialEq)]
32pub enum Variable<D: Dialect> {
33 AbsolutePos(Elem<D>),
34 AbsolutePosBaseName, AbsolutePosX,
36 AbsolutePosY,
37 AbsolutePosZ,
38 UnitPos,
39 UnitPosBaseName, UnitPosX,
41 UnitPosY,
42 UnitPosZ,
43 CubePos(Elem<D>),
44 CubePosBaseName, CubePosX,
46 CubePosY,
47 CubePosZ,
48 CubeDim,
49 CubeDimBaseName, CubeDimX,
51 CubeDimY,
52 CubeDimZ,
53 CubeCount(Elem<D>),
54 CubeCountBaseName, CubeCountX,
56 CubeCountY,
57 CubeCountZ,
58 PlaneDim,
59 PlaneDimChecked,
60 PlanePos,
61 UnitPosPlane,
62 ClusterRank,
63 ClusterIndexX,
64 ClusterIndexY,
65 ClusterIndexZ,
66 GlobalInputArray(Id, Item<D>),
67 GlobalOutputArray(Id, Item<D>),
68 GlobalScalar {
69 id: Id,
70 elem: Elem<D>,
71 },
72 ConstantArray(Id, Item<D>, usize),
73 Constant(ConstantValue, Item<D>),
74 TensorMap(Id),
75 LocalMut {
76 id: Id,
77 item: Item<D>,
78 },
79 LocalConst {
80 id: Id,
81 item: Item<D>,
82 },
83 Named {
84 name: &'static str,
85 item: Item<D>,
86 },
87 Slice {
88 id: Id,
89 item: Item<D>,
90 },
91 SharedArray(Id, Item<D>, usize),
92 Shared(Id, Item<D>),
93 LocalArray(Id, Item<D>, usize),
94 WmmaFragment {
95 id: Id,
96 frag: Fragment<D>,
97 },
98 Pipeline {
99 id: Id,
100 },
101 Barrier {
102 id: Id,
103 level: BarrierLevel,
104 },
105 BarrierToken {
106 id: Id,
107 level: BarrierLevel,
108 },
109 Tmp {
110 id: Id,
111 item: Item<D>,
112 is_declared: bool,
113 is_ptr: bool,
114 is_const: bool,
115 },
116}
117
118impl<D: Dialect> Component<D> for Variable<D> {
119 fn index(&self, index: usize) -> IndexedVariable<D> {
120 self.index(index)
121 }
122
123 fn item(&self) -> Item<D> {
124 match self {
125 Variable::AbsolutePos(elem) => Item::scalar(*elem, true),
126 Variable::AbsolutePosBaseName => Item {
127 elem: Elem::U32,
128 vectorization: 3,
129 native: true,
130 },
131 Variable::AbsolutePosX => Item::scalar(Elem::U32, true),
132 Variable::AbsolutePosY => Item::scalar(Elem::U32, true),
133 Variable::AbsolutePosZ => Item::scalar(Elem::U32, true),
134 Variable::CubeCount(elem) => Item::scalar(*elem, true),
135 Variable::CubeCountBaseName => Item {
136 elem: Elem::U32,
137 vectorization: 3,
138 native: true,
139 },
140 Variable::CubeCountX => Item::scalar(Elem::U32, true),
141 Variable::CubeCountY => Item::scalar(Elem::U32, true),
142 Variable::CubeCountZ => Item::scalar(Elem::U32, true),
143 Variable::CubeDimBaseName => Item {
144 elem: Elem::U32,
145 vectorization: 3,
146 native: true,
147 },
148 Variable::CubeDim => Item::scalar(Elem::U32, true),
149 Variable::CubeDimX => Item::scalar(Elem::U32, true),
150 Variable::CubeDimY => Item::scalar(Elem::U32, true),
151 Variable::CubeDimZ => Item::scalar(Elem::U32, true),
152 Variable::CubePos(elem) => Item::scalar(*elem, true),
153 Variable::CubePosBaseName => Item {
154 elem: Elem::U32,
155 vectorization: 3,
156 native: true,
157 },
158 Variable::CubePosX => Item::scalar(Elem::U32, true),
159 Variable::CubePosY => Item::scalar(Elem::U32, true),
160 Variable::CubePosZ => Item::scalar(Elem::U32, true),
161 Variable::UnitPos => Item::scalar(Elem::U32, true),
162 Variable::UnitPosBaseName => Item {
163 elem: Elem::U32,
164 vectorization: 3,
165 native: true,
166 },
167 Variable::UnitPosX => Item::scalar(Elem::U32, true),
168 Variable::UnitPosY => Item::scalar(Elem::U32, true),
169 Variable::UnitPosZ => Item::scalar(Elem::U32, true),
170 Variable::PlaneDim => Item::scalar(Elem::U32, true),
171 Variable::PlaneDimChecked => Item::scalar(Elem::U32, true),
172 Variable::PlanePos => Item::scalar(Elem::U32, true),
173 Variable::UnitPosPlane => Item::scalar(Elem::U32, true),
174 Variable::ClusterRank => Item::scalar(Elem::U32, true),
175 Variable::ClusterIndexX => Item::scalar(Elem::U32, true),
176 Variable::ClusterIndexY => Item::scalar(Elem::U32, true),
177 Variable::ClusterIndexZ => Item::scalar(Elem::U32, true),
178 Variable::GlobalInputArray(_, e) => *e,
179 Variable::GlobalOutputArray(_, e) => *e,
180 Variable::LocalArray(_, e, _) => *e,
181 Variable::SharedArray(_, e, _) => *e,
182 Variable::Shared(_, e) => *e,
183 Variable::ConstantArray(_, e, _) => *e,
184 Variable::LocalMut { item, .. } => *item,
185 Variable::LocalConst { item, .. } => *item,
186 Variable::Named { item, .. } => *item,
187 Variable::Slice { item, .. } => *item,
188 Variable::Constant(_, e) => *e,
189 Variable::GlobalScalar { elem, .. } => Item::scalar(*elem, false),
190 Variable::WmmaFragment { frag, .. } => Item::scalar(frag.elem, false),
191 Variable::Tmp { item, .. } => *item,
192 Variable::Pipeline { .. }
193 | Variable::Barrier { .. }
194 | Variable::BarrierToken { .. } => Item::new(Elem::Bool, 1, false),
195 Variable::TensorMap(_) => unreachable!(),
196 }
197 }
198
199 fn is_const(&self) -> bool {
200 if let Variable::Tmp { is_const, .. } = self {
201 return *is_const;
202 }
203
204 matches!(
205 self,
206 Variable::LocalConst { .. } | Variable::GlobalInputArray { .. }
207 )
208 }
209}
210
211pub(crate) fn format_const<D: Dialect>(number: &ConstantValue, item: &Item<D>) -> String {
212 let number = match item.elem() {
214 Elem::FP4(FP4Kind::E2M1) => e2m1::from_f64(number.as_f64()).to_bits(),
215 Elem::FP4x2(FP4Kind::E2M1) => {
216 let v = number.as_f64() as f32;
217 let value = [v, v];
218 e2m1x2::from_f32_slice(&value).remove(0).to_bits()
219 }
220 Elem::FP6(_) | Elem::FP6x2(_) => {
221 todo!("FP6 constants are not yet supported")
222 }
223 Elem::FP8(FP8Kind::E4M3) => e4m3::from_f64(number.as_f64()).to_bits(),
224 Elem::FP8(FP8Kind::E5M2) => e5m2::from_f64(number.as_f64()).to_bits(),
225 Elem::FP8(FP8Kind::UE8M0) => ue8m0::from_f64(number.as_f64()).to_bits(),
226 _ => {
227 return format!("{number}");
228 }
229 };
230 format!("{number}")
231}
232
233impl<D: Dialect> Display for Variable<D> {
234 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
235 match self {
236 Variable::GlobalInputArray(id, _) => f.write_fmt(format_args!("buffer_{id}")),
237 Variable::GlobalOutputArray(id, _) => write!(f, "buffer_{id}"),
238 Variable::TensorMap(id) => write!(f, "tensor_map_{id}"),
239 Variable::LocalMut { id, .. } => f.write_fmt(format_args!("l_mut_{id}")),
240 Variable::LocalConst { id, .. } => f.write_fmt(format_args!("l_{id}")),
241 Variable::Named { name, .. } => f.write_fmt(format_args!("{name}")),
242 Variable::Slice { id, .. } => {
243 write!(f, "slice_{id}")
244 }
245 Variable::GlobalScalar { id, elem } => write!(f, "info.scalars_{elem}[{id}]"),
246 Variable::Constant(number, item) if item.vectorization <= 1 => {
247 let value = format_const(number, item);
248 write!(f, "{item}({value})")
249 }
250 Variable::Constant(number, item) => {
251 let number = format_const(number, item);
252 let values = (0..item.vectorization)
253 .map(|_| format!("{}({number})", item.elem()))
254 .collect::<Vec<_>>();
255 write!(f, "{item} {{ {} }}", values.join(","))
256 }
257 Variable::SharedArray(number, _, _) | Variable::Shared(number, _) => {
258 write!(f, "shared_memory_{number}")
259 }
260
261 Variable::AbsolutePos(_) => D::compile_absolute_pos(f),
262 Variable::AbsolutePosBaseName => D::compile_absolute_pos_base_name(f),
263 Variable::AbsolutePosX => D::compile_absolute_pos_x(f),
264 Variable::AbsolutePosY => D::compile_absolute_pos_y(f),
265 Variable::AbsolutePosZ => D::compile_absolute_pos_z(f),
266 Variable::CubeCount(_) => D::compile_cube_count(f),
267 Variable::CubeCountBaseName => D::compile_cube_count_base_name(f),
268 Variable::CubeCountX => D::compile_cube_count_x(f),
269 Variable::CubeCountY => D::compile_cube_count_y(f),
270 Variable::CubeCountZ => D::compile_cube_count_z(f),
271 Variable::CubeDim => D::compile_cube_dim(f),
272 Variable::CubeDimBaseName => D::compile_cube_dim_base_name(f),
273 Variable::CubeDimX => D::compile_cube_dim_x(f),
274 Variable::CubeDimY => D::compile_cube_dim_y(f),
275 Variable::CubeDimZ => D::compile_cube_dim_z(f),
276 Variable::CubePos(_) => D::compile_cube_pos(f),
277 Variable::CubePosBaseName => D::compile_cube_pos_base_name(f),
278 Variable::CubePosX => D::compile_cube_pos_x(f),
279 Variable::CubePosY => D::compile_cube_pos_y(f),
280 Variable::CubePosZ => D::compile_cube_pos_z(f),
281 Variable::UnitPos => D::compile_unit_pos(f),
282 Variable::UnitPosBaseName => D::compile_unit_pos_base_name(f),
283 Variable::UnitPosX => D::compile_unit_pos_x(f),
284 Variable::UnitPosY => D::compile_unit_pos_y(f),
285 Variable::UnitPosZ => D::compile_unit_pos_z(f),
286 Variable::PlaneDim => D::compile_plane_dim(f),
287 Variable::PlaneDimChecked => D::compile_plane_dim_checked(f),
288 Variable::PlanePos => D::compile_plane_pos(f),
289 Variable::UnitPosPlane => D::compile_unit_pos_plane(f),
290 Variable::ClusterRank => D::compile_cluster_pos(f),
291 Variable::ClusterIndexX => D::compile_cluster_pos_x(f),
292 Variable::ClusterIndexY => D::compile_cluster_pos_y(f),
293 Variable::ClusterIndexZ => D::compile_cluster_pos_z(f),
294
295 Variable::ConstantArray(number, _, _) => f.write_fmt(format_args!("arrays_{number}")),
296 Variable::LocalArray(id, _, _) => {
297 write!(f, "l_arr_{id}")
298 }
299 Variable::WmmaFragment { id: index, frag } => {
300 let name = match frag.ident {
301 FragmentIdent::A => "a",
302 FragmentIdent::B => "b",
303 FragmentIdent::Accumulator => "acc",
304 FragmentIdent::_Dialect(_) => "",
305 };
306 write!(f, "frag_{name}_{index}")
307 }
308 Variable::Tmp { id, .. } => write!(f, "_tmp_{id}"),
309 Variable::Pipeline { id, .. } => write!(f, "pipeline_{id}"),
310 Variable::Barrier { id, .. } => write!(f, "barrier_{id}"),
311 Variable::BarrierToken { id, .. } => write!(f, "barrier_{id}_token"),
312 }
313 }
314}
315
316impl<D: Dialect> Variable<D> {
317 pub fn is_optimized(&self) -> bool {
318 self.item().is_optimized()
319 }
320
321 pub fn tmp(item: Item<D>) -> Self {
325 let inc = COUNTER_TMP_VAR.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
326
327 Variable::Tmp {
328 id: inc as Id,
329 item,
330 is_declared: false,
331 is_ptr: false,
332 is_const: false,
333 }
334 }
335
336 pub fn to_const(&mut self) {
337 if let Variable::Tmp { is_const, .. } = self {
338 *is_const = true;
339 }
340 }
341
342 pub fn reinterpret_ptr(&self, f: &mut Formatter<'_>, item: Item<D>) -> Self {
344 let mut out = Self::tmp_ptr(item);
345
346 if self.is_const() {
347 out.to_const();
348 }
349
350 let elem = out.elem();
351 let qualifier = out.const_qualifier();
352 let addr_space = D::address_space_for_variable(self);
353 let out_fmt = out.fmt_left();
354
355 writeln!(
356 f,
357 "{out_fmt} = reinterpret_cast<{addr_space}{elem}{qualifier}*>({self});"
358 )
359 .unwrap();
360
361 out
362 }
363
364 pub fn tmp_ptr(item: Item<D>) -> Self {
368 let inc = COUNTER_TMP_VAR.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
369
370 Variable::Tmp {
371 id: inc as Id,
372 item,
373 is_declared: false,
374 is_ptr: true,
375 is_const: false,
376 }
377 }
378
379 pub fn tmp_declared(item: Item<D>) -> Self {
385 let inc = COUNTER_TMP_VAR.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
386
387 Variable::Tmp {
388 id: inc as Id,
389 item,
390 is_declared: true,
391 is_ptr: false,
392 is_const: false,
393 }
394 }
395
396 pub fn optimized_args<const N: usize>(args: [Self; N]) -> OptimizedArgs<N, D> {
397 let args_after = args.map(|a| a.optimized());
398
399 let is_optimized = args_after.iter().all(|var| var.is_optimized());
400
401 if is_optimized {
402 let vectorization_before = args
403 .iter()
404 .map(|var| var.item().vectorization)
405 .max()
406 .unwrap();
407 let vectorization_after = args_after
408 .iter()
409 .map(|var| var.item().vectorization)
410 .max()
411 .unwrap();
412
413 OptimizedArgs::new(args_after, Some(vectorization_before / vectorization_after))
414 } else {
415 OptimizedArgs::new(args, None)
416 }
417 }
418
419 pub fn optimized(&self) -> Self {
420 match self {
421 Variable::GlobalInputArray(id, item) => {
422 Variable::GlobalInputArray(*id, item.optimized())
423 }
424 Variable::GlobalOutputArray(id, item) => {
425 Variable::GlobalOutputArray(*id, item.optimized())
426 }
427 Variable::LocalMut { id, item } => Variable::LocalMut {
428 id: *id,
429 item: item.optimized(),
430 },
431 Variable::LocalConst { id, item } => Variable::LocalConst {
432 id: *id,
433 item: item.optimized(),
434 },
435 Variable::Slice { id, item } => Variable::Slice {
436 id: *id,
437 item: item.optimized(),
438 },
439 Variable::Tmp {
440 id,
441 item,
442 is_declared,
443 is_ptr,
444 is_const,
445 } => Variable::Tmp {
446 id: *id,
447 item: item.optimized(),
448 is_declared: *is_declared,
449 is_ptr: *is_ptr,
450 is_const: *is_const,
451 },
452 Variable::SharedArray(id, item, size) => {
453 let before = item.vectorization;
454 let item = item.optimized();
455 let after = item.vectorization;
456 let scaling = before / after;
457
458 Variable::SharedArray(*id, item, size / scaling)
459 }
460 Variable::LocalArray(id, item, size) => {
461 let before = item.vectorization;
462 let item = item.optimized();
463 let after = item.vectorization;
464 let scaling = before / after;
465
466 Variable::LocalArray(*id, item.optimized(), size / scaling)
467 }
468 _ => *self,
469 }
470 }
471
472 pub fn is_always_scalar(&self) -> bool {
473 match self {
474 Variable::AbsolutePos(_) => true,
475 Variable::AbsolutePosBaseName => false,
476 Variable::AbsolutePosX => true,
477 Variable::AbsolutePosY => true,
478 Variable::AbsolutePosZ => true,
479 Variable::CubeCount(_) => true,
480 Variable::CubeCountBaseName => false,
481 Variable::CubeCountX => true,
482 Variable::CubeCountY => true,
483 Variable::CubeCountZ => true,
484 Variable::CubeDim => true,
485 Variable::CubeDimBaseName => false,
486 Variable::CubeDimX => true,
487 Variable::CubeDimY => true,
488 Variable::CubeDimZ => true,
489 Variable::CubePos(_) => true,
490 Variable::CubePosBaseName => true,
491 Variable::CubePosX => true,
492 Variable::CubePosY => true,
493 Variable::CubePosZ => true,
494 Variable::UnitPos => true,
495 Variable::UnitPosBaseName => true,
496 Variable::UnitPosPlane => true,
497 Variable::UnitPosX => true,
498 Variable::UnitPosY => true,
499 Variable::UnitPosZ => true,
500 Variable::PlaneDim => true,
501 Variable::PlaneDimChecked => true,
502 Variable::PlanePos => true,
503 Variable::ClusterRank => true,
504 Variable::ClusterIndexX => true,
505 Variable::ClusterIndexY => true,
506 Variable::ClusterIndexZ => true,
507
508 Variable::Barrier { .. } => false,
509 Variable::BarrierToken { .. } => false,
510 Variable::ConstantArray(_, _, _) => false,
511 Variable::Constant(_, _) => true,
512 Variable::GlobalInputArray(_, _) => false,
513 Variable::GlobalOutputArray(_, _) => false,
514 Variable::GlobalScalar { .. } => true,
515 Variable::LocalArray(_, _, _) => false,
516 Variable::LocalConst { .. } => false,
517 Variable::LocalMut { .. } => false,
518 Variable::Named { .. } => false,
519 Variable::Pipeline { .. } => false,
520 Variable::SharedArray(_, _, _) => false,
521 Variable::Shared(_, _) => false,
522 Variable::Slice { .. } => false,
523 Variable::Tmp { .. } => false,
524 Variable::WmmaFragment { .. } => false,
525 Variable::TensorMap { .. } => false,
526 }
527 }
528
529 pub fn index(&self, index: usize) -> IndexedVariable<D> {
530 IndexedVariable {
531 var: *self,
532 index,
533 optimized: self.is_optimized(),
534 }
535 }
536
537 pub fn const_qualifier(&self) -> &str {
538 if self.is_const() { " const" } else { "" }
539 }
540
541 pub fn id(&self) -> Option<Id> {
542 match self {
543 Variable::GlobalInputArray(id, ..) => Some(*id),
544 Variable::GlobalOutputArray(id, ..) => Some(*id),
545 Variable::GlobalScalar { id, .. } => Some(*id),
546 Variable::ConstantArray(id, ..) => Some(*id),
547 Variable::LocalMut { id, .. } => Some(*id),
548 Variable::LocalConst { id, .. } => Some(*id),
549 Variable::Slice { id, .. } => Some(*id),
550 Variable::Shared(id, ..) => Some(*id),
551 Variable::SharedArray(id, ..) => Some(*id),
552 Variable::LocalArray(id, ..) => Some(*id),
553 Variable::WmmaFragment { id, .. } => Some(*id),
554 Variable::Pipeline { id, .. } => Some(*id),
555 Variable::Barrier { id, .. } => Some(*id),
556 Variable::Tmp { id, .. } => Some(*id),
557 _ => None,
558 }
559 }
560
561 pub fn fmt_ptr(&self) -> String {
564 match self {
565 Variable::Slice { .. }
566 | Variable::SharedArray(_, _, _)
567 | Variable::GlobalInputArray(_, _)
568 | Variable::GlobalOutputArray(_, _) => format!("{self}"),
569 _ => format!("&{self}"),
570 }
571 }
572
573 pub fn fmt_cast_to(&self, item: Item<D>) -> String {
575 if self.item() == item {
576 self.to_string()
577 } else {
578 format!("{item}({self})")
579 }
580 }
581}
582
583impl<D: Dialect> FmtLeft for Variable<D> {
584 fn fmt_left(&self) -> String {
585 match self {
586 Self::LocalConst { item, .. } => match item.elem {
587 Elem::Atomic(_) => {
588 format!("{item}* {self}")
589 }
590 _ => {
591 format!("const {item} {self}")
592 }
593 },
594 Variable::Tmp {
595 item,
596 is_declared,
597 is_ptr,
598 is_const,
599 ..
600 } => {
601 if *is_declared {
602 return format!("{self}");
603 }
604 if *is_ptr {
605 if *is_const {
606 return format!("const {item} *{self}");
607 }
608 return format!("{item} *{self}");
609 }
610
611 format!("{item} {self}")
612 }
613 var => format!("{var}"),
614 }
615 }
616}
617
618#[derive(Debug, Clone)]
619pub struct IndexedVariable<D: Dialect> {
620 var: Variable<D>,
621 optimized: bool,
622 index: usize,
623}
624
625impl<D: Dialect> Component<D> for IndexedVariable<D> {
626 fn item(&self) -> Item<D> {
627 self.var.item()
628 }
629
630 fn index(&self, index: usize) -> IndexedVariable<D> {
631 self.var.index(index)
632 }
633
634 fn is_const(&self) -> bool {
635 self.var.is_const()
636 }
637}
638
639impl<D: Dialect> Display for IndexedVariable<D> {
640 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
641 let var = &self.var;
642
643 if let Variable::Constant(value, item) = var {
644 let value = format_const(value, item);
645 return write!(f, "{}({value})", item.elem());
646 }
647
648 let ref_ = matches!(var, Variable::LocalConst { .. })
649 .then_some("const&")
650 .unwrap_or("&");
651
652 if self.var.item().vectorization > 1 {
653 if self.optimized {
654 let item = self.var.item();
655 let addr_space = D::address_space_for_variable(&self.var);
656 write!(
657 f,
658 "(reinterpret_cast<{addr_space}{item} {ref_}>({var})).i_{}",
659 self.index
660 )
661 } else {
662 write!(f, "{var}.i_{}", self.index)
663 }
664 } else if self.optimized {
665 let item = self.var.item();
666 let addr_space = D::address_space_for_variable(&self.var);
667 write!(f, "reinterpret_cast<{addr_space}{item} {ref_}>({var})")
668 } else {
669 write!(f, "{var}")
670 }
671 }
672}
673
674impl<D: Dialect> FmtLeft for IndexedVariable<D> {
675 fn fmt_left(&self) -> String {
676 let var = &self.var;
677 let ref_ = matches!(var, Variable::LocalConst { .. })
678 .then_some("const&")
679 .unwrap_or("&");
680
681 let name = if self.var.item().vectorization > 1 {
682 if self.optimized {
683 let item = self.var.item();
684 let addr_space = D::address_space_for_variable(&self.var);
685 format!(
686 "(reinterpret_cast<{addr_space}{item} {ref_}>({var})).i_{}",
687 self.index
688 )
689 } else {
690 format!("{var}.i_{}", self.index)
691 }
692 } else {
693 format!("{var}")
694 };
695 match var {
696 Variable::LocalConst { item, .. } => format!("const {item} {name}"),
697 Variable::Tmp { item, is_ptr, .. } => {
698 if *is_ptr {
699 format!("{item} *{name}")
700 } else {
701 format!("{item} {name}")
702 }
703 }
704 _ => name,
705 }
706 }
707}
708
709impl FmtLeft for &String {
710 fn fmt_left(&self) -> String {
711 self.to_string()
712 }
713}