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