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