cranelift_codegen/
write.rs

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
6use crate::entity::SecondaryMap;
7use crate::ir::entities::AnyEntity;
8use crate::ir::{Block, DataFlowGraph, Function, Inst, SigRef, Type, Value, ValueDef};
9use crate::packed_option::ReservedValue;
10use alloc::string::{String, ToString};
11use alloc::vec::Vec;
12use core::fmt::{self, Write};
13
14/// A `FuncWriter` used to decorate functions during printing.
15pub 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.
123pub struct PlainWriter;
124
125impl 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.
150pub 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
155fn 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.
169pub 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
193fn 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
201fn 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///
211pub 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
242fn 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//
273fn 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
304fn 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
321fn 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.
373pub 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 { table, arg, .. } => write!(w, " {}, {}", table, arg),
461        Load {
462            flags, arg, offset, ..
463        } => write!(w, "{} {}{}", flags, arg, offset),
464        Store {
465            flags,
466            args,
467            offset,
468            ..
469        } => write!(w, "{} {}, {}{}", flags, args[0], args[1], offset),
470        Trap { code, .. } => write!(w, " {}", code),
471        CondTrap { arg, code, .. } => write!(w, " {}, {}", arg, code),
472    }?;
473
474    let mut sep = "  ; ";
475    for arg in dfg.inst_values(inst) {
476        if let ValueDef::Result(src, _) = dfg.value_def(arg) {
477            let imm = match dfg.insts[src] {
478                UnaryImm { imm, .. } => imm.to_string(),
479                UnaryIeee32 { imm, .. } => imm.to_string(),
480                UnaryIeee64 { imm, .. } => imm.to_string(),
481                UnaryConst {
482                    constant_handle, ..
483                } => constant_handle.to_string(),
484                _ => continue,
485            };
486            write!(w, "{}{} = {}", sep, arg, imm)?;
487            sep = ", ";
488        }
489    }
490    Ok(())
491}
492
493/// Displayable slice of values.
494struct DisplayValues<'a>(&'a [Value]);
495
496impl<'a> fmt::Display for DisplayValues<'a> {
497    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
498        for (i, val) in self.0.iter().enumerate() {
499            if i == 0 {
500                write!(f, "{}", val)?;
501            } else {
502                write!(f, ", {}", val)?;
503            }
504        }
505        Ok(())
506    }
507}
508
509#[cfg(test)]
510mod tests {
511    use crate::cursor::{Cursor, CursorPosition, FuncCursor};
512    use crate::ir::types;
513    use crate::ir::{Function, InstBuilder, StackSlotData, StackSlotKind, UserFuncName};
514    use alloc::string::ToString;
515
516    #[test]
517    fn basic() {
518        let mut f = Function::new();
519        assert_eq!(f.to_string(), "function u0:0() fast {\n}\n");
520
521        f.name = UserFuncName::testcase("foo");
522        assert_eq!(f.to_string(), "function %foo() fast {\n}\n");
523
524        f.create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 4));
525        assert_eq!(
526            f.to_string(),
527            "function %foo() fast {\n    ss0 = explicit_slot 4\n}\n"
528        );
529
530        let block = f.dfg.make_block();
531        f.layout.append_block(block);
532        assert_eq!(
533            f.to_string(),
534            "function %foo() fast {\n    ss0 = explicit_slot 4\n\nblock0:\n}\n"
535        );
536
537        f.dfg.append_block_param(block, types::I8);
538        assert_eq!(
539            f.to_string(),
540            "function %foo() fast {\n    ss0 = explicit_slot 4\n\nblock0(v0: i8):\n}\n"
541        );
542
543        f.dfg.append_block_param(block, types::F32.by(4).unwrap());
544        assert_eq!(
545            f.to_string(),
546            "function %foo() fast {\n    ss0 = explicit_slot 4\n\nblock0(v0: i8, v1: f32x4):\n}\n"
547        );
548
549        {
550            let mut cursor = FuncCursor::new(&mut f);
551            cursor.set_position(CursorPosition::After(block));
552            cursor.ins().return_(&[])
553        };
554        assert_eq!(
555            f.to_string(),
556            "function %foo() fast {\n    ss0 = explicit_slot 4\n\nblock0(v0: i8, v1: f32x4):\n    return\n}\n"
557        );
558    }
559
560    #[test]
561    fn aliases() {
562        use crate::ir::InstBuilder;
563
564        let mut func = Function::new();
565        {
566            let block0 = func.dfg.make_block();
567            let mut pos = FuncCursor::new(&mut func);
568            pos.insert_block(block0);
569
570            // make some detached values for change_to_alias
571            let v0 = pos.func.dfg.append_block_param(block0, types::I32);
572            let v1 = pos.func.dfg.append_block_param(block0, types::I32);
573            let v2 = pos.func.dfg.append_block_param(block0, types::I32);
574            pos.func.dfg.detach_block_params(block0);
575
576            // alias to a param--will be printed at beginning of block defining param
577            let v3 = pos.func.dfg.append_block_param(block0, types::I32);
578            pos.func.dfg.change_to_alias(v0, v3);
579
580            // alias to an alias--should print attached to alias, not ultimate target
581            pos.func.dfg.make_value_alias_for_serialization(v0, v2); // v0 <- v2
582
583            // alias to a result--will be printed after instruction producing result
584            let _dummy0 = pos.ins().iconst(types::I32, 42);
585            let v4 = pos.ins().iadd(v0, v0);
586            pos.func.dfg.change_to_alias(v1, v4);
587            let _dummy1 = pos.ins().iconst(types::I32, 23);
588            let _v7 = pos.ins().iadd(v1, v1);
589        }
590        assert_eq!(
591            func.to_string(),
592            "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"
593        );
594    }
595
596    #[test]
597    fn cold_blocks() {
598        let mut func = Function::new();
599        {
600            let mut pos = FuncCursor::new(&mut func);
601
602            let block0 = pos.func.dfg.make_block();
603            pos.insert_block(block0);
604            pos.func.layout.set_cold(block0);
605
606            let block1 = pos.func.dfg.make_block();
607            pos.insert_block(block1);
608            pos.func.dfg.append_block_param(block1, types::I32);
609            pos.func.layout.set_cold(block1);
610        }
611
612        assert_eq!(
613            func.to_string(),
614            "function u0:0() fast {\nblock0 cold:\n\nblock1(v0: i32) cold:\n}\n"
615        );
616    }
617}