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 {
418             destination,
419             ref args,
420             ..
421         } => {
422             write!(w, " {}", destination)?;
423             write_block_args(w, args.as_slice(pool))
424         }
425         Branch {
426             destination,
427             ref args,
428             ..
429         } => {
430             let args = args.as_slice(pool);
431             write!(w, " {}, {}", args[0], destination)?;
432             write_block_args(w, &args[1..])
433         }
434         BranchTable {
435             arg,
436             destination,
437             table,
438             ..
439         } => write!(w, " {}, {}, {}", arg, destination, table),
440         Call {
441             func_ref, ref args, ..
442         } => write!(w, " {}({})", func_ref, DisplayValues(args.as_slice(pool))),
443         CallIndirect {
444             sig_ref, ref args, ..
445         } => {
446             let args = args.as_slice(pool);
447             write!(
448                 w,
449                 " {}, {}({})",
450                 sig_ref,
451                 args[0],
452                 DisplayValues(&args[1..])
453             )
454         }
455         FuncAddr { func_ref, .. } => write!(w, " {}", func_ref),
456         StackLoad {
457             stack_slot, offset, ..
458         } => write!(w, " {}{}", stack_slot, offset),
459         StackStore {
460             arg,
461             stack_slot,
462             offset,
463             ..
464         } => write!(w, " {}, {}{}", arg, stack_slot, offset),
465         DynamicStackLoad {
466             dynamic_stack_slot, ..
467         } => write!(w, " {}", dynamic_stack_slot),
468         DynamicStackStore {
469             arg,
470             dynamic_stack_slot,
471             ..
472         } => write!(w, " {}, {}", arg, dynamic_stack_slot),
473         TableAddr { table, arg, .. } => write!(w, " {}, {}", table, arg),
474         Load {
475             flags, arg, offset, ..
476         } => write!(w, "{} {}{}", flags, arg, offset),
477         Store {
478             flags,
479             args,
480             offset,
481             ..
482         } => write!(w, "{} {}, {}{}", flags, args[0], args[1], offset),
483         Trap { code, .. } => write!(w, " {}", code),
484         CondTrap { arg, code, .. } => write!(w, " {}, {}", arg, code),
485     }?;
486 
487     let mut sep = "  ; ";
488     for &arg in dfg.inst_args(inst) {
489         if let ValueDef::Result(src, _) = dfg.value_def(arg) {
490             let imm = match dfg.insts[src] {
491                 UnaryImm { imm, .. } => imm.to_string(),
492                 UnaryIeee32 { imm, .. } => imm.to_string(),
493                 UnaryIeee64 { imm, .. } => imm.to_string(),
494                 UnaryConst {
495                     constant_handle, ..
496                 } => constant_handle.to_string(),
497                 _ => continue,
498             };
499             write!(w, "{}{} = {}", sep, arg, imm)?;
500             sep = ", ";
501         }
502     }
503     Ok(())
504 }
505 
506 /// Write block args using optional parantheses.
507 fn write_block_args(w: &mut dyn Write, args: &[Value]) -> fmt::Result {
508     if args.is_empty() {
509         Ok(())
510     } else {
511         write!(w, "({})", DisplayValues(args))
512     }
513 }
514 
515 /// Displayable slice of values.
516 struct DisplayValues<'a>(&'a [Value]);
517 
518 impl<'a> fmt::Display for DisplayValues<'a> {
519     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
520         for (i, val) in self.0.iter().enumerate() {
521             if i == 0 {
522                 write!(f, "{}", val)?;
523             } else {
524                 write!(f, ", {}", val)?;
525             }
526         }
527         Ok(())
528     }
529 }
530 
531 struct DisplayValuesWithDelimiter<'a>(&'a [Value], char);
532 
533 impl<'a> fmt::Display for DisplayValuesWithDelimiter<'a> {
534     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
535         for (i, val) in self.0.iter().enumerate() {
536             if i == 0 {
537                 write!(f, "{}", val)?;
538             } else {
539                 write!(f, "{}{}", self.1, val)?;
540             }
541         }
542         Ok(())
543     }
544 }
545 
546 #[cfg(test)]
547 mod tests {
548     use crate::cursor::{Cursor, CursorPosition, FuncCursor};
549     use crate::ir::types;
550     use crate::ir::{Function, InstBuilder, StackSlotData, StackSlotKind, UserFuncName};
551     use alloc::string::ToString;
552 
553     #[test]
554     fn basic() {
555         let mut f = Function::new();
556         assert_eq!(f.to_string(), "function u0:0() fast {\n}\n");
557 
558         f.name = UserFuncName::testcase("foo");
559         assert_eq!(f.to_string(), "function %foo() fast {\n}\n");
560 
561         f.create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 4));
562         assert_eq!(
563             f.to_string(),
564             "function %foo() fast {\n    ss0 = explicit_slot 4\n}\n"
565         );
566 
567         let block = f.dfg.make_block();
568         f.layout.append_block(block);
569         assert_eq!(
570             f.to_string(),
571             "function %foo() fast {\n    ss0 = explicit_slot 4\n\nblock0:\n}\n"
572         );
573 
574         f.dfg.append_block_param(block, types::I8);
575         assert_eq!(
576             f.to_string(),
577             "function %foo() fast {\n    ss0 = explicit_slot 4\n\nblock0(v0: i8):\n}\n"
578         );
579 
580         f.dfg.append_block_param(block, types::F32.by(4).unwrap());
581         assert_eq!(
582             f.to_string(),
583             "function %foo() fast {\n    ss0 = explicit_slot 4\n\nblock0(v0: i8, v1: f32x4):\n}\n"
584         );
585 
586         {
587             let mut cursor = FuncCursor::new(&mut f);
588             cursor.set_position(CursorPosition::After(block));
589             cursor.ins().return_(&[])
590         };
591         assert_eq!(
592             f.to_string(),
593             "function %foo() fast {\n    ss0 = explicit_slot 4\n\nblock0(v0: i8, v1: f32x4):\n    return\n}\n"
594         );
595     }
596 
597     #[test]
598     fn aliases() {
599         use crate::ir::InstBuilder;
600 
601         let mut func = Function::new();
602         {
603             let block0 = func.dfg.make_block();
604             let mut pos = FuncCursor::new(&mut func);
605             pos.insert_block(block0);
606 
607             // make some detached values for change_to_alias
608             let v0 = pos.func.dfg.append_block_param(block0, types::I32);
609             let v1 = pos.func.dfg.append_block_param(block0, types::I32);
610             let v2 = pos.func.dfg.append_block_param(block0, types::I32);
611             pos.func.dfg.detach_block_params(block0);
612 
613             // alias to a param--will be printed at beginning of block defining param
614             let v3 = pos.func.dfg.append_block_param(block0, types::I32);
615             pos.func.dfg.change_to_alias(v0, v3);
616 
617             // alias to an alias--should print attached to alias, not ultimate target
618             pos.func.dfg.make_value_alias_for_serialization(v0, v2); // v0 <- v2
619 
620             // alias to a result--will be printed after instruction producing result
621             let _dummy0 = pos.ins().iconst(types::I32, 42);
622             let v4 = pos.ins().iadd(v0, v0);
623             pos.func.dfg.change_to_alias(v1, v4);
624             let _dummy1 = pos.ins().iconst(types::I32, 23);
625             let _v7 = pos.ins().iadd(v1, v1);
626         }
627         assert_eq!(
628             func.to_string(),
629             "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"
630         );
631     }
632 
633     #[test]
634     fn cold_blocks() {
635         let mut func = Function::new();
636         {
637             let mut pos = FuncCursor::new(&mut func);
638 
639             let block0 = pos.func.dfg.make_block();
640             pos.insert_block(block0);
641             pos.func.layout.set_cold(block0);
642 
643             let block1 = pos.func.dfg.make_block();
644             pos.insert_block(block1);
645             pos.func.dfg.append_block_param(block1, types::I32);
646             pos.func.layout.set_cold(block1);
647         }
648 
649         assert_eq!(
650             func.to_string(),
651             "function u0:0() fast {\nblock0 cold:\n\nblock1(v0: i32) cold:\n}\n"
652         );
653     }
654 }
655