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::{Block, DataFlowGraph, Function, Inst, SigRef, Type, Value, ValueDef};
9 use crate::packed_option::ReservedValue;
10 use alloc::string::{String, ToString};
11 use alloc::vec::Vec;
12 use core::fmt::{self, Write};
13 
14 /// A `FuncWriter` used to decorate functions during printing.
15 pub trait FuncWriter {
16     /// Write the basic block header for the current function.
17     fn write_block_header(
18         &mut self,
19         w: &mut dyn Write,
20         func: &Function,
21         block: Block,
22         indent: usize,
23     ) -> fmt::Result;
24 
25     /// Write the given `inst` to `w`.
26     fn write_instruction(
27         &mut self,
28         w: &mut dyn Write,
29         func: &Function,
30         aliases: &SecondaryMap<Value, Vec<Value>>,
31         inst: Inst,
32         indent: usize,
33     ) -> fmt::Result;
34 
35     /// Write the preamble to `w`. By default, this uses `write_entity_definition`.
36     fn write_preamble(&mut self, w: &mut dyn Write, func: &Function) -> Result<bool, fmt::Error> {
37         self.super_preamble(w, func)
38     }
39 
40     /// Default impl of `write_preamble`
41     fn super_preamble(&mut self, w: &mut dyn Write, func: &Function) -> Result<bool, fmt::Error> {
42         let mut any = false;
43 
44         for (ss, slot) in func.dynamic_stack_slots.iter() {
45             any = true;
46             self.write_entity_definition(w, func, ss.into(), slot)?;
47         }
48 
49         for (ss, slot) in func.sized_stack_slots.iter() {
50             any = true;
51             self.write_entity_definition(w, func, ss.into(), slot)?;
52         }
53 
54         for (gv, gv_data) in &func.global_values {
55             any = true;
56             self.write_entity_definition(w, func, gv.into(), gv_data)?;
57         }
58 
59         for (table, table_data) in &func.tables {
60             if !table_data.index_type.is_invalid() {
61                 any = true;
62                 self.write_entity_definition(w, func, table.into(), table_data)?;
63             }
64         }
65 
66         // Write out all signatures before functions since function declarations can refer to
67         // signatures.
68         for (sig, sig_data) in &func.dfg.signatures {
69             any = true;
70             self.write_entity_definition(w, func, sig.into(), &sig_data)?;
71         }
72 
73         for (fnref, ext_func) in &func.dfg.ext_funcs {
74             if ext_func.signature != SigRef::reserved_value() {
75                 any = true;
76                 self.write_entity_definition(
77                     w,
78                     func,
79                     fnref.into(),
80                     &ext_func.display(Some(&func.params)),
81                 )?;
82             }
83         }
84 
85         for (jt, jt_data) in &func.jump_tables {
86             any = true;
87             self.write_entity_definition(w, func, jt.into(), jt_data)?;
88         }
89 
90         for (&cref, cval) in func.dfg.constants.iter() {
91             any = true;
92             self.write_entity_definition(w, func, cref.into(), cval)?;
93         }
94 
95         if let Some(limit) = func.stack_limit {
96             any = true;
97             self.write_entity_definition(w, func, AnyEntity::StackLimit, &limit)?;
98         }
99 
100         Ok(any)
101     }
102 
103     /// Write an entity definition defined in the preamble to `w`.
104     fn write_entity_definition(
105         &mut self,
106         w: &mut dyn Write,
107         func: &Function,
108         entity: AnyEntity,
109         value: &dyn fmt::Display,
110     ) -> fmt::Result {
111         self.super_entity_definition(w, func, entity, value)
112     }
113 
114     /// Default impl of `write_entity_definition`
115     #[allow(unused_variables)]
116     fn super_entity_definition(
117         &mut self,
118         w: &mut dyn Write,
119         func: &Function,
120         entity: AnyEntity,
121         value: &dyn fmt::Display,
122     ) -> fmt::Result {
123         writeln!(w, "    {} = {}", entity, value)
124     }
125 }
126 
127 /// A `PlainWriter` that doesn't decorate the function.
128 pub struct PlainWriter;
129 
130 impl FuncWriter for PlainWriter {
131     fn write_instruction(
132         &mut self,
133         w: &mut dyn Write,
134         func: &Function,
135         aliases: &SecondaryMap<Value, Vec<Value>>,
136         inst: Inst,
137         indent: usize,
138     ) -> fmt::Result {
139         write_instruction(w, func, aliases, inst, indent)
140     }
141 
142     fn write_block_header(
143         &mut self,
144         w: &mut dyn Write,
145         func: &Function,
146         block: Block,
147         indent: usize,
148     ) -> fmt::Result {
149         write_block_header(w, func, block, indent)
150     }
151 }
152 
153 /// Write `func` to `w` as equivalent text.
154 /// Use `isa` to emit ISA-dependent annotations.
155 pub fn write_function(w: &mut dyn Write, func: &Function) -> fmt::Result {
156     decorate_function(&mut PlainWriter, w, func)
157 }
158 
159 /// Create a reverse-alias map from a value to all aliases having that value as a direct target
160 fn alias_map(func: &Function) -> SecondaryMap<Value, Vec<Value>> {
161     let mut aliases = SecondaryMap::<_, Vec<_>>::new();
162     for v in func.dfg.values() {
163         // VADFS returns the immediate target of an alias
164         if let Some(k) = func.dfg.value_alias_dest_for_serialization(v) {
165             aliases[k].push(v);
166         }
167     }
168     aliases
169 }
170 
171 /// Writes `func` to `w` as text.
172 /// write_function_plain is passed as 'closure' to print instructions as text.
173 /// pretty_function_error is passed as 'closure' to add error decoration.
174 pub fn decorate_function<FW: FuncWriter>(
175     func_w: &mut FW,
176     w: &mut dyn Write,
177     func: &Function,
178 ) -> fmt::Result {
179     write!(w, "function ")?;
180     write_spec(w, func)?;
181     writeln!(w, " {{")?;
182     let aliases = alias_map(func);
183     let mut any = func_w.write_preamble(w, func)?;
184     for block in &func.layout {
185         if any {
186             writeln!(w)?;
187         }
188         decorate_block(func_w, w, func, &aliases, block)?;
189         any = true;
190     }
191     writeln!(w, "}}")
192 }
193 
194 //----------------------------------------------------------------------
195 //
196 // Function spec.
197 
198 fn write_spec(w: &mut dyn Write, func: &Function) -> fmt::Result {
199     write!(w, "{}{}", func.name, func.signature)
200 }
201 
202 //----------------------------------------------------------------------
203 //
204 // Basic blocks
205 
206 fn write_arg(w: &mut dyn Write, func: &Function, arg: Value) -> fmt::Result {
207     write!(w, "{}: {}", arg, func.dfg.value_type(arg))
208 }
209 
210 /// Write out the basic block header, outdented:
211 ///
212 ///    block1:
213 ///    block1(v1: i32):
214 ///    block10(v4: f64, v5: b1):
215 ///
216 pub fn write_block_header(
217     w: &mut dyn Write,
218     func: &Function,
219     block: Block,
220     indent: usize,
221 ) -> fmt::Result {
222     let cold = if func.layout.is_cold(block) {
223         " cold"
224     } else {
225         ""
226     };
227 
228     // The `indent` is the instruction indentation. block headers are 4 spaces out from that.
229     write!(w, "{1:0$}{2}", indent - 4, "", block)?;
230 
231     let mut args = func.dfg.block_params(block).iter().cloned();
232     match args.next() {
233         None => return writeln!(w, "{}:", cold),
234         Some(arg) => {
235             write!(w, "(")?;
236             write_arg(w, func, arg)?;
237         }
238     }
239     // Remaining arguments.
240     for arg in args {
241         write!(w, ", ")?;
242         write_arg(w, func, arg)?;
243     }
244     writeln!(w, "){}:", cold)
245 }
246 
247 fn decorate_block<FW: FuncWriter>(
248     func_w: &mut FW,
249     w: &mut dyn Write,
250     func: &Function,
251     aliases: &SecondaryMap<Value, Vec<Value>>,
252     block: Block,
253 ) -> fmt::Result {
254     // Indent all instructions if any srclocs are present.
255     let indent = if func.rel_srclocs().is_empty() { 4 } else { 36 };
256 
257     func_w.write_block_header(w, func, block, indent)?;
258     for a in func.dfg.block_params(block).iter().cloned() {
259         write_value_aliases(w, aliases, a, indent)?;
260     }
261 
262     for inst in func.layout.block_insts(block) {
263         func_w.write_instruction(w, func, aliases, inst, indent)?;
264     }
265 
266     Ok(())
267 }
268 
269 //----------------------------------------------------------------------
270 //
271 // Instructions
272 
273 // Should `inst` be printed with a type suffix?
274 //
275 // Polymorphic instructions may need a suffix indicating the value of the controlling type variable
276 // if it can't be trivially inferred.
277 //
278 fn type_suffix(func: &Function, inst: Inst) -> Option<Type> {
279     let inst_data = &func.dfg.insts[inst];
280     let constraints = inst_data.opcode().constraints();
281 
282     if !constraints.is_polymorphic() {
283         return None;
284     }
285 
286     // If the controlling type variable can be inferred from the type of the designated value input
287     // operand, we don't need the type suffix.
288     if constraints.use_typevar_operand() {
289         let ctrl_var = inst_data.typevar_operand(&func.dfg.value_lists).unwrap();
290         let def_block = match func.dfg.value_def(ctrl_var) {
291             ValueDef::Result(instr, _) => func.layout.inst_block(instr),
292             ValueDef::Param(block, _) => Some(block),
293             ValueDef::Union(..) => None,
294         };
295         if def_block.is_some() && def_block == func.layout.inst_block(inst) {
296             return None;
297         }
298     }
299 
300     let rtype = func.dfg.ctrl_typevar(inst);
301     assert!(
302         !rtype.is_invalid(),
303         "Polymorphic instruction must produce a result"
304     );
305     Some(rtype)
306 }
307 
308 /// Write out any aliases to the given target, including indirect aliases
309 fn write_value_aliases(
310     w: &mut dyn Write,
311     aliases: &SecondaryMap<Value, Vec<Value>>,
312     target: Value,
313     indent: usize,
314 ) -> fmt::Result {
315     let mut todo_stack = vec![target];
316     while let Some(target) = todo_stack.pop() {
317         for &a in &aliases[target] {
318             writeln!(w, "{1:0$}{2} -> {3}", indent, "", a, target)?;
319             todo_stack.push(a);
320         }
321     }
322 
323     Ok(())
324 }
325 
326 fn write_instruction(
327     w: &mut dyn Write,
328     func: &Function,
329     aliases: &SecondaryMap<Value, Vec<Value>>,
330     inst: Inst,
331     indent: usize,
332 ) -> fmt::Result {
333     // Prefix containing source location, encoding, and value locations.
334     let mut s = String::with_capacity(16);
335 
336     // Source location goes first.
337     let srcloc = func.srcloc(inst);
338     if !srcloc.is_default() {
339         write!(s, "{} ", srcloc)?;
340     }
341 
342     // Write out prefix and indent the instruction.
343     write!(w, "{1:0$}", indent, s)?;
344 
345     // Write out the result values, if any.
346     let mut has_results = false;
347     for r in func.dfg.inst_results(inst) {
348         if !has_results {
349             has_results = true;
350             write!(w, "{}", r)?;
351         } else {
352             write!(w, ", {}", r)?;
353         }
354     }
355     if has_results {
356         write!(w, " = ")?;
357     }
358 
359     // Then the opcode, possibly with a '.type' suffix.
360     let opcode = func.dfg.insts[inst].opcode();
361 
362     match type_suffix(func, inst) {
363         Some(suf) => write!(w, "{}.{}", opcode, suf)?,
364         None => write!(w, "{}", opcode)?,
365     }
366 
367     write_operands(w, &func.dfg, inst)?;
368     writeln!(w)?;
369 
370     // Value aliases come out on lines after the instruction defining the referent.
371     for r in func.dfg.inst_results(inst) {
372         write_value_aliases(w, aliases, *r, indent)?;
373     }
374     Ok(())
375 }
376 
377 /// Write the operands of `inst` to `w` with a prepended space.
378 pub fn write_operands(w: &mut dyn Write, dfg: &DataFlowGraph, inst: Inst) -> fmt::Result {
379     let pool = &dfg.value_lists;
380     use crate::ir::instructions::InstructionData::*;
381     match dfg.insts[inst] {
382         AtomicRmw { op, args, .. } => write!(w, " {} {}, {}", op, args[0], args[1]),
383         AtomicCas { args, .. } => write!(w, " {}, {}, {}", args[0], args[1], args[2]),
384         LoadNoOffset { flags, arg, .. } => write!(w, "{} {}", flags, arg),
385         StoreNoOffset { flags, args, .. } => write!(w, "{} {}, {}", flags, args[0], args[1]),
386         Unary { arg, .. } => write!(w, " {}", arg),
387         UnaryImm { imm, .. } => write!(w, " {}", imm),
388         UnaryIeee32 { imm, .. } => write!(w, " {}", imm),
389         UnaryIeee64 { imm, .. } => write!(w, " {}", imm),
390         UnaryGlobalValue { global_value, .. } => write!(w, " {}", global_value),
391         UnaryConst {
392             constant_handle, ..
393         } => write!(w, " {}", constant_handle),
394         Binary { args, .. } => write!(w, " {}, {}", args[0], args[1]),
395         BinaryImm8 { arg, imm, .. } => write!(w, " {}, {}", arg, imm),
396         BinaryImm64 { arg, imm, .. } => write!(w, " {}, {}", arg, imm),
397         Ternary { args, .. } => write!(w, " {}, {}, {}", args[0], args[1], args[2]),
398         MultiAry { ref args, .. } => {
399             if args.is_empty() {
400                 write!(w, "")
401             } else {
402                 write!(w, " {}", DisplayValues(args.as_slice(pool)))
403             }
404         }
405         NullAry { .. } => write!(w, " "),
406         TernaryImm8 { imm, args, .. } => write!(w, " {}, {}, {}", args[0], args[1], imm),
407         Shuffle { imm, args, .. } => {
408             let data = dfg.immediates.get(imm).expect(
409                 "Expected the shuffle mask to already be inserted into the immediates table",
410             );
411             write!(w, " {}, {}, {}", args[0], args[1], data)
412         }
413         IntCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]),
414         IntCompareImm { cond, arg, imm, .. } => write!(w, " {} {}, {}", cond, arg, imm),
415         IntAddTrap { args, code, .. } => write!(w, " {}, {}, {}", args[0], args[1], code),
416         FloatCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]),
417         Jump { destination, .. } => {
418             write!(w, " {}", destination.block(pool))?;
419             write_block_args(w, destination.args_slice(pool))
420         }
421         Branch {
422             arg, destination, ..
423         } => {
424             write!(w, " {}, {}", arg, destination.block(pool))?;
425             write_block_args(w, destination.args_slice(pool))
426         }
427         BranchTable {
428             arg,
429             destination,
430             table,
431             ..
432         } => write!(w, " {}, {}, {}", arg, destination, table),
433         Call {
434             func_ref, ref args, ..
435         } => write!(w, " {}({})", func_ref, DisplayValues(args.as_slice(pool))),
436         CallIndirect {
437             sig_ref, ref args, ..
438         } => {
439             let args = args.as_slice(pool);
440             write!(
441                 w,
442                 " {}, {}({})",
443                 sig_ref,
444                 args[0],
445                 DisplayValues(&args[1..])
446             )
447         }
448         FuncAddr { func_ref, .. } => write!(w, " {}", func_ref),
449         StackLoad {
450             stack_slot, offset, ..
451         } => write!(w, " {}{}", stack_slot, offset),
452         StackStore {
453             arg,
454             stack_slot,
455             offset,
456             ..
457         } => write!(w, " {}, {}{}", arg, stack_slot, offset),
458         DynamicStackLoad {
459             dynamic_stack_slot, ..
460         } => write!(w, " {}", dynamic_stack_slot),
461         DynamicStackStore {
462             arg,
463             dynamic_stack_slot,
464             ..
465         } => write!(w, " {}, {}", arg, dynamic_stack_slot),
466         TableAddr { table, arg, .. } => write!(w, " {}, {}", table, arg),
467         Load {
468             flags, arg, offset, ..
469         } => write!(w, "{} {}{}", flags, arg, offset),
470         Store {
471             flags,
472             args,
473             offset,
474             ..
475         } => write!(w, "{} {}, {}{}", flags, args[0], args[1], offset),
476         Trap { code, .. } => write!(w, " {}", code),
477         CondTrap { arg, code, .. } => write!(w, " {}, {}", arg, code),
478     }?;
479 
480     let mut sep = "  ; ";
481     for arg in dfg.inst_values(inst) {
482         if let ValueDef::Result(src, _) = dfg.value_def(arg) {
483             let imm = match dfg.insts[src] {
484                 UnaryImm { imm, .. } => imm.to_string(),
485                 UnaryIeee32 { imm, .. } => imm.to_string(),
486                 UnaryIeee64 { imm, .. } => imm.to_string(),
487                 UnaryConst {
488                     constant_handle, ..
489                 } => constant_handle.to_string(),
490                 _ => continue,
491             };
492             write!(w, "{}{} = {}", sep, arg, imm)?;
493             sep = ", ";
494         }
495     }
496     Ok(())
497 }
498 
499 /// Write block args using optional parantheses.
500 fn write_block_args(w: &mut dyn Write, args: &[Value]) -> fmt::Result {
501     if args.is_empty() {
502         Ok(())
503     } else {
504         write!(w, "({})", DisplayValues(args))
505     }
506 }
507 
508 /// Displayable slice of values.
509 struct DisplayValues<'a>(&'a [Value]);
510 
511 impl<'a> fmt::Display for DisplayValues<'a> {
512     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
513         for (i, val) in self.0.iter().enumerate() {
514             if i == 0 {
515                 write!(f, "{}", val)?;
516             } else {
517                 write!(f, ", {}", val)?;
518             }
519         }
520         Ok(())
521     }
522 }
523 
524 struct DisplayValuesWithDelimiter<'a>(&'a [Value], char);
525 
526 impl<'a> fmt::Display for DisplayValuesWithDelimiter<'a> {
527     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
528         for (i, val) in self.0.iter().enumerate() {
529             if i == 0 {
530                 write!(f, "{}", val)?;
531             } else {
532                 write!(f, "{}{}", self.1, val)?;
533             }
534         }
535         Ok(())
536     }
537 }
538 
539 #[cfg(test)]
540 mod tests {
541     use crate::cursor::{Cursor, CursorPosition, FuncCursor};
542     use crate::ir::types;
543     use crate::ir::{Function, InstBuilder, StackSlotData, StackSlotKind, UserFuncName};
544     use alloc::string::ToString;
545 
546     #[test]
547     fn basic() {
548         let mut f = Function::new();
549         assert_eq!(f.to_string(), "function u0:0() fast {\n}\n");
550 
551         f.name = UserFuncName::testcase("foo");
552         assert_eq!(f.to_string(), "function %foo() fast {\n}\n");
553 
554         f.create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 4));
555         assert_eq!(
556             f.to_string(),
557             "function %foo() fast {\n    ss0 = explicit_slot 4\n}\n"
558         );
559 
560         let block = f.dfg.make_block();
561         f.layout.append_block(block);
562         assert_eq!(
563             f.to_string(),
564             "function %foo() fast {\n    ss0 = explicit_slot 4\n\nblock0:\n}\n"
565         );
566 
567         f.dfg.append_block_param(block, types::I8);
568         assert_eq!(
569             f.to_string(),
570             "function %foo() fast {\n    ss0 = explicit_slot 4\n\nblock0(v0: i8):\n}\n"
571         );
572 
573         f.dfg.append_block_param(block, types::F32.by(4).unwrap());
574         assert_eq!(
575             f.to_string(),
576             "function %foo() fast {\n    ss0 = explicit_slot 4\n\nblock0(v0: i8, v1: f32x4):\n}\n"
577         );
578 
579         {
580             let mut cursor = FuncCursor::new(&mut f);
581             cursor.set_position(CursorPosition::After(block));
582             cursor.ins().return_(&[])
583         };
584         assert_eq!(
585             f.to_string(),
586             "function %foo() fast {\n    ss0 = explicit_slot 4\n\nblock0(v0: i8, v1: f32x4):\n    return\n}\n"
587         );
588     }
589 
590     #[test]
591     fn aliases() {
592         use crate::ir::InstBuilder;
593 
594         let mut func = Function::new();
595         {
596             let block0 = func.dfg.make_block();
597             let mut pos = FuncCursor::new(&mut func);
598             pos.insert_block(block0);
599 
600             // make some detached values for change_to_alias
601             let v0 = pos.func.dfg.append_block_param(block0, types::I32);
602             let v1 = pos.func.dfg.append_block_param(block0, types::I32);
603             let v2 = pos.func.dfg.append_block_param(block0, types::I32);
604             pos.func.dfg.detach_block_params(block0);
605 
606             // alias to a param--will be printed at beginning of block defining param
607             let v3 = pos.func.dfg.append_block_param(block0, types::I32);
608             pos.func.dfg.change_to_alias(v0, v3);
609 
610             // alias to an alias--should print attached to alias, not ultimate target
611             pos.func.dfg.make_value_alias_for_serialization(v0, v2); // v0 <- v2
612 
613             // alias to a result--will be printed after instruction producing result
614             let _dummy0 = pos.ins().iconst(types::I32, 42);
615             let v4 = pos.ins().iadd(v0, v0);
616             pos.func.dfg.change_to_alias(v1, v4);
617             let _dummy1 = pos.ins().iconst(types::I32, 23);
618             let _v7 = pos.ins().iadd(v1, v1);
619         }
620         assert_eq!(
621             func.to_string(),
622             "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"
623         );
624     }
625 
626     #[test]
627     fn cold_blocks() {
628         let mut func = Function::new();
629         {
630             let mut pos = FuncCursor::new(&mut func);
631 
632             let block0 = pos.func.dfg.make_block();
633             pos.insert_block(block0);
634             pos.func.layout.set_cold(block0);
635 
636             let block1 = pos.func.dfg.make_block();
637             pos.insert_block(block1);
638             pos.func.dfg.append_block_param(block1, types::I32);
639             pos.func.layout.set_cold(block1);
640         }
641 
642         assert_eq!(
643             func.to_string(),
644             "function u0:0() fast {\nblock0 cold:\n\nblock1(v0: i32) cold:\n}\n"
645         );
646     }
647 }
648