1use crate::attributes::Attribute;
2use crate::blocks::BlockKey;
3use crate::context::Context;
4use crate::operations::OpKey;
5use crate::regions::RegionKey;
6use crate::values::ValueKey;
7use std::fmt::Write;
8
9pub struct Printer<'a> {
10 ctx: &'a Context,
11 indent: usize,
12 output: String,
13 value_names: std::collections::HashMap<ValueKey, String>,
14 next_value_id: u32,
15 next_block_id: u32,
16 block_names: std::collections::HashMap<BlockKey, String>,
17}
18
19impl<'a> Printer<'a> {
20 pub fn new(ctx: &'a Context) -> Self {
21 Self {
22 ctx,
23 indent: 0,
24 output: String::new(),
25 value_names: std::collections::HashMap::new(),
26 next_value_id: 0,
27 next_block_id: 0,
28 block_names: std::collections::HashMap::new(),
29 }
30 }
31
32 pub fn print_all(&mut self) -> &str {
33 for module in &self.ctx.modules {
34 let name = self.ctx.strings.resolve(module.name);
35 self.write_line(&format!("module @{} {{", name));
36 self.indent += 1;
37
38 for func in &module.functions {
39 self.print_function(func);
40 }
41
42 self.indent -= 1;
43 self.write_line("}");
44 self.write_line("");
45 }
46
47 for (block_key, _block) in &self.ctx.blocks {
49 if self.ctx.blocks[block_key].parent_region.is_none() {
50 self.print_block(block_key);
51 }
52 }
53
54 &self.output
55 }
56
57 fn print_function(&mut self, func: &crate::functions::FunctionData) {
58 let name = self.ctx.strings.resolve(func.name);
59 let mut sig = format!("func @{}(", name);
60
61 for (i, ¶m_ty) in func.params.iter().enumerate() {
62 if i > 0 {
63 sig.push_str(", ");
64 }
65 let pname = self.fresh_value_name();
66 let _ = write!(sig, "%{}: {}", pname, self.format_type(param_ty));
67 }
68
69 sig.push_str(") -> ");
70 if func.returns.len() == 1 {
71 let _ = write!(sig, "{}", self.format_type(func.returns[0]));
72 } else {
73 sig.push('(');
74 for (i, &ret_ty) in func.returns.iter().enumerate() {
75 if i > 0 {
76 sig.push_str(", ");
77 }
78 let _ = write!(sig, "{}", self.format_type(ret_ty));
79 }
80 sig.push(')');
81 }
82
83 if let Some(body) = func.body {
84 sig.push_str(" {");
85 self.write_line(&sig);
86 self.indent += 1;
87 self.print_region(body);
88 self.indent -= 1;
89 self.write_line("}");
90 } else {
91 self.write_line(&sig);
92 }
93 self.write_line("");
94 }
95
96 fn print_region(&mut self, region_key: RegionKey) {
97 if let Some(region) = self.ctx.get_region(region_key) {
98 for &block_key in ®ion.blocks {
99 self.print_block(block_key);
100 }
101 }
102 }
103
104 fn print_block(&mut self, block_key: BlockKey) {
105 let block_name = self.get_block_name(block_key);
106 if let Some(block) = self.ctx.get_block(block_key) {
107 if !block.args.is_empty() {
108 let mut args = String::new();
109 for (i, &arg) in block.args.iter().enumerate() {
110 if i > 0 {
111 args.push_str(", ");
112 }
113 let vname = self.get_value_name(arg);
114 if let Some(val) = self.ctx.get_value(arg) {
115 let _ = write!(args, "%{}: {}", vname, self.format_type(val.ty));
116 }
117 }
118 self.write_line(&format!("^{}({}):", block_name, args));
119 } else {
120 self.write_line(&format!("^{}:", block_name));
121 }
122
123 self.indent += 1;
124 for &op_key in &block.ops {
125 self.print_op(op_key);
126 }
127 self.indent -= 1;
128 }
129 }
130
131 fn print_op(&mut self, op_key: OpKey) {
132 if let Some(op) = self.ctx.get_op(op_key) {
133 let op_name = self.ctx.strings.resolve(op.name).to_string();
134 let mut line = String::new();
135
136 if !op.results.is_empty() {
138 for (i, &result) in op.results.iter().enumerate() {
139 if i > 0 {
140 line.push_str(", ");
141 }
142 let vname = self.get_value_name(result);
143 let _ = write!(line, "%{}", vname);
144 }
145 line.push_str(" = ");
146 }
147
148 let _ = write!(line, "\"{}\"", op_name);
150
151 line.push('(');
153 for (i, &input) in op.inputs.iter().enumerate() {
154 if i > 0 {
155 line.push_str(", ");
156 }
157 let vname = self.get_value_name(input);
158 let _ = write!(line, "%{}", vname);
159 }
160 line.push(')');
161
162 if !op.attrs.is_empty() {
164 line.push_str(" {");
165 for (i, (key, val)) in op.attrs.iter().enumerate() {
166 if i > 0 {
167 line.push_str(", ");
168 }
169 let _ = write!(line, "{} = {}", key, self.format_attr(val));
170 }
171 line.push('}');
172 }
173
174 line.push_str(" : (");
176 for (i, &input) in op.inputs.iter().enumerate() {
177 if i > 0 {
178 line.push_str(", ");
179 }
180 if let Some(val) = self.ctx.get_value(input) {
181 let _ = write!(line, "{}", self.format_type(val.ty));
182 }
183 }
184 line.push_str(") -> ");
185
186 if op.results.len() == 1 {
187 if let Some(val) = self.ctx.get_value(op.results[0]) {
188 let _ = write!(line, "{}", self.format_type(val.ty));
189 }
190 } else if op.results.is_empty() {
191 line.push_str("()");
192 } else {
193 line.push('(');
194 for (i, &result) in op.results.iter().enumerate() {
195 if i > 0 {
196 line.push_str(", ");
197 }
198 if let Some(val) = self.ctx.get_value(result) {
199 let _ = write!(line, "{}", self.format_type(val.ty));
200 }
201 }
202 line.push(')');
203 }
204
205 self.write_line(&line);
206
207 if !op.regions.is_empty() {
209 for ®ion in &op.regions {
210 self.indent += 1;
211 self.print_region(region);
212 self.indent -= 1;
213 }
214 }
215 }
216 }
217
218 fn format_type(&self, ty_id: crate::types::TypeId) -> String {
219 let ty = self.ctx.resolve_type(ty_id);
220 format!("{}", ty)
221 }
222
223 fn format_attr(&self, attr: &Attribute) -> String {
224 match attr {
225 Attribute::Integer(v) => format!("{}", v),
226 Attribute::Float(v) => format!("{:.6}", v),
227 Attribute::String(s) => {
228 let resolved = self.ctx.strings.resolve(*s);
229 format!("\"{}\"", resolved)
230 }
231 Attribute::Bool(b) => format!("{}", b),
232 Attribute::Type(_) => "type".to_string(),
233 Attribute::Array(arr) => {
234 let inner: Vec<String> = arr.iter().map(|a| self.format_attr(a)).collect();
235 format!("[{}]", inner.join(", "))
236 }
237 Attribute::Dict(map) => {
238 let inner: Vec<String> = map
239 .iter()
240 .map(|(k, v)| format!("{}: {}", k, self.format_attr(v)))
241 .collect();
242 format!("{{{}}}", inner.join(", "))
243 }
244 }
245 }
246
247 fn get_value_name(&mut self, key: ValueKey) -> String {
248 if let Some(name) = self.value_names.get(&key) {
249 return name.clone();
250 }
251
252 let name = if let Some(val) = self.ctx.get_value(key) {
254 if let Some(name_id) = val.name {
255 self.ctx.strings.resolve(name_id).to_string()
256 } else {
257 self.fresh_value_name()
258 }
259 } else {
260 self.fresh_value_name()
261 };
262
263 self.value_names.insert(key, name.clone());
264 name
265 }
266
267 fn fresh_value_name(&mut self) -> String {
268 let name = format!("v{}", self.next_value_id);
269 self.next_value_id += 1;
270 name
271 }
272
273 fn get_block_name(&mut self, key: BlockKey) -> String {
274 if let Some(name) = self.block_names.get(&key) {
275 return name.clone();
276 }
277 let name = format!("bb{}", self.next_block_id);
278 self.next_block_id += 1;
279 self.block_names.insert(key, name.clone());
280 name
281 }
282
283 fn write_line(&mut self, text: &str) {
284 for _ in 0..self.indent {
285 self.output.push_str(" ");
286 }
287 self.output.push_str(text);
288 self.output.push('\n');
289 }
290
291 pub fn into_string(self) -> String {
292 self.output
293 }
294}
295
296pub fn print_ir(ctx: &Context) -> String {
297 let mut printer = Printer::new(ctx);
298 printer.print_all();
299 printer.into_string()
300}
301
302#[cfg(test)]
303mod tests {
304 use super::*;
305
306 #[test]
307 fn test_print_empty_context() {
308 let ctx = Context::new();
309 let output = print_ir(&ctx);
310 assert!(output.is_empty() || output.trim().is_empty());
311 }
312}