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