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::{LabelValueLoc, 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: LabelValueLoc, regs: &RegInfo) -> fmt::Result281 fn write_valueloc(w: &mut dyn Write, loc: LabelValueLoc, regs: &RegInfo) -> fmt::Result {
282     match loc {
283         LabelValueLoc::ValueLoc(ValueLoc::Reg(r)) => write!(w, "{}", regs.display_regunit(r)),
284         LabelValueLoc::ValueLoc(ValueLoc::Stack(ss)) => write!(w, "{}", ss),
285         LabelValueLoc::ValueLoc(ValueLoc::Unassigned) => write!(w, "?"),
286         LabelValueLoc::Reg(r) => write!(w, "{:?}", r),
287         LabelValueLoc::SPOffset(off) => write!(w, "[sp+{}]", off),
288     }
289 }
290 
write_value_range_markers( w: &mut dyn Write, val_ranges: &ValueLabelsRanges, regs: &RegInfo, offset: u32, indent: usize, ) -> fmt::Result291 fn write_value_range_markers(
292     w: &mut dyn Write,
293     val_ranges: &ValueLabelsRanges,
294     regs: &RegInfo,
295     offset: u32,
296     indent: usize,
297 ) -> fmt::Result {
298     let mut result = String::new();
299     let mut shown = HashSet::new();
300     for (val, rng) in val_ranges {
301         for i in (0..rng.len()).rev() {
302             if rng[i].start == offset {
303                 write!(&mut result, " {}@", val)?;
304                 write_valueloc(&mut result, rng[i].loc, regs)?;
305                 shown.insert(val);
306                 break;
307             }
308         }
309     }
310     for (val, rng) in val_ranges {
311         for i in (0..rng.len()).rev() {
312             if rng[i].end == offset && !shown.contains(val) {
313                 write!(&mut result, " {}\u{2620}", val)?;
314                 break;
315             }
316         }
317     }
318     if !result.is_empty() {
319         writeln!(w, ";{1:0$}; {2}", indent + 24, "", result)?;
320     }
321     Ok(())
322 }
323 
decorate_block<FW: FuncWriter>( func_w: &mut FW, w: &mut dyn Write, func: &Function, aliases: &SecondaryMap<Value, Vec<Value>>, annotations: &DisplayFunctionAnnotations, block: Block, ) -> fmt::Result324 fn decorate_block<FW: FuncWriter>(
325     func_w: &mut FW,
326     w: &mut dyn Write,
327     func: &Function,
328     aliases: &SecondaryMap<Value, Vec<Value>>,
329     annotations: &DisplayFunctionAnnotations,
330     block: Block,
331 ) -> fmt::Result {
332     // Indent all instructions if any encodings are present.
333     let indent = if func.encodings.is_empty() && func.srclocs.is_empty() {
334         4
335     } else {
336         36
337     };
338     let isa = annotations.isa;
339 
340     func_w.write_block_header(w, func, isa, block, indent)?;
341     for a in func.dfg.block_params(block).iter().cloned() {
342         write_value_aliases(w, aliases, a, indent)?;
343     }
344 
345     if let Some(isa) = isa {
346         if !func.offsets.is_empty() {
347             let encinfo = isa.encoding_info();
348             let regs = &isa.register_info();
349             for (offset, inst, size) in func.inst_offsets(block, &encinfo) {
350                 func_w.write_instruction(w, func, aliases, Some(isa), inst, indent)?;
351                 if size > 0 {
352                     if let Some(val_ranges) = annotations.value_ranges {
353                         write_value_range_markers(w, val_ranges, regs, offset + size, indent)?;
354                     }
355                 }
356             }
357             return Ok(());
358         }
359     }
360 
361     for inst in func.layout.block_insts(block) {
362         func_w.write_instruction(w, func, aliases, isa, inst, indent)?;
363     }
364 
365     Ok(())
366 }
367 
368 //----------------------------------------------------------------------
369 //
370 // Instructions
371 
372 // Should `inst` be printed with a type suffix?
373 //
374 // Polymorphic instructions may need a suffix indicating the value of the controlling type variable
375 // if it can't be trivially inferred.
376 //
type_suffix(func: &Function, inst: Inst) -> Option<Type>377 fn type_suffix(func: &Function, inst: Inst) -> Option<Type> {
378     let inst_data = &func.dfg[inst];
379     let constraints = inst_data.opcode().constraints();
380 
381     if !constraints.is_polymorphic() {
382         return None;
383     }
384 
385     // If the controlling type variable can be inferred from the type of the designated value input
386     // operand, we don't need the type suffix.
387     if constraints.use_typevar_operand() {
388         let ctrl_var = inst_data.typevar_operand(&func.dfg.value_lists).unwrap();
389         let def_block = match func.dfg.value_def(ctrl_var) {
390             ValueDef::Result(instr, _) => func.layout.inst_block(instr),
391             ValueDef::Param(block, _) => Some(block),
392         };
393         if def_block.is_some() && def_block == func.layout.inst_block(inst) {
394             return None;
395         }
396     }
397 
398     let rtype = func.dfg.ctrl_typevar(inst);
399     assert!(
400         !rtype.is_invalid(),
401         "Polymorphic instruction must produce a result"
402     );
403     Some(rtype)
404 }
405 
406 /// 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::Result407 fn write_value_aliases(
408     w: &mut dyn Write,
409     aliases: &SecondaryMap<Value, Vec<Value>>,
410     target: Value,
411     indent: usize,
412 ) -> fmt::Result {
413     let mut todo_stack = vec![target];
414     while let Some(target) = todo_stack.pop() {
415         for &a in &aliases[target] {
416             writeln!(w, "{1:0$}{2} -> {3}", indent, "", a, target)?;
417             todo_stack.push(a);
418         }
419     }
420 
421     Ok(())
422 }
423 
write_instruction( w: &mut dyn Write, func: &Function, aliases: &SecondaryMap<Value, Vec<Value>>, isa: Option<&dyn TargetIsa>, inst: Inst, indent: usize, ) -> fmt::Result424 fn write_instruction(
425     w: &mut dyn Write,
426     func: &Function,
427     aliases: &SecondaryMap<Value, Vec<Value>>,
428     isa: Option<&dyn TargetIsa>,
429     inst: Inst,
430     indent: usize,
431 ) -> fmt::Result {
432     // Prefix containing source location, encoding, and value locations.
433     let mut s = String::with_capacity(16);
434 
435     // Source location goes first.
436     let srcloc = func.srclocs[inst];
437     if !srcloc.is_default() {
438         write!(s, "{} ", srcloc)?;
439     }
440 
441     // Write out encoding info.
442     if let Some(enc) = func.encodings.get(inst).cloned() {
443         if let Some(isa) = isa {
444             write!(s, "[{}", isa.encoding_info().display(enc))?;
445             // Write value locations, if we have them.
446             if !func.locations.is_empty() {
447                 let regs = isa.register_info();
448                 for &r in func.dfg.inst_results(inst) {
449                     write!(s, ",{}", func.locations[r].display(&regs))?
450                 }
451             }
452             write!(s, "] ")?;
453         } else {
454             write!(s, "[{}] ", enc)?;
455         }
456     }
457 
458     // Write out prefix and indent the instruction.
459     write!(w, "{1:0$}", indent, s)?;
460 
461     // Write out the result values, if any.
462     let mut has_results = false;
463     for r in func.dfg.inst_results(inst) {
464         if !has_results {
465             has_results = true;
466             write!(w, "{}", r)?;
467         } else {
468             write!(w, ", {}", r)?;
469         }
470     }
471     if has_results {
472         write!(w, " = ")?;
473     }
474 
475     // Then the opcode, possibly with a '.type' suffix.
476     let opcode = func.dfg[inst].opcode();
477 
478     match type_suffix(func, inst) {
479         Some(suf) => write!(w, "{}.{}", opcode, suf)?,
480         None => write!(w, "{}", opcode)?,
481     }
482 
483     write_operands(w, &func.dfg, isa, inst)?;
484     writeln!(w)?;
485 
486     // Value aliases come out on lines after the instruction defining the referent.
487     for r in func.dfg.inst_results(inst) {
488         write_value_aliases(w, aliases, *r, indent)?;
489     }
490     Ok(())
491 }
492 
493 /// 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::Result494 pub fn write_operands(
495     w: &mut dyn Write,
496     dfg: &DataFlowGraph,
497     isa: Option<&dyn TargetIsa>,
498     inst: Inst,
499 ) -> fmt::Result {
500     let pool = &dfg.value_lists;
501     use crate::ir::instructions::InstructionData::*;
502     match dfg[inst] {
503         AtomicRmw { op, args, .. } => write!(w, " {}, {}, {}", op, args[0], args[1]),
504         AtomicCas { args, .. } => write!(w, " {}, {}, {}", args[0], args[1], args[2]),
505         LoadNoOffset { flags, arg, .. } => write!(w, "{} {}", flags, arg),
506         StoreNoOffset { flags, args, .. } => write!(w, "{} {}, {}", flags, args[0], args[1]),
507         Unary { arg, .. } => write!(w, " {}", arg),
508         UnaryImm { imm, .. } => write!(w, " {}", imm),
509         UnaryIeee32 { imm, .. } => write!(w, " {}", imm),
510         UnaryIeee64 { imm, .. } => write!(w, " {}", imm),
511         UnaryBool { imm, .. } => write!(w, " {}", imm),
512         UnaryGlobalValue { global_value, .. } => write!(w, " {}", global_value),
513         UnaryConst {
514             constant_handle, ..
515         } => write!(w, " {}", constant_handle),
516         Binary { args, .. } => write!(w, " {}, {}", args[0], args[1]),
517         BinaryImm8 { arg, imm, .. } => write!(w, " {}, {}", arg, imm),
518         BinaryImm64 { arg, imm, .. } => write!(w, " {}, {}", arg, imm),
519         Ternary { args, .. } => write!(w, " {}, {}, {}", args[0], args[1], args[2]),
520         MultiAry { ref args, .. } => {
521             if args.is_empty() {
522                 write!(w, "")
523             } else {
524                 write!(w, " {}", DisplayValues(args.as_slice(pool)))
525             }
526         }
527         NullAry { .. } => write!(w, " "),
528         TernaryImm8 { imm, args, .. } => write!(w, " {}, {}, {}", args[0], args[1], imm),
529         Shuffle { mask, args, .. } => {
530             let data = dfg.immediates.get(mask).expect(
531                 "Expected the shuffle mask to already be inserted into the immediates table",
532             );
533             write!(w, " {}, {}, {}", args[0], args[1], data)
534         }
535         IntCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]),
536         IntCompareImm { cond, arg, imm, .. } => write!(w, " {} {}, {}", cond, arg, imm),
537         IntCond { cond, arg, .. } => write!(w, " {} {}", cond, arg),
538         FloatCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]),
539         FloatCond { cond, arg, .. } => write!(w, " {} {}", cond, arg),
540         IntSelect { cond, args, .. } => {
541             write!(w, " {} {}, {}, {}", cond, args[0], args[1], args[2])
542         }
543         Jump {
544             destination,
545             ref args,
546             ..
547         } => {
548             write!(w, " {}", destination)?;
549             write_block_args(w, args.as_slice(pool))
550         }
551         Branch {
552             destination,
553             ref args,
554             ..
555         } => {
556             let args = args.as_slice(pool);
557             write!(w, " {}, {}", args[0], destination)?;
558             write_block_args(w, &args[1..])
559         }
560         BranchInt {
561             cond,
562             destination,
563             ref args,
564             ..
565         } => {
566             let args = args.as_slice(pool);
567             write!(w, " {} {}, {}", cond, args[0], destination)?;
568             write_block_args(w, &args[1..])
569         }
570         BranchFloat {
571             cond,
572             destination,
573             ref args,
574             ..
575         } => {
576             let args = args.as_slice(pool);
577             write!(w, " {} {}, {}", cond, args[0], destination)?;
578             write_block_args(w, &args[1..])
579         }
580         BranchIcmp {
581             cond,
582             destination,
583             ref args,
584             ..
585         } => {
586             let args = args.as_slice(pool);
587             write!(w, " {} {}, {}, {}", cond, args[0], args[1], destination)?;
588             write_block_args(w, &args[2..])
589         }
590         BranchTable {
591             arg,
592             destination,
593             table,
594             ..
595         } => write!(w, " {}, {}, {}", arg, destination, table),
596         BranchTableBase { table, .. } => write!(w, " {}", table),
597         BranchTableEntry {
598             args, imm, table, ..
599         } => write!(w, " {}, {}, {}, {}", args[0], args[1], imm, table),
600         IndirectJump { arg, table, .. } => write!(w, " {}, {}", arg, table),
601         Call {
602             func_ref, ref args, ..
603         } => write!(w, " {}({})", func_ref, DisplayValues(args.as_slice(pool))),
604         CallIndirect {
605             sig_ref, ref args, ..
606         } => {
607             let args = args.as_slice(pool);
608             write!(
609                 w,
610                 " {}, {}({})",
611                 sig_ref,
612                 args[0],
613                 DisplayValues(&args[1..])
614             )
615         }
616         FuncAddr { func_ref, .. } => write!(w, " {}", func_ref),
617         StackLoad {
618             stack_slot, offset, ..
619         } => write!(w, " {}{}", stack_slot, offset),
620         StackStore {
621             arg,
622             stack_slot,
623             offset,
624             ..
625         } => write!(w, " {}, {}{}", arg, stack_slot, offset),
626         HeapAddr { heap, arg, imm, .. } => write!(w, " {}, {}, {}", heap, arg, imm),
627         TableAddr { table, arg, .. } => write!(w, " {}, {}", table, arg),
628         Load {
629             flags, arg, offset, ..
630         } => write!(w, "{} {}{}", flags, arg, offset),
631         LoadComplex {
632             flags,
633             ref args,
634             offset,
635             ..
636         } => {
637             let args = args.as_slice(pool);
638             write!(
639                 w,
640                 "{} {}{}",
641                 flags,
642                 DisplayValuesWithDelimiter(&args, '+'),
643                 offset
644             )
645         }
646         Store {
647             flags,
648             args,
649             offset,
650             ..
651         } => write!(w, "{} {}, {}{}", flags, args[0], args[1], offset),
652         StoreComplex {
653             flags,
654             ref args,
655             offset,
656             ..
657         } => {
658             let args = args.as_slice(pool);
659             write!(
660                 w,
661                 "{} {}, {}{}",
662                 flags,
663                 args[0],
664                 DisplayValuesWithDelimiter(&args[1..], '+'),
665                 offset
666             )
667         }
668         RegMove { arg, src, dst, .. } => {
669             if let Some(isa) = isa {
670                 let regs = isa.register_info();
671                 write!(
672                     w,
673                     " {}, {} -> {}",
674                     arg,
675                     regs.display_regunit(src),
676                     regs.display_regunit(dst)
677                 )
678             } else {
679                 write!(w, " {}, %{} -> %{}", arg, src, dst)
680             }
681         }
682         CopySpecial { src, dst, .. } => {
683             if let Some(isa) = isa {
684                 let regs = isa.register_info();
685                 write!(
686                     w,
687                     " {} -> {}",
688                     regs.display_regunit(src),
689                     regs.display_regunit(dst)
690                 )
691             } else {
692                 write!(w, " %{} -> %{}", src, dst)
693             }
694         }
695         CopyToSsa { src, .. } => {
696             if let Some(isa) = isa {
697                 let regs = isa.register_info();
698                 write!(w, " {}", regs.display_regunit(src))
699             } else {
700                 write!(w, " %{}", src)
701             }
702         }
703         RegSpill { arg, src, dst, .. } => {
704             if let Some(isa) = isa {
705                 let regs = isa.register_info();
706                 write!(w, " {}, {} -> {}", arg, regs.display_regunit(src), dst)
707             } else {
708                 write!(w, " {}, %{} -> {}", arg, src, dst)
709             }
710         }
711         RegFill { arg, src, dst, .. } => {
712             if let Some(isa) = isa {
713                 let regs = isa.register_info();
714                 write!(w, " {}, {} -> {}", arg, src, regs.display_regunit(dst))
715             } else {
716                 write!(w, " {}, {} -> %{}", arg, src, dst)
717             }
718         }
719         Trap { code, .. } => write!(w, " {}", code),
720         CondTrap { arg, code, .. } => write!(w, " {}, {}", arg, code),
721         IntCondTrap {
722             cond, arg, code, ..
723         } => write!(w, " {} {}, {}", cond, arg, code),
724         FloatCondTrap {
725             cond, arg, code, ..
726         } => write!(w, " {} {}, {}", cond, arg, code),
727     }
728 }
729 
730 /// Write block args using optional parantheses.
write_block_args(w: &mut dyn Write, args: &[Value]) -> fmt::Result731 fn write_block_args(w: &mut dyn Write, args: &[Value]) -> fmt::Result {
732     if args.is_empty() {
733         Ok(())
734     } else {
735         write!(w, "({})", DisplayValues(args))
736     }
737 }
738 
739 /// Displayable slice of values.
740 struct DisplayValues<'a>(&'a [Value]);
741 
742 impl<'a> fmt::Display for DisplayValues<'a> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result743     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
744         for (i, val) in self.0.iter().enumerate() {
745             if i == 0 {
746                 write!(f, "{}", val)?;
747             } else {
748                 write!(f, ", {}", val)?;
749             }
750         }
751         Ok(())
752     }
753 }
754 
755 struct DisplayValuesWithDelimiter<'a>(&'a [Value], char);
756 
757 impl<'a> fmt::Display for DisplayValuesWithDelimiter<'a> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result758     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
759         for (i, val) in self.0.iter().enumerate() {
760             if i == 0 {
761                 write!(f, "{}", val)?;
762             } else {
763                 write!(f, "{}{}", self.1, val)?;
764             }
765         }
766         Ok(())
767     }
768 }
769 
770 #[cfg(test)]
771 mod tests {
772     use crate::cursor::{Cursor, CursorPosition, FuncCursor};
773     use crate::ir::types;
774     use crate::ir::{ExternalName, Function, InstBuilder, StackSlotData, StackSlotKind};
775     use alloc::string::ToString;
776 
777     #[test]
basic()778     fn basic() {
779         let mut f = Function::new();
780         assert_eq!(f.to_string(), "function u0:0() fast {\n}\n");
781 
782         f.name = ExternalName::testcase("foo");
783         assert_eq!(f.to_string(), "function %foo() fast {\n}\n");
784 
785         f.create_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 4));
786         assert_eq!(
787             f.to_string(),
788             "function %foo() fast {\n    ss0 = explicit_slot 4\n}\n"
789         );
790 
791         let block = f.dfg.make_block();
792         f.layout.append_block(block);
793         assert_eq!(
794             f.to_string(),
795             "function %foo() fast {\n    ss0 = explicit_slot 4\n\nblock0:\n}\n"
796         );
797 
798         f.dfg.append_block_param(block, types::I8);
799         assert_eq!(
800             f.to_string(),
801             "function %foo() fast {\n    ss0 = explicit_slot 4\n\nblock0(v0: i8):\n}\n"
802         );
803 
804         f.dfg.append_block_param(block, types::F32.by(4).unwrap());
805         assert_eq!(
806             f.to_string(),
807             "function %foo() fast {\n    ss0 = explicit_slot 4\n\nblock0(v0: i8, v1: f32x4):\n}\n"
808         );
809 
810         {
811             let mut cursor = FuncCursor::new(&mut f);
812             cursor.set_position(CursorPosition::After(block));
813             cursor.ins().return_(&[])
814         };
815         assert_eq!(
816             f.to_string(),
817             "function %foo() fast {\n    ss0 = explicit_slot 4\n\nblock0(v0: i8, v1: f32x4):\n    return\n}\n"
818         );
819     }
820 
821     #[test]
aliases()822     fn aliases() {
823         use crate::ir::InstBuilder;
824 
825         let mut func = Function::new();
826         {
827             let block0 = func.dfg.make_block();
828             let mut pos = FuncCursor::new(&mut func);
829             pos.insert_block(block0);
830 
831             // make some detached values for change_to_alias
832             let v0 = pos.func.dfg.append_block_param(block0, types::I32);
833             let v1 = pos.func.dfg.append_block_param(block0, types::I32);
834             let v2 = pos.func.dfg.append_block_param(block0, types::I32);
835             pos.func.dfg.detach_block_params(block0);
836 
837             // alias to a param--will be printed at beginning of block defining param
838             let v3 = pos.func.dfg.append_block_param(block0, types::I32);
839             pos.func.dfg.change_to_alias(v0, v3);
840 
841             // alias to an alias--should print attached to alias, not ultimate target
842             pos.func.dfg.make_value_alias_for_serialization(v0, v2); // v0 <- v2
843 
844             // alias to a result--will be printed after instruction producing result
845             let _dummy0 = pos.ins().iconst(types::I32, 42);
846             let v4 = pos.ins().iadd(v0, v0);
847             pos.func.dfg.change_to_alias(v1, v4);
848             let _dummy1 = pos.ins().iconst(types::I32, 23);
849             let _v7 = pos.ins().iadd(v1, v1);
850         }
851         assert_eq!(
852             func.to_string(),
853             "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"
854         );
855     }
856 }
857