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.
write_block_header( &mut self, w: &mut dyn Write, func: &Function, isa: Option<&dyn TargetIsa>, block: Block, indent: usize, ) -> fmt::Result23     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`.
write_instruction( &mut self, w: &mut dyn Write, func: &Function, aliases: &SecondaryMap<Value, Vec<Value>>, isa: Option<&dyn TargetIsa>, inst: Inst, indent: usize, ) -> fmt::Result33     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`.
write_preamble( &mut self, w: &mut dyn Write, func: &Function, regs: Option<&RegInfo>, ) -> Result<bool, fmt::Error>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`
super_preamble( &mut self, w: &mut dyn Write, func: &Function, regs: Option<&RegInfo>, ) -> Result<bool, fmt::Error>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`.
write_entity_definition( &mut self, w: &mut dyn Write, func: &Function, entity: AnyEntity, value: &dyn fmt::Display, ) -> fmt::Result119     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)]
super_entity_definition( &mut self, w: &mut dyn Write, func: &Function, entity: AnyEntity, value: &dyn fmt::Display, ) -> fmt::Result131     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 {
write_instruction( &mut self, w: &mut dyn Write, func: &Function, aliases: &SecondaryMap<Value, Vec<Value>>, isa: Option<&dyn TargetIsa>, inst: Inst, indent: usize, ) -> fmt::Result146     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 
write_block_header( &mut self, w: &mut dyn Write, func: &Function, isa: Option<&dyn TargetIsa>, block: Block, indent: usize, ) -> fmt::Result158     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.
write_function( w: &mut dyn Write, func: &Function, annotations: &DisplayFunctionAnnotations, ) -> fmt::Result172 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
alias_map(func: &Function) -> SecondaryMap<Value, Vec<Value>>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.
decorate_function<FW: FuncWriter>( func_w: &mut FW, w: &mut dyn Write, func: &Function, annotations: &DisplayFunctionAnnotations, ) -> fmt::Result195 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 
write_spec(w: &mut dyn Write, func: &Function, regs: Option<&RegInfo>) -> fmt::Result223 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 
write_arg( w: &mut dyn Write, func: &Function, regs: Option<&RegInfo>, arg: Value, ) -> fmt::Result231 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 ///
write_block_header( w: &mut dyn Write, func: &Function, isa: Option<&dyn TargetIsa>, block: Block, indent: usize, ) -> fmt::Result252 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 
write_valueloc(w: &mut dyn Write, loc: ValueLoc, regs: &RegInfo) -> fmt::Result281 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 
write_value_range_markers( w: &mut dyn Write, val_ranges: &ValueLabelsRanges, regs: &RegInfo, offset: u32, indent: usize, ) -> fmt::Result289 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 
decorate_block<FW: FuncWriter>( func_w: &mut FW, w: &mut dyn Write, func: &Function, aliases: &SecondaryMap<Value, Vec<Value>>, annotations: &DisplayFunctionAnnotations, block: Block, ) -> fmt::Result322 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 //
type_suffix(func: &Function, inst: Inst) -> Option<Type>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
write_value_aliases( w: &mut dyn Write, aliases: &SecondaryMap<Value, Vec<Value>>, target: Value, indent: usize, ) -> fmt::Result405 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 
write_instruction( w: &mut dyn Write, func: &Function, aliases: &SecondaryMap<Value, Vec<Value>>, isa: Option<&dyn TargetIsa>, inst: Inst, indent: usize, ) -> fmt::Result422 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.
write_operands( w: &mut dyn Write, dfg: &DataFlowGraph, isa: Option<&dyn TargetIsa>, inst: Inst, ) -> fmt::Result492 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         Unary { arg, .. } => write!(w, " {}", arg),
502         UnaryImm { imm, .. } => write!(w, " {}", imm),
503         UnaryIeee32 { imm, .. } => write!(w, " {}", imm),
504         UnaryIeee64 { imm, .. } => write!(w, " {}", imm),
505         UnaryBool { imm, .. } => write!(w, " {}", imm),
506         UnaryGlobalValue { global_value, .. } => write!(w, " {}", global_value),
507         UnaryConst {
508             constant_handle, ..
509         } => write!(w, " {}", constant_handle),
510         Binary { args, .. } => write!(w, " {}, {}", args[0], args[1]),
511         BinaryImm8 { arg, imm, .. } => write!(w, " {}, {}", arg, imm),
512         BinaryImm64 { arg, imm, .. } => write!(w, " {}, {}", arg, imm),
513         Ternary { args, .. } => write!(w, " {}, {}, {}", args[0], args[1], args[2]),
514         MultiAry { ref args, .. } => {
515             if args.is_empty() {
516                 write!(w, "")
517             } else {
518                 write!(w, " {}", DisplayValues(args.as_slice(pool)))
519             }
520         }
521         NullAry { .. } => write!(w, " "),
522         TernaryImm8 { imm, args, .. } => write!(w, " {}, {}, {}", args[0], args[1], imm),
523         Shuffle { mask, args, .. } => {
524             let data = dfg.immediates.get(mask).expect(
525                 "Expected the shuffle mask to already be inserted into the immediates table",
526             );
527             write!(w, " {}, {}, {}", args[0], args[1], data)
528         }
529         IntCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]),
530         IntCompareImm { cond, arg, imm, .. } => write!(w, " {} {}, {}", cond, arg, imm),
531         IntCond { cond, arg, .. } => write!(w, " {} {}", cond, arg),
532         FloatCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]),
533         FloatCond { cond, arg, .. } => write!(w, " {} {}", cond, arg),
534         IntSelect { cond, args, .. } => {
535             write!(w, " {} {}, {}, {}", cond, args[0], args[1], args[2])
536         }
537         Jump {
538             destination,
539             ref args,
540             ..
541         } => {
542             write!(w, " {}", destination)?;
543             write_block_args(w, args.as_slice(pool))
544         }
545         Branch {
546             destination,
547             ref args,
548             ..
549         } => {
550             let args = args.as_slice(pool);
551             write!(w, " {}, {}", args[0], destination)?;
552             write_block_args(w, &args[1..])
553         }
554         BranchInt {
555             cond,
556             destination,
557             ref args,
558             ..
559         } => {
560             let args = args.as_slice(pool);
561             write!(w, " {} {}, {}", cond, args[0], destination)?;
562             write_block_args(w, &args[1..])
563         }
564         BranchFloat {
565             cond,
566             destination,
567             ref args,
568             ..
569         } => {
570             let args = args.as_slice(pool);
571             write!(w, " {} {}, {}", cond, args[0], destination)?;
572             write_block_args(w, &args[1..])
573         }
574         BranchIcmp {
575             cond,
576             destination,
577             ref args,
578             ..
579         } => {
580             let args = args.as_slice(pool);
581             write!(w, " {} {}, {}, {}", cond, args[0], args[1], destination)?;
582             write_block_args(w, &args[2..])
583         }
584         BranchTable {
585             arg,
586             destination,
587             table,
588             ..
589         } => write!(w, " {}, {}, {}", arg, destination, table),
590         BranchTableBase { table, .. } => write!(w, " {}", table),
591         BranchTableEntry {
592             args, imm, table, ..
593         } => write!(w, " {}, {}, {}, {}", args[0], args[1], imm, table),
594         IndirectJump { arg, table, .. } => write!(w, " {}, {}", arg, table),
595         Call {
596             func_ref, ref args, ..
597         } => write!(w, " {}({})", func_ref, DisplayValues(args.as_slice(pool))),
598         CallIndirect {
599             sig_ref, ref args, ..
600         } => {
601             let args = args.as_slice(pool);
602             write!(
603                 w,
604                 " {}, {}({})",
605                 sig_ref,
606                 args[0],
607                 DisplayValues(&args[1..])
608             )
609         }
610         FuncAddr { func_ref, .. } => write!(w, " {}", func_ref),
611         StackLoad {
612             stack_slot, offset, ..
613         } => write!(w, " {}{}", stack_slot, offset),
614         StackStore {
615             arg,
616             stack_slot,
617             offset,
618             ..
619         } => write!(w, " {}, {}{}", arg, stack_slot, offset),
620         HeapAddr { heap, arg, imm, .. } => write!(w, " {}, {}, {}", heap, arg, imm),
621         TableAddr { table, arg, .. } => write!(w, " {}, {}", table, arg),
622         Load {
623             flags, arg, offset, ..
624         } => write!(w, "{} {}{}", flags, arg, offset),
625         LoadComplex {
626             flags,
627             ref args,
628             offset,
629             ..
630         } => {
631             let args = args.as_slice(pool);
632             write!(
633                 w,
634                 "{} {}{}",
635                 flags,
636                 DisplayValuesWithDelimiter(&args, '+'),
637                 offset
638             )
639         }
640         Store {
641             flags,
642             args,
643             offset,
644             ..
645         } => write!(w, "{} {}, {}{}", flags, args[0], args[1], offset),
646         StoreComplex {
647             flags,
648             ref args,
649             offset,
650             ..
651         } => {
652             let args = args.as_slice(pool);
653             write!(
654                 w,
655                 "{} {}, {}{}",
656                 flags,
657                 args[0],
658                 DisplayValuesWithDelimiter(&args[1..], '+'),
659                 offset
660             )
661         }
662         RegMove { arg, src, dst, .. } => {
663             if let Some(isa) = isa {
664                 let regs = isa.register_info();
665                 write!(
666                     w,
667                     " {}, {} -> {}",
668                     arg,
669                     regs.display_regunit(src),
670                     regs.display_regunit(dst)
671                 )
672             } else {
673                 write!(w, " {}, %{} -> %{}", arg, src, dst)
674             }
675         }
676         CopySpecial { src, dst, .. } => {
677             if let Some(isa) = isa {
678                 let regs = isa.register_info();
679                 write!(
680                     w,
681                     " {} -> {}",
682                     regs.display_regunit(src),
683                     regs.display_regunit(dst)
684                 )
685             } else {
686                 write!(w, " %{} -> %{}", src, dst)
687             }
688         }
689         CopyToSsa { src, .. } => {
690             if let Some(isa) = isa {
691                 let regs = isa.register_info();
692                 write!(w, " {}", regs.display_regunit(src))
693             } else {
694                 write!(w, " %{}", src)
695             }
696         }
697         RegSpill { arg, src, dst, .. } => {
698             if let Some(isa) = isa {
699                 let regs = isa.register_info();
700                 write!(w, " {}, {} -> {}", arg, regs.display_regunit(src), dst)
701             } else {
702                 write!(w, " {}, %{} -> {}", arg, src, dst)
703             }
704         }
705         RegFill { arg, src, dst, .. } => {
706             if let Some(isa) = isa {
707                 let regs = isa.register_info();
708                 write!(w, " {}, {} -> {}", arg, src, regs.display_regunit(dst))
709             } else {
710                 write!(w, " {}, {} -> %{}", arg, src, dst)
711             }
712         }
713         Trap { code, .. } => write!(w, " {}", code),
714         CondTrap { arg, code, .. } => write!(w, " {}, {}", arg, code),
715         IntCondTrap {
716             cond, arg, code, ..
717         } => write!(w, " {} {}, {}", cond, arg, code),
718         FloatCondTrap {
719             cond, arg, code, ..
720         } => write!(w, " {} {}, {}", cond, arg, code),
721     }
722 }
723 
724 /// Write block args using optional parantheses.
write_block_args(w: &mut dyn Write, args: &[Value]) -> fmt::Result725 fn write_block_args(w: &mut dyn Write, args: &[Value]) -> fmt::Result {
726     if args.is_empty() {
727         Ok(())
728     } else {
729         write!(w, "({})", DisplayValues(args))
730     }
731 }
732 
733 /// Displayable slice of values.
734 struct DisplayValues<'a>(&'a [Value]);
735 
736 impl<'a> fmt::Display for DisplayValues<'a> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result737     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
738         for (i, val) in self.0.iter().enumerate() {
739             if i == 0 {
740                 write!(f, "{}", val)?;
741             } else {
742                 write!(f, ", {}", val)?;
743             }
744         }
745         Ok(())
746     }
747 }
748 
749 struct DisplayValuesWithDelimiter<'a>(&'a [Value], char);
750 
751 impl<'a> fmt::Display for DisplayValuesWithDelimiter<'a> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result752     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
753         for (i, val) in self.0.iter().enumerate() {
754             if i == 0 {
755                 write!(f, "{}", val)?;
756             } else {
757                 write!(f, "{}{}", self.1, val)?;
758             }
759         }
760         Ok(())
761     }
762 }
763 
764 #[cfg(test)]
765 mod tests {
766     use crate::cursor::{Cursor, CursorPosition, FuncCursor};
767     use crate::ir::types;
768     use crate::ir::{ExternalName, Function, InstBuilder, StackSlotData, StackSlotKind};
769     use alloc::string::ToString;
770 
771     #[test]
basic()772     fn basic() {
773         let mut f = Function::new();
774         assert_eq!(f.to_string(), "function u0:0() fast {\n}\n");
775 
776         f.name = ExternalName::testcase("foo");
777         assert_eq!(f.to_string(), "function %foo() fast {\n}\n");
778 
779         f.create_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 4));
780         assert_eq!(
781             f.to_string(),
782             "function %foo() fast {\n    ss0 = explicit_slot 4\n}\n"
783         );
784 
785         let block = f.dfg.make_block();
786         f.layout.append_block(block);
787         assert_eq!(
788             f.to_string(),
789             "function %foo() fast {\n    ss0 = explicit_slot 4\n\nblock0:\n}\n"
790         );
791 
792         f.dfg.append_block_param(block, types::I8);
793         assert_eq!(
794             f.to_string(),
795             "function %foo() fast {\n    ss0 = explicit_slot 4\n\nblock0(v0: i8):\n}\n"
796         );
797 
798         f.dfg.append_block_param(block, types::F32.by(4).unwrap());
799         assert_eq!(
800             f.to_string(),
801             "function %foo() fast {\n    ss0 = explicit_slot 4\n\nblock0(v0: i8, v1: f32x4):\n}\n"
802         );
803 
804         {
805             let mut cursor = FuncCursor::new(&mut f);
806             cursor.set_position(CursorPosition::After(block));
807             cursor.ins().return_(&[])
808         };
809         assert_eq!(
810             f.to_string(),
811             "function %foo() fast {\n    ss0 = explicit_slot 4\n\nblock0(v0: i8, v1: f32x4):\n    return\n}\n"
812         );
813     }
814 
815     #[test]
aliases()816     fn aliases() {
817         use crate::ir::InstBuilder;
818 
819         let mut func = Function::new();
820         {
821             let block0 = func.dfg.make_block();
822             let mut pos = FuncCursor::new(&mut func);
823             pos.insert_block(block0);
824 
825             // make some detached values for change_to_alias
826             let v0 = pos.func.dfg.append_block_param(block0, types::I32);
827             let v1 = pos.func.dfg.append_block_param(block0, types::I32);
828             let v2 = pos.func.dfg.append_block_param(block0, types::I32);
829             pos.func.dfg.detach_block_params(block0);
830 
831             // alias to a param--will be printed at beginning of block defining param
832             let v3 = pos.func.dfg.append_block_param(block0, types::I32);
833             pos.func.dfg.change_to_alias(v0, v3);
834 
835             // alias to an alias--should print attached to alias, not ultimate target
836             pos.func.dfg.make_value_alias_for_serialization(v0, v2); // v0 <- v2
837 
838             // alias to a result--will be printed after instruction producing result
839             let _dummy0 = pos.ins().iconst(types::I32, 42);
840             let v4 = pos.ins().iadd(v0, v0);
841             pos.func.dfg.change_to_alias(v1, v4);
842             let _dummy1 = pos.ins().iconst(types::I32, 23);
843             let _v7 = pos.ins().iadd(v1, v1);
844         }
845         assert_eq!(
846             func.to_string(),
847             "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"
848         );
849     }
850 }
851