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