cranelift_codegen/
write.rs

1//! Converting Cranelift IR to text.
2//!
3//! The `write` module provides the `write_function` function which converts an IR `Function` to an
4//! equivalent textual form. This textual form can be read back by the `cranelift-reader` crate.
5
6use crate::entity::SecondaryMap;
7use crate::ir::entities::AnyEntity;
8use crate::ir::{
9    DataFlowGraph, DisplayFunctionAnnotations, Ebb, Function, Inst, SigRef, Type, Value, ValueDef,
10    ValueLoc,
11};
12use crate::isa::{RegInfo, TargetIsa};
13use crate::packed_option::ReservedValue;
14use crate::value_label::ValueLabelsRanges;
15use crate::HashSet;
16use alloc::string::String;
17use alloc::vec::Vec;
18use core::fmt::{self, Write};
19
20/// A `FuncWriter` used to decorate functions during printing.
21pub trait FuncWriter {
22    /// Write the extended basic block header for the current function.
23    fn write_ebb_header(
24        &mut self,
25        w: &mut dyn Write,
26        func: &Function,
27        isa: Option<&dyn TargetIsa>,
28        ebb: Ebb,
29        indent: usize,
30    ) -> fmt::Result;
31
32    /// Write the given `inst` to `w`.
33    fn write_instruction(
34        &mut self,
35        w: &mut dyn Write,
36        func: &Function,
37        aliases: &SecondaryMap<Value, Vec<Value>>,
38        isa: Option<&dyn TargetIsa>,
39        inst: Inst,
40        indent: usize,
41    ) -> fmt::Result;
42
43    /// Write the preamble to `w`. By default, this uses `write_entity_definition`.
44    fn write_preamble(
45        &mut self,
46        w: &mut dyn Write,
47        func: &Function,
48        regs: Option<&RegInfo>,
49    ) -> Result<bool, fmt::Error> {
50        self.super_preamble(w, func, regs)
51    }
52
53    /// Default impl of `write_preamble`
54    fn super_preamble(
55        &mut self,
56        w: &mut dyn Write,
57        func: &Function,
58        regs: Option<&RegInfo>,
59    ) -> Result<bool, fmt::Error> {
60        let mut any = false;
61
62        for (ss, slot) in func.stack_slots.iter() {
63            any = true;
64            self.write_entity_definition(w, func, ss.into(), slot)?;
65        }
66
67        for (gv, gv_data) in &func.global_values {
68            any = true;
69            self.write_entity_definition(w, func, gv.into(), gv_data)?;
70        }
71
72        for (heap, heap_data) in &func.heaps {
73            if !heap_data.index_type.is_invalid() {
74                any = true;
75                self.write_entity_definition(w, func, heap.into(), heap_data)?;
76            }
77        }
78
79        for (table, table_data) in &func.tables {
80            if !table_data.index_type.is_invalid() {
81                any = true;
82                self.write_entity_definition(w, func, table.into(), table_data)?;
83            }
84        }
85
86        // Write out all signatures before functions since function declarations can refer to
87        // signatures.
88        for (sig, sig_data) in &func.dfg.signatures {
89            any = true;
90            self.write_entity_definition(w, func, sig.into(), &sig_data.display(regs))?;
91        }
92
93        for (fnref, ext_func) in &func.dfg.ext_funcs {
94            if ext_func.signature != SigRef::reserved_value() {
95                any = true;
96                self.write_entity_definition(w, func, fnref.into(), ext_func)?;
97            }
98        }
99
100        for (jt, jt_data) in &func.jump_tables {
101            any = true;
102            self.write_entity_definition(w, func, jt.into(), jt_data)?;
103        }
104
105        Ok(any)
106    }
107
108    /// Write an entity definition defined in the preamble to `w`.
109    fn write_entity_definition(
110        &mut self,
111        w: &mut dyn Write,
112        func: &Function,
113        entity: AnyEntity,
114        value: &dyn fmt::Display,
115    ) -> fmt::Result {
116        self.super_entity_definition(w, func, entity, value)
117    }
118
119    /// Default impl of `write_entity_definition`
120    #[allow(unused_variables)]
121    fn super_entity_definition(
122        &mut self,
123        w: &mut dyn Write,
124        func: &Function,
125        entity: AnyEntity,
126        value: &dyn fmt::Display,
127    ) -> fmt::Result {
128        writeln!(w, "    {} = {}", entity, value)
129    }
130}
131
132/// A `PlainWriter` that doesn't decorate the function.
133pub struct PlainWriter;
134
135impl FuncWriter for PlainWriter {
136    fn write_instruction(
137        &mut self,
138        w: &mut dyn Write,
139        func: &Function,
140        aliases: &SecondaryMap<Value, Vec<Value>>,
141        isa: Option<&dyn TargetIsa>,
142        inst: Inst,
143        indent: usize,
144    ) -> fmt::Result {
145        write_instruction(w, func, aliases, isa, inst, indent)
146    }
147
148    fn write_ebb_header(
149        &mut self,
150        w: &mut dyn Write,
151        func: &Function,
152        isa: Option<&dyn TargetIsa>,
153        ebb: Ebb,
154        indent: usize,
155    ) -> fmt::Result {
156        write_ebb_header(w, func, isa, ebb, indent)
157    }
158}
159
160/// Write `func` to `w` as equivalent text.
161/// Use `isa` to emit ISA-dependent annotations.
162pub fn write_function(
163    w: &mut dyn Write,
164    func: &Function,
165    annotations: &DisplayFunctionAnnotations,
166) -> fmt::Result {
167    decorate_function(&mut PlainWriter, w, func, annotations)
168}
169
170/// Create a reverse-alias map from a value to all aliases having that value as a direct target
171fn alias_map(func: &Function) -> SecondaryMap<Value, Vec<Value>> {
172    let mut aliases = SecondaryMap::<_, Vec<_>>::new();
173    for v in func.dfg.values() {
174        // VADFS returns the immediate target of an alias
175        if let Some(k) = func.dfg.value_alias_dest_for_serialization(v) {
176            aliases[k].push(v);
177        }
178    }
179    aliases
180}
181
182/// Writes `func` to `w` as text.
183/// write_function_plain is passed as 'closure' to print instructions as text.
184/// pretty_function_error is passed as 'closure' to add error decoration.
185pub fn decorate_function<FW: FuncWriter>(
186    func_w: &mut FW,
187    w: &mut dyn Write,
188    func: &Function,
189    annotations: &DisplayFunctionAnnotations,
190) -> fmt::Result {
191    let regs = annotations.isa.map(TargetIsa::register_info);
192    let regs = regs.as_ref();
193
194    write!(w, "function ")?;
195    write_spec(w, func, regs)?;
196    writeln!(w, " {{")?;
197    let aliases = alias_map(func);
198    let mut any = func_w.write_preamble(w, func, regs)?;
199    for ebb in &func.layout {
200        if any {
201            writeln!(w)?;
202        }
203        decorate_ebb(func_w, w, func, &aliases, annotations, ebb)?;
204        any = true;
205    }
206    writeln!(w, "}}")
207}
208
209//----------------------------------------------------------------------
210//
211// Function spec.
212
213fn write_spec(w: &mut dyn Write, func: &Function, regs: Option<&RegInfo>) -> fmt::Result {
214    write!(w, "{}{}", func.name, func.signature.display(regs))
215}
216
217//----------------------------------------------------------------------
218//
219// Basic blocks
220
221fn write_arg(
222    w: &mut dyn Write,
223    func: &Function,
224    regs: Option<&RegInfo>,
225    arg: Value,
226) -> fmt::Result {
227    write!(w, "{}: {}", arg, func.dfg.value_type(arg))?;
228    let loc = func.locations[arg];
229    if loc.is_assigned() {
230        write!(w, " [{}]", loc.display(regs))?
231    }
232
233    Ok(())
234}
235
236/// Write out the basic block header, outdented:
237///
238///    ebb1:
239///    ebb1(v1: i32):
240///    ebb10(v4: f64, v5: b1):
241///
242pub fn write_ebb_header(
243    w: &mut dyn Write,
244    func: &Function,
245    isa: Option<&dyn TargetIsa>,
246    ebb: Ebb,
247    indent: usize,
248) -> fmt::Result {
249    // The `indent` is the instruction indentation. EBB headers are 4 spaces out from that.
250    write!(w, "{1:0$}{2}", indent - 4, "", ebb)?;
251
252    let regs = isa.map(TargetIsa::register_info);
253    let regs = regs.as_ref();
254
255    let mut args = func.dfg.ebb_params(ebb).iter().cloned();
256    match args.next() {
257        None => return writeln!(w, ":"),
258        Some(arg) => {
259            write!(w, "(")?;
260            write_arg(w, func, regs, arg)?;
261        }
262    }
263    // Remaining arguments.
264    for arg in args {
265        write!(w, ", ")?;
266        write_arg(w, func, regs, arg)?;
267    }
268    writeln!(w, "):")
269}
270
271fn write_valueloc(w: &mut dyn Write, loc: ValueLoc, regs: &RegInfo) -> fmt::Result {
272    match loc {
273        ValueLoc::Reg(r) => write!(w, "{}", regs.display_regunit(r)),
274        ValueLoc::Stack(ss) => write!(w, "{}", ss),
275        ValueLoc::Unassigned => write!(w, "?"),
276    }
277}
278
279fn write_value_range_markers(
280    w: &mut dyn Write,
281    val_ranges: &ValueLabelsRanges,
282    regs: &RegInfo,
283    offset: u32,
284    indent: usize,
285) -> fmt::Result {
286    let mut result = String::new();
287    let mut shown = HashSet::new();
288    for (val, rng) in val_ranges {
289        for i in (0..rng.len()).rev() {
290            if rng[i].start == offset {
291                write!(&mut result, " {}@", val)?;
292                write_valueloc(&mut result, rng[i].loc, regs)?;
293                shown.insert(val);
294                break;
295            }
296        }
297    }
298    for (val, rng) in val_ranges {
299        for i in (0..rng.len()).rev() {
300            if rng[i].end == offset && !shown.contains(val) {
301                write!(&mut result, " {}\u{2620}", val)?;
302                break;
303            }
304        }
305    }
306    if !result.is_empty() {
307        writeln!(w, ";{1:0$}; {2}", indent + 24, "", result)?;
308    }
309    Ok(())
310}
311
312fn decorate_ebb<FW: FuncWriter>(
313    func_w: &mut FW,
314    w: &mut dyn Write,
315    func: &Function,
316    aliases: &SecondaryMap<Value, Vec<Value>>,
317    annotations: &DisplayFunctionAnnotations,
318    ebb: Ebb,
319) -> fmt::Result {
320    // Indent all instructions if any encodings are present.
321    let indent = if func.encodings.is_empty() && func.srclocs.is_empty() {
322        4
323    } else {
324        36
325    };
326    let isa = annotations.isa;
327
328    func_w.write_ebb_header(w, func, isa, ebb, indent)?;
329    for a in func.dfg.ebb_params(ebb).iter().cloned() {
330        write_value_aliases(w, aliases, a, indent)?;
331    }
332
333    if let Some(isa) = isa {
334        if !func.offsets.is_empty() {
335            let encinfo = isa.encoding_info();
336            let regs = &isa.register_info();
337            for (offset, inst, size) in func.inst_offsets(ebb, &encinfo) {
338                func_w.write_instruction(w, func, aliases, Some(isa), inst, indent)?;
339                if size > 0 {
340                    if let Some(val_ranges) = annotations.value_ranges {
341                        write_value_range_markers(w, val_ranges, regs, offset + size, indent)?;
342                    }
343                }
344            }
345            return Ok(());
346        }
347    }
348
349    for inst in func.layout.ebb_insts(ebb) {
350        func_w.write_instruction(w, func, aliases, isa, inst, indent)?;
351    }
352
353    Ok(())
354}
355
356//----------------------------------------------------------------------
357//
358// Instructions
359
360// Should `inst` be printed with a type suffix?
361//
362// Polymorphic instructions may need a suffix indicating the value of the controlling type variable
363// if it can't be trivially inferred.
364//
365fn type_suffix(func: &Function, inst: Inst) -> Option<Type> {
366    let inst_data = &func.dfg[inst];
367    let constraints = inst_data.opcode().constraints();
368
369    if !constraints.is_polymorphic() {
370        return None;
371    }
372
373    // If the controlling type variable can be inferred from the type of the designated value input
374    // operand, we don't need the type suffix.
375    if constraints.use_typevar_operand() {
376        let ctrl_var = inst_data.typevar_operand(&func.dfg.value_lists).unwrap();
377        let def_ebb = match func.dfg.value_def(ctrl_var) {
378            ValueDef::Result(instr, _) => func.layout.inst_ebb(instr),
379            ValueDef::Param(ebb, _) => Some(ebb),
380        };
381        if def_ebb.is_some() && def_ebb == func.layout.inst_ebb(inst) {
382            return None;
383        }
384    }
385
386    let rtype = func.dfg.ctrl_typevar(inst);
387    assert!(
388        !rtype.is_invalid(),
389        "Polymorphic instruction must produce a result"
390    );
391    Some(rtype)
392}
393
394/// Write out any aliases to the given target, including indirect aliases
395fn write_value_aliases(
396    w: &mut dyn Write,
397    aliases: &SecondaryMap<Value, Vec<Value>>,
398    target: Value,
399    indent: usize,
400) -> fmt::Result {
401    let mut todo_stack = vec![target];
402    while let Some(target) = todo_stack.pop() {
403        for &a in &aliases[target] {
404            writeln!(w, "{1:0$}{2} -> {3}", indent, "", a, target)?;
405            todo_stack.push(a);
406        }
407    }
408
409    Ok(())
410}
411
412fn write_instruction(
413    w: &mut dyn Write,
414    func: &Function,
415    aliases: &SecondaryMap<Value, Vec<Value>>,
416    isa: Option<&dyn TargetIsa>,
417    inst: Inst,
418    indent: usize,
419) -> fmt::Result {
420    // Prefix containing source location, encoding, and value locations.
421    let mut s = String::with_capacity(16);
422
423    // Source location goes first.
424    let srcloc = func.srclocs[inst];
425    if !srcloc.is_default() {
426        write!(s, "{} ", srcloc)?;
427    }
428
429    // Write out encoding info.
430    if let Some(enc) = func.encodings.get(inst).cloned() {
431        if let Some(isa) = isa {
432            write!(s, "[{}", isa.encoding_info().display(enc))?;
433            // Write value locations, if we have them.
434            if !func.locations.is_empty() {
435                let regs = isa.register_info();
436                for &r in func.dfg.inst_results(inst) {
437                    write!(s, ",{}", func.locations[r].display(&regs))?
438                }
439            }
440            write!(s, "] ")?;
441        } else {
442            write!(s, "[{}] ", enc)?;
443        }
444    }
445
446    // Write out prefix and indent the instruction.
447    write!(w, "{1:0$}", indent, s)?;
448
449    // Write out the result values, if any.
450    let mut has_results = false;
451    for r in func.dfg.inst_results(inst) {
452        if !has_results {
453            has_results = true;
454            write!(w, "{}", r)?;
455        } else {
456            write!(w, ", {}", r)?;
457        }
458    }
459    if has_results {
460        write!(w, " = ")?;
461    }
462
463    // Then the opcode, possibly with a '.type' suffix.
464    let opcode = func.dfg[inst].opcode();
465
466    match type_suffix(func, inst) {
467        Some(suf) => write!(w, "{}.{}", opcode, suf)?,
468        None => write!(w, "{}", opcode)?,
469    }
470
471    write_operands(w, &func.dfg, isa, inst)?;
472    writeln!(w)?;
473
474    // Value aliases come out on lines after the instruction defining the referent.
475    for r in func.dfg.inst_results(inst) {
476        write_value_aliases(w, aliases, *r, indent)?;
477    }
478    Ok(())
479}
480
481/// Write the operands of `inst` to `w` with a prepended space.
482pub fn write_operands(
483    w: &mut dyn Write,
484    dfg: &DataFlowGraph,
485    isa: Option<&dyn TargetIsa>,
486    inst: Inst,
487) -> fmt::Result {
488    let pool = &dfg.value_lists;
489    use crate::ir::instructions::InstructionData::*;
490    match dfg[inst] {
491        Unary { arg, .. } => write!(w, " {}", arg),
492        UnaryImm { imm, .. } => write!(w, " {}", imm),
493        UnaryIeee32 { imm, .. } => write!(w, " {}", imm),
494        UnaryIeee64 { imm, .. } => write!(w, " {}", imm),
495        UnaryBool { imm, .. } => write!(w, " {}", imm),
496        UnaryGlobalValue { global_value, .. } => write!(w, " {}", global_value),
497        Binary { args, .. } => write!(w, " {}, {}", args[0], args[1]),
498        BinaryImm { arg, imm, .. } => write!(w, " {}, {}", arg, imm),
499        Ternary { args, .. } => write!(w, " {}, {}, {}", args[0], args[1], args[2]),
500        MultiAry { ref args, .. } => {
501            if args.is_empty() {
502                write!(w, "")
503            } else {
504                write!(w, " {}", DisplayValues(args.as_slice(pool)))
505            }
506        }
507        NullAry { .. } => write!(w, " "),
508        InsertLane { lane, args, .. } => write!(w, " {}, {}, {}", args[0], lane, args[1]),
509        ExtractLane { lane, arg, .. } => write!(w, " {}, {}", arg, lane),
510        UnaryConst {
511            constant_handle, ..
512        } => {
513            let constant_data = dfg.constants.get(constant_handle);
514            write!(w, " {}", constant_data)
515        }
516        Shuffle { mask, args, .. } => {
517            let data = dfg.immediates.get(mask).expect(
518                "Expected the shuffle mask to already be inserted into the immediates table",
519            );
520            write!(w, " {}, {}, {}", args[0], args[1], data)
521        }
522        IntCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]),
523        IntCompareImm { cond, arg, imm, .. } => write!(w, " {} {}, {}", cond, arg, imm),
524        IntCond { cond, arg, .. } => write!(w, " {} {}", cond, arg),
525        FloatCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]),
526        FloatCond { cond, arg, .. } => write!(w, " {} {}", cond, arg),
527        IntSelect { cond, args, .. } => {
528            write!(w, " {} {}, {}, {}", cond, args[0], args[1], args[2])
529        }
530        Jump {
531            destination,
532            ref args,
533            ..
534        } => {
535            write!(w, " {}", destination)?;
536            write_ebb_args(w, args.as_slice(pool))
537        }
538        Branch {
539            destination,
540            ref args,
541            ..
542        } => {
543            let args = args.as_slice(pool);
544            write!(w, " {}, {}", args[0], destination)?;
545            write_ebb_args(w, &args[1..])
546        }
547        BranchInt {
548            cond,
549            destination,
550            ref args,
551            ..
552        } => {
553            let args = args.as_slice(pool);
554            write!(w, " {} {}, {}", cond, args[0], destination)?;
555            write_ebb_args(w, &args[1..])
556        }
557        BranchFloat {
558            cond,
559            destination,
560            ref args,
561            ..
562        } => {
563            let args = args.as_slice(pool);
564            write!(w, " {} {}, {}", cond, args[0], destination)?;
565            write_ebb_args(w, &args[1..])
566        }
567        BranchIcmp {
568            cond,
569            destination,
570            ref args,
571            ..
572        } => {
573            let args = args.as_slice(pool);
574            write!(w, " {} {}, {}, {}", cond, args[0], args[1], destination)?;
575            write_ebb_args(w, &args[2..])
576        }
577        BranchTable {
578            arg,
579            destination,
580            table,
581            ..
582        } => write!(w, " {}, {}, {}", arg, destination, table),
583        BranchTableBase { table, .. } => write!(w, " {}", table),
584        BranchTableEntry {
585            args, imm, table, ..
586        } => write!(w, " {}, {}, {}, {}", args[0], args[1], imm, table),
587        IndirectJump { arg, table, .. } => write!(w, " {}, {}", arg, table),
588        Call {
589            func_ref, ref args, ..
590        } => write!(w, " {}({})", func_ref, DisplayValues(args.as_slice(pool))),
591        CallIndirect {
592            sig_ref, ref args, ..
593        } => {
594            let args = args.as_slice(pool);
595            write!(
596                w,
597                " {}, {}({})",
598                sig_ref,
599                args[0],
600                DisplayValues(&args[1..])
601            )
602        }
603        FuncAddr { func_ref, .. } => write!(w, " {}", func_ref),
604        StackLoad {
605            stack_slot, offset, ..
606        } => write!(w, " {}{}", stack_slot, offset),
607        StackStore {
608            arg,
609            stack_slot,
610            offset,
611            ..
612        } => write!(w, " {}, {}{}", arg, stack_slot, offset),
613        HeapAddr { heap, arg, imm, .. } => write!(w, " {}, {}, {}", heap, arg, imm),
614        TableAddr { table, arg, .. } => write!(w, " {}, {}", table, arg),
615        Load {
616            flags, arg, offset, ..
617        } => write!(w, "{} {}{}", flags, arg, offset),
618        LoadComplex {
619            flags,
620            ref args,
621            offset,
622            ..
623        } => {
624            let args = args.as_slice(pool);
625            write!(
626                w,
627                "{} {}{}",
628                flags,
629                DisplayValuesWithDelimiter(&args, '+'),
630                offset
631            )
632        }
633        Store {
634            flags,
635            args,
636            offset,
637            ..
638        } => write!(w, "{} {}, {}{}", flags, args[0], args[1], offset),
639        StoreComplex {
640            flags,
641            ref args,
642            offset,
643            ..
644        } => {
645            let args = args.as_slice(pool);
646            write!(
647                w,
648                "{} {}, {}{}",
649                flags,
650                args[0],
651                DisplayValuesWithDelimiter(&args[1..], '+'),
652                offset
653            )
654        }
655        RegMove { arg, src, dst, .. } => {
656            if let Some(isa) = isa {
657                let regs = isa.register_info();
658                write!(
659                    w,
660                    " {}, {} -> {}",
661                    arg,
662                    regs.display_regunit(src),
663                    regs.display_regunit(dst)
664                )
665            } else {
666                write!(w, " {}, %{} -> %{}", arg, src, dst)
667            }
668        }
669        CopySpecial { src, dst, .. } => {
670            if let Some(isa) = isa {
671                let regs = isa.register_info();
672                write!(
673                    w,
674                    " {} -> {}",
675                    regs.display_regunit(src),
676                    regs.display_regunit(dst)
677                )
678            } else {
679                write!(w, " %{} -> %{}", src, dst)
680            }
681        }
682        CopyToSsa { src, .. } => {
683            if let Some(isa) = isa {
684                let regs = isa.register_info();
685                write!(w, " {}", regs.display_regunit(src))
686            } else {
687                write!(w, " %{}", src)
688            }
689        }
690        RegSpill { arg, src, dst, .. } => {
691            if let Some(isa) = isa {
692                let regs = isa.register_info();
693                write!(w, " {}, {} -> {}", arg, regs.display_regunit(src), dst)
694            } else {
695                write!(w, " {}, %{} -> {}", arg, src, dst)
696            }
697        }
698        RegFill { arg, src, dst, .. } => {
699            if let Some(isa) = isa {
700                let regs = isa.register_info();
701                write!(w, " {}, {} -> {}", arg, src, regs.display_regunit(dst))
702            } else {
703                write!(w, " {}, {} -> %{}", arg, src, dst)
704            }
705        }
706        Trap { code, .. } => write!(w, " {}", code),
707        CondTrap { arg, code, .. } => write!(w, " {}, {}", arg, code),
708        IntCondTrap {
709            cond, arg, code, ..
710        } => write!(w, " {} {}, {}", cond, arg, code),
711        FloatCondTrap {
712            cond, arg, code, ..
713        } => write!(w, " {} {}, {}", cond, arg, code),
714    }
715}
716
717/// Write EBB args using optional parantheses.
718fn write_ebb_args(w: &mut dyn Write, args: &[Value]) -> fmt::Result {
719    if args.is_empty() {
720        Ok(())
721    } else {
722        write!(w, "({})", DisplayValues(args))
723    }
724}
725
726/// Displayable slice of values.
727struct DisplayValues<'a>(&'a [Value]);
728
729impl<'a> fmt::Display for DisplayValues<'a> {
730    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
731        for (i, val) in self.0.iter().enumerate() {
732            if i == 0 {
733                write!(f, "{}", val)?;
734            } else {
735                write!(f, ", {}", val)?;
736            }
737        }
738        Ok(())
739    }
740}
741
742struct DisplayValuesWithDelimiter<'a>(&'a [Value], char);
743
744impl<'a> fmt::Display for DisplayValuesWithDelimiter<'a> {
745    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
746        for (i, val) in self.0.iter().enumerate() {
747            if i == 0 {
748                write!(f, "{}", val)?;
749            } else {
750                write!(f, "{}{}", self.1, val)?;
751            }
752        }
753        Ok(())
754    }
755}
756
757#[cfg(test)]
758mod tests {
759    use crate::cursor::{Cursor, CursorPosition, FuncCursor};
760    use crate::ir::types;
761    use crate::ir::{ExternalName, Function, InstBuilder, StackSlotData, StackSlotKind};
762    use alloc::string::ToString;
763
764    #[test]
765    fn basic() {
766        let mut f = Function::new();
767        assert_eq!(f.to_string(), "function u0:0() fast {\n}\n");
768
769        f.name = ExternalName::testcase("foo");
770        assert_eq!(f.to_string(), "function %foo() fast {\n}\n");
771
772        f.create_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 4));
773        assert_eq!(
774            f.to_string(),
775            "function %foo() fast {\n    ss0 = explicit_slot 4\n}\n"
776        );
777
778        let ebb = f.dfg.make_ebb();
779        f.layout.append_ebb(ebb);
780        assert_eq!(
781            f.to_string(),
782            "function %foo() fast {\n    ss0 = explicit_slot 4\n\nebb0:\n}\n"
783        );
784
785        f.dfg.append_ebb_param(ebb, types::I8);
786        assert_eq!(
787            f.to_string(),
788            "function %foo() fast {\n    ss0 = explicit_slot 4\n\nebb0(v0: i8):\n}\n"
789        );
790
791        f.dfg.append_ebb_param(ebb, types::F32.by(4).unwrap());
792        assert_eq!(
793            f.to_string(),
794            "function %foo() fast {\n    ss0 = explicit_slot 4\n\nebb0(v0: i8, v1: f32x4):\n}\n"
795        );
796
797        {
798            let mut cursor = FuncCursor::new(&mut f);
799            cursor.set_position(CursorPosition::After(ebb));
800            cursor.ins().return_(&[])
801        };
802        assert_eq!(
803            f.to_string(),
804            "function %foo() fast {\n    ss0 = explicit_slot 4\n\nebb0(v0: i8, v1: f32x4):\n    return\n}\n"
805        );
806    }
807
808    #[test]
809    fn aliases() {
810        use crate::ir::InstBuilder;
811
812        let mut func = Function::new();
813        {
814            let ebb0 = func.dfg.make_ebb();
815            let mut pos = FuncCursor::new(&mut func);
816            pos.insert_ebb(ebb0);
817
818            // make some detached values for change_to_alias
819            let v0 = pos.func.dfg.append_ebb_param(ebb0, types::I32);
820            let v1 = pos.func.dfg.append_ebb_param(ebb0, types::I32);
821            let v2 = pos.func.dfg.append_ebb_param(ebb0, types::I32);
822            pos.func.dfg.detach_ebb_params(ebb0);
823
824            // alias to a param--will be printed at beginning of ebb defining param
825            let v3 = pos.func.dfg.append_ebb_param(ebb0, types::I32);
826            pos.func.dfg.change_to_alias(v0, v3);
827
828            // alias to an alias--should print attached to alias, not ultimate target
829            pos.func.dfg.make_value_alias_for_serialization(v0, v2); // v0 <- v2
830
831            // alias to a result--will be printed after instruction producing result
832            let _dummy0 = pos.ins().iconst(types::I32, 42);
833            let v4 = pos.ins().iadd(v0, v0);
834            pos.func.dfg.change_to_alias(v1, v4);
835            let _dummy1 = pos.ins().iconst(types::I32, 23);
836            let _v7 = pos.ins().iadd(v1, v1);
837        }
838        assert_eq!(
839            func.to_string(),
840            "function u0:0() fast {\nebb0(v3: i32):\n    v0 -> v3\n    v2 -> v0\n    v4 = iconst.i32 42\n    v5 = iadd v0, v0\n    v1 -> v5\n    v6 = iconst.i32 23\n    v7 = iadd v1, v1\n}\n"
841        );
842    }
843}