Skip to main content

wave_compiler/mir/
display.rs

1// Copyright 2026 Ojima Abraham
2// SPDX-License-Identifier: Apache-2.0
3
4//! Pretty-printing for MIR functions and instructions.
5//!
6//! Provides human-readable output of the MIR for debugging and
7//! compiler development purposes.
8
9use std::fmt::{self, Write};
10
11use super::basic_block::Terminator;
12use super::function::MirFunction;
13use super::instruction::MirInst;
14
15/// Format a MIR function for display.
16#[must_use]
17pub fn display_function(func: &MirFunction) -> String {
18    let mut out = String::new();
19    let _ = writeln!(
20        out,
21        "function {} ({} params):",
22        func.name,
23        func.params.len()
24    );
25    for param in &func.params {
26        let _ = writeln!(
27            out,
28            "  param {} : {} ({})",
29            param.value, param.ty, param.name
30        );
31    }
32    out.push('\n');
33    for block in &func.blocks {
34        let _ = writeln!(out, "{}:", block.id);
35        for phi in &block.phis {
36            let _ = write!(out, "  {} = phi {}", phi.dest, phi.ty);
37            for (bid, val) in &phi.incoming {
38                let _ = write!(out, " [{bid}: {val}]");
39            }
40            out.push('\n');
41        }
42        for inst in &block.instructions {
43            let _ = writeln!(out, "  {}", format_inst(inst));
44        }
45        let _ = writeln!(out, "  {}", format_terminator(&block.terminator));
46    }
47    out
48}
49
50fn format_inst(inst: &MirInst) -> String {
51    match inst {
52        MirInst::BinOp {
53            dest,
54            op,
55            lhs,
56            rhs,
57            ty,
58        } => format!("{dest} = {op:?} {ty} {lhs}, {rhs}"),
59        MirInst::UnaryOp {
60            dest,
61            op,
62            operand,
63            ty,
64        } => format!("{dest} = {op:?} {ty} {operand}"),
65        MirInst::Load {
66            dest,
67            addr,
68            space,
69            ty,
70        } => format!("{dest} = load {ty} {space} [{addr}]"),
71        MirInst::Store { addr, value, space } => format!("store {space} [{addr}], {value}"),
72        MirInst::Call { dest, func, args } => {
73            let args_str: Vec<String> = args.iter().map(ToString::to_string).collect();
74            match dest {
75                Some(d) => format!("{d} = call {func:?}({args})", args = args_str.join(", ")),
76                None => format!("call {func:?}({args})", args = args_str.join(", ")),
77            }
78        }
79        MirInst::Cast {
80            dest,
81            value,
82            from,
83            to,
84        } => format!("{dest} = cast {from} -> {to} {value}"),
85        MirInst::Const { dest, value } => format!("{dest} = const {value:?}"),
86        MirInst::Shuffle {
87            dest,
88            value,
89            lane,
90            mode,
91        } => format!("{dest} = shuffle {mode:?} {value}, {lane}"),
92        MirInst::AtomicRmw {
93            dest,
94            addr,
95            value,
96            op,
97            ..
98        } => format!("{dest} = atomic_{op:?} [{addr}], {value}"),
99        MirInst::ReadSpecialReg { dest, sr_index } => format!("{dest} = read_sr {sr_index}"),
100        MirInst::Barrier => "barrier".to_string(),
101        MirInst::Fence { scope } => format!("fence {scope:?}"),
102    }
103}
104
105fn format_terminator(term: &Terminator) -> String {
106    match term {
107        Terminator::Branch { target } => format!("br {target}"),
108        Terminator::CondBranch {
109            cond,
110            true_target,
111            false_target,
112        } => format!("br {cond}, {true_target}, {false_target}"),
113        Terminator::Return => "ret".to_string(),
114    }
115}
116
117impl fmt::Display for MirFunction {
118    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119        write!(f, "{}", display_function(self))
120    }
121}
122
123#[cfg(test)]
124mod tests {
125    use super::*;
126    use crate::hir::expr::BinOp;
127    use crate::mir::basic_block::{BasicBlock, Terminator};
128    use crate::mir::instruction::{ConstValue, MirInst};
129    use crate::mir::types::MirType;
130    use crate::mir::value::{BlockId, ValueId};
131
132    #[test]
133    fn test_display_simple_function() {
134        let mut func = MirFunction::new("add".into(), BlockId(0));
135        let mut bb = BasicBlock::new(BlockId(0));
136        bb.instructions.push(MirInst::Const {
137            dest: ValueId(0),
138            value: ConstValue::I32(42),
139        });
140        bb.terminator = Terminator::Return;
141        func.blocks.push(bb);
142
143        let output = display_function(&func);
144        assert!(output.contains("function add"));
145        assert!(output.contains("const"));
146        assert!(output.contains("ret"));
147    }
148
149    #[test]
150    fn test_display_branch() {
151        let mut func = MirFunction::new("test".into(), BlockId(0));
152        let mut bb = BasicBlock::new(BlockId(0));
153        bb.terminator = Terminator::CondBranch {
154            cond: ValueId(0),
155            true_target: BlockId(1),
156            false_target: BlockId(2),
157        };
158        func.blocks.push(bb);
159
160        let output = display_function(&func);
161        assert!(output.contains("br %0, bb1, bb2"));
162    }
163
164    #[test]
165    fn test_display_binop() {
166        let inst = MirInst::BinOp {
167            dest: ValueId(2),
168            op: BinOp::Add,
169            lhs: ValueId(0),
170            rhs: ValueId(1),
171            ty: MirType::I32,
172        };
173        let s = format_inst(&inst);
174        assert!(s.contains("%2 = "));
175        assert!(s.contains("Add"));
176        assert!(s.contains("%0"));
177        assert!(s.contains("%1"));
178    }
179}