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