1use std::fmt::{self, Write};
10
11use super::basic_block::Terminator;
12use super::function::MirFunction;
13use super::instruction::MirInst;
14
15#[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}