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, true);
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 let mut entry_args: Option<Vec<ValueKey>> = None;
67 if let Some(region) = func.body {
68 if let Some(r) = self.ctx.get_region(region) {
69 if let Some(&block_key) = r.blocks.first() {
70 if let Some(block) = self.ctx.get_block(block_key) {
71 entry_args = Some(block.args.clone());
72 }
73 }
74 }
75 }
76
77 if let Some(args) = entry_args {
78 for (i, arg) in args.into_iter().enumerate() {
79 if i > 0 {
80 sig.push_str(", ");
81 }
82 let vname = self.get_value_name(arg);
83 if let Some(val) = self.ctx.get_value(arg) {
84 let _ = write!(sig, "%{}: {}", vname, self.format_type(val.ty));
85 }
86 }
87 } else {
88 for (i, ¶m_ty) in func.params.iter().enumerate() {
89 if i > 0 {
90 sig.push_str(", ");
91 }
92 let pname = self.fresh_value_name();
93 let _ = write!(sig, "%{}: {}", pname, self.format_type(param_ty));
94 }
95 }
96
97 sig.push_str(") -> ");
98 if func.returns.len() == 1 {
99 let _ = write!(sig, "{}", self.format_type(func.returns[0]));
100 } else {
101 sig.push('(');
102 for (i, &ret_ty) in func.returns.iter().enumerate() {
103 if i > 0 {
104 sig.push_str(", ");
105 }
106 let _ = write!(sig, "{}", self.format_type(ret_ty));
107 }
108 sig.push(')');
109 }
110
111 if let Some(body) = func.body {
112 sig.push_str(" {");
113 self.write_line(&sig);
114 self.indent += 1;
115 self.print_function_body(body);
116 self.indent -= 1;
117 self.write_line("}");
118 } else {
119 self.write_line(&sig);
120 }
121 self.write_line("");
122 }
123
124 fn print_region(&mut self, region_key: RegionKey) {
125 if let Some(region) = self.ctx.get_region(region_key) {
126 for &block_key in ®ion.blocks {
127 self.print_block(block_key, true);
128 }
129 }
130 }
131
132 fn print_function_body(&mut self, region_key: RegionKey) {
139 if let Some(region) = self.ctx.get_region(region_key) {
140 for (i, &block_key) in region.blocks.iter().enumerate() {
141 self.print_block(block_key, i != 0);
142 }
143 }
144 }
145
146 fn print_block(&mut self, block_key: BlockKey, print_header: bool) {
147 if print_header {
148 let block_name = self.get_block_name(block_key);
149 if let Some(block) = self.ctx.get_block(block_key) {
150 if !block.args.is_empty() {
151 let mut args = String::new();
152 for (i, &arg) in block.args.iter().enumerate() {
153 if i > 0 {
154 args.push_str(", ");
155 }
156 let vname = self.get_value_name(arg);
157 if let Some(val) = self.ctx.get_value(arg) {
158 let _ = write!(args, "%{}: {}", vname, self.format_type(val.ty));
159 }
160 }
161 self.write_line(&format!("^{}({}):", block_name, args));
162 } else {
163 self.write_line(&format!("^{}:", block_name));
164 }
165 }
166 self.indent += 1;
167 }
168
169 if let Some(block) = self.ctx.get_block(block_key) {
170 for &op_key in &block.ops {
171 self.print_op(op_key);
172 }
173 }
174
175 if print_header {
176 self.indent -= 1;
177 }
178 }
179
180 fn print_op(&mut self, op_key: OpKey) {
181 if let Some(op) = self.ctx.get_op(op_key) {
182 let op_name = self.ctx.strings.resolve(op.name).to_string();
183 let mut line = String::new();
184
185 if !op.results.is_empty() {
187 for (i, &result) in op.results.iter().enumerate() {
188 if i > 0 {
189 line.push_str(", ");
190 }
191 let vname = self.get_value_name(result);
192 let _ = write!(line, "%{}", vname);
193 }
194 line.push_str(" = ");
195 }
196
197 let _ = write!(line, "\"{}\"", op_name);
199
200 line.push('(');
202 for (i, &input) in op.inputs.iter().enumerate() {
203 if i > 0 {
204 line.push_str(", ");
205 }
206 let vname = self.get_value_name(input);
207 let _ = write!(line, "%{}", vname);
208 }
209 line.push(')');
210
211 if !op.attrs.is_empty() {
213 line.push_str(" {");
214 for (i, (key, val)) in op.attrs.iter().enumerate() {
215 if i > 0 {
216 line.push_str(", ");
217 }
218 let _ = write!(line, "{} = {}", key, self.format_attr(val));
219 }
220 line.push('}');
221 }
222
223 line.push_str(" : (");
225 for (i, &input) in op.inputs.iter().enumerate() {
226 if i > 0 {
227 line.push_str(", ");
228 }
229 if let Some(val) = self.ctx.get_value(input) {
230 let _ = write!(line, "{}", self.format_type(val.ty));
231 }
232 }
233 line.push_str(") -> ");
234
235 if op.results.len() == 1 {
236 if let Some(val) = self.ctx.get_value(op.results[0]) {
237 let _ = write!(line, "{}", self.format_type(val.ty));
238 }
239 } else if op.results.is_empty() {
240 line.push_str("()");
241 } else {
242 line.push('(');
243 for (i, &result) in op.results.iter().enumerate() {
244 if i > 0 {
245 line.push_str(", ");
246 }
247 if let Some(val) = self.ctx.get_value(result) {
248 let _ = write!(line, "{}", self.format_type(val.ty));
249 }
250 }
251 line.push(')');
252 }
253
254 self.write_line(&line);
255
256 if !op.regions.is_empty() {
258 for ®ion in &op.regions {
259 self.indent += 1;
260 self.print_region(region);
261 self.indent -= 1;
262 }
263 }
264 }
265 }
266
267 fn format_type(&self, ty_id: crate::types::TypeId) -> String {
268 let ty = self.ctx.resolve_type(ty_id);
269 format!("{}", ty)
270 }
271
272 fn format_attr(&self, attr: &Attribute) -> String {
273 match attr {
274 Attribute::Integer(v) => format!("{}", v),
275 Attribute::Float(v) => format!("{:.6}", v),
276 Attribute::String(s) => {
277 let resolved = self.ctx.strings.resolve(*s);
278 format!("\"{}\"", resolved)
279 }
280 Attribute::Bool(b) => format!("{}", b),
281 Attribute::Type(_) => "type".to_string(),
282 Attribute::Array(arr) => {
283 let inner: Vec<String> = arr.iter().map(|a| self.format_attr(a)).collect();
284 format!("[{}]", inner.join(", "))
285 }
286 Attribute::Dict(map) => {
287 let inner: Vec<String> = map
288 .iter()
289 .map(|(k, v)| format!("{}: {}", k, self.format_attr(v)))
290 .collect();
291 format!("{{{}}}", inner.join(", "))
292 }
293 }
294 }
295
296 fn get_value_name(&mut self, key: ValueKey) -> String {
297 if let Some(name) = self.value_names.get(&key) {
298 return name.clone();
299 }
300
301 let name = if let Some(val) = self.ctx.get_value(key) {
303 if let Some(name_id) = val.name {
304 self.ctx.strings.resolve(name_id).to_string()
305 } else {
306 self.fresh_value_name()
307 }
308 } else {
309 self.fresh_value_name()
310 };
311
312 self.value_names.insert(key, name.clone());
313 name
314 }
315
316 fn fresh_value_name(&mut self) -> String {
317 let name = format!("v{}", self.next_value_id);
318 self.next_value_id += 1;
319 name
320 }
321
322 fn get_block_name(&mut self, key: BlockKey) -> String {
323 if let Some(name) = self.block_names.get(&key) {
324 return name.clone();
325 }
326 let name = format!("bb{}", self.next_block_id);
327 self.next_block_id += 1;
328 self.block_names.insert(key, name.clone());
329 name
330 }
331
332 fn write_line(&mut self, text: &str) {
333 for _ in 0..self.indent {
334 self.output.push_str(" ");
335 }
336 self.output.push_str(text);
337 self.output.push('\n');
338 }
339
340 pub fn into_string(self) -> String {
341 self.output
342 }
343}
344
345pub fn print_ir(ctx: &Context) -> String {
346 let mut printer = Printer::new(ctx);
347 printer.print_all();
348 printer.into_string()
349}
350
351#[cfg(test)]
352mod tests {
353 use super::*;
354
355 #[test]
356 fn test_print_empty_context() {
357 let ctx = Context::new();
358 let output = print_ir(&ctx);
359 assert!(output.is_empty() || output.trim().is_empty());
360 }
361}