joule_mir/
pretty.rs

1//! Pretty printing for MIR
2//!
3//! This module provides human-readable output for MIR structures,
4//! useful for debugging and inspection.
5
6use crate::{
7    BasicBlock, BasicBlockId, FunctionMIR, MirContext, Operand, Place, Rvalue, Statement,
8    Terminator,
9};
10use std::fmt::Write;
11
12/// Pretty print a MIR context
13pub fn print_mir_context(ctx: &MirContext) -> String {
14    let mut output = String::new();
15
16    for (_, func) in &ctx.functions {
17        let _ = write!(output, "{}\n", print_function(func));
18    }
19
20    output
21}
22
23/// Pretty print a function
24pub fn print_function(func: &FunctionMIR) -> String {
25    let mut output = String::new();
26
27    // Function header
28    let _ = writeln!(output, "fn {} -> {:?} {{", func.name, func.return_ty);
29
30    // Locals
31    if !func.locals.is_empty() {
32        let _ = writeln!(output, "  // Locals:");
33        for (idx, local) in func.locals.iter().enumerate() {
34            let name = if idx == 0 {
35                "_ret".to_string()
36            } else {
37                format!("_{}", idx)
38            };
39            let mut_str = if local.mutable { "mut " } else { "" };
40            let _ = writeln!(output, "  //   {}: {}{:?}", name, mut_str, local.ty);
41        }
42        let _ = writeln!(output);
43    }
44
45    // Basic blocks
46    for (idx, block) in func.basic_blocks.iter().enumerate() {
47        let _ = write!(
48            output,
49            "{}",
50            print_basic_block(BasicBlockId::new(idx as u32), block)
51        );
52    }
53
54    let _ = writeln!(output, "}}");
55
56    output
57}
58
59/// Pretty print a basic block
60pub fn print_basic_block(id: BasicBlockId, block: &BasicBlock) -> String {
61    let mut output = String::new();
62
63    let _ = writeln!(output, "  {}:", id);
64
65    for stmt in &block.statements {
66        let _ = writeln!(output, "    {}", print_statement(stmt));
67    }
68
69    let _ = writeln!(output, "    {}", print_terminator(&block.terminator));
70    let _ = writeln!(output);
71
72    output
73}
74
75/// Pretty print a statement
76pub fn print_statement(stmt: &Statement) -> String {
77    match stmt {
78        Statement::Assign {
79            place,
80            rvalue,
81            span: _,
82        } => {
83            format!("{} = {}", print_place(place), print_rvalue(rvalue))
84        }
85        Statement::StorageLive { local, span: _ } => {
86            format!("StorageLive({})", local)
87        }
88        Statement::StorageDead { local, span: _ } => {
89            format!("StorageDead({})", local)
90        }
91        Statement::Nop => "nop".to_string(),
92    }
93}
94
95/// Pretty print a terminator
96pub fn print_terminator(term: &Terminator) -> String {
97    match term {
98        Terminator::Return { .. } => "return".to_string(),
99        Terminator::Goto { target, .. } => {
100            format!("goto {}", target)
101        }
102        Terminator::SwitchInt {
103            discriminant,
104            targets,
105            ..
106        } => {
107            let mut s = format!("switchInt({}) -> [", print_operand(discriminant));
108            for (value, target) in &targets.branches {
109                s.push_str(&format!("{}: {}, ", value, target));
110            }
111            s.push_str(&format!("otherwise: {}]", targets.otherwise));
112            s
113        }
114        Terminator::Call {
115            func,
116            args,
117            destination,
118            target,
119            ..
120        } => {
121            let args_str = args
122                .iter()
123                .map(print_operand)
124                .collect::<Vec<_>>()
125                .join(", ");
126            format!(
127                "{} = {}({}) -> {}",
128                print_place(destination),
129                print_operand(func),
130                args_str,
131                target
132            )
133        }
134        Terminator::Abort { .. } => "abort".to_string(),
135        Terminator::Unreachable { .. } => "unreachable".to_string(),
136
137        // Concurrency terminators
138        Terminator::Spawn {
139            func,
140            args,
141            destination,
142            target,
143            ..
144        } => {
145            let args_str = args
146                .iter()
147                .map(print_operand)
148                .collect::<Vec<_>>()
149                .join(", ");
150            format!(
151                "{} = spawn {}({}) -> {}",
152                print_place(destination),
153                print_operand(func),
154                args_str,
155                target
156            )
157        }
158        Terminator::TaskAwait {
159            task,
160            destination,
161            target,
162            ..
163        } => {
164            format!(
165                "{} = await {} -> {}",
166                print_place(destination),
167                print_operand(task),
168                target
169            )
170        }
171        Terminator::TaskGroupEnter {
172            destination,
173            body,
174            join_block,
175            ..
176        } => {
177            format!(
178                "{} = task_group_enter -> [body: {}, join: {}]",
179                print_place(destination),
180                body,
181                join_block
182            )
183        }
184        Terminator::TaskGroupExit { group, target, .. } => {
185            format!("task_group_exit {} -> {}", print_operand(group), target)
186        }
187        Terminator::ChannelRecv {
188            channel,
189            destination,
190            target,
191            closed_target,
192            ..
193        } => {
194            format!(
195                "{} = recv {} -> [ok: {}, closed: {}]",
196                print_place(destination),
197                print_operand(channel),
198                target,
199                closed_target
200            )
201        }
202        Terminator::ChannelSend {
203            channel,
204            value,
205            target,
206            closed_target,
207            ..
208        } => {
209            format!(
210                "send {} <- {} -> [ok: {}, closed: {}]",
211                print_operand(channel),
212                print_operand(value),
213                target,
214                closed_target
215            )
216        }
217        Terminator::Select {
218            arms,
219            default,
220            destination,
221            selected_arm,
222            ..
223        } => {
224            let arms_str = arms
225                .iter()
226                .enumerate()
227                .map(|(i, arm)| format!("{}: {:?} -> {}", i, arm.operation, arm.target))
228                .collect::<Vec<_>>()
229                .join(", ");
230            let default_str = default
231                .map(|b| format!(", default: {}", b))
232                .unwrap_or_default();
233            format!(
234                "({}, {}) = select [{}{}]",
235                print_place(destination),
236                print_place(selected_arm),
237                arms_str,
238                default_str
239            )
240        }
241        Terminator::Cancel { .. } => "cancel".to_string(),
242    }
243}
244
245/// Pretty print a place
246pub fn print_place(place: &Place) -> String {
247    let mut s = format!("{}", place.local);
248    for elem in &place.projection {
249        match elem {
250            crate::PlaceElem::Deref => s.push_str(".*"),
251            crate::PlaceElem::Field(field) => {
252                s.push_str(&format!(".{}", field.0));
253            }
254            crate::PlaceElem::Index(local) => {
255                s.push_str(&format!("[{}]", local));
256            }
257            crate::PlaceElem::Downcast(name) => {
258                s.push_str(&format!(" as {}", name.as_str()));
259            }
260        }
261    }
262    s
263}
264
265/// Pretty print an rvalue
266pub fn print_rvalue(rvalue: &Rvalue) -> String {
267    match rvalue {
268        Rvalue::Use(op) => print_operand(op),
269        Rvalue::BinaryOp { op, left, right } => {
270            format!(
271                "{:?}({}, {})",
272                op,
273                print_operand(left),
274                print_operand(right)
275            )
276        }
277        Rvalue::UnaryOp { op, operand } => {
278            format!("{:?}({})", op, print_operand(operand))
279        }
280        Rvalue::Ref { mutable, place } => {
281            let mut_str = if *mutable { "mut " } else { "" };
282            format!("&{}{}", mut_str, print_place(place))
283        }
284        Rvalue::Aggregate { kind, operands } => {
285            let ops = operands
286                .iter()
287                .map(print_operand)
288                .collect::<Vec<_>>()
289                .join(", ");
290            format!("{:?}({})", kind, ops)
291        }
292        Rvalue::Cast {
293            operand,
294            target_ty,
295            kind,
296        } => {
297            format!("{} as {:?} ({:?})", print_operand(operand), target_ty, kind)
298        }
299        Rvalue::Discriminant { place } => {
300            format!("discriminant({})", print_place(place))
301        }
302        Rvalue::Len { place } => {
303            format!("len({})", print_place(place))
304        }
305        Rvalue::SimdBinaryOp {
306            op,
307            element_ty,
308            width,
309            left,
310            right,
311        } => {
312            format!(
313                "simd_{:?}_{:?}x{:?}({}, {})",
314                op,
315                element_ty,
316                width,
317                print_operand(left),
318                print_operand(right)
319            )
320        }
321        Rvalue::SimdLoad {
322            element_ty,
323            width,
324            source,
325        } => {
326            format!(
327                "simd_load_{:?}x{:?}({})",
328                element_ty,
329                width,
330                print_place(source)
331            )
332        }
333        Rvalue::SimdStore {
334            element_ty,
335            width,
336            value,
337            dest,
338        } => {
339            format!(
340                "simd_store_{:?}x{:?}({}, {})",
341                element_ty,
342                width,
343                print_operand(value),
344                print_place(dest)
345            )
346        }
347        Rvalue::SimdSplat {
348            element_ty,
349            width,
350            value,
351        } => {
352            format!(
353                "simd_splat_{:?}x{:?}({})",
354                element_ty,
355                width,
356                print_operand(value)
357            )
358        }
359
360        // Concurrency rvalues
361        Rvalue::ChannelCreate {
362            element_ty,
363            capacity,
364        } => {
365            format!("channel::<{:?}>({})", element_ty, capacity)
366        }
367        Rvalue::ChannelTryRecv { channel } => {
368            format!("try_recv({})", print_operand(channel))
369        }
370        Rvalue::ChannelTrySend { channel, value } => {
371            format!(
372                "try_send({}, {})",
373                print_operand(channel),
374                print_operand(value)
375            )
376        }
377        Rvalue::ChannelSender { channel } => {
378            format!("sender({})", print_operand(channel))
379        }
380        Rvalue::ChannelReceiver { channel } => {
381            format!("receiver({})", print_operand(channel))
382        }
383        Rvalue::ChannelClose { channel } => {
384            format!("close({})", print_operand(channel))
385        }
386        Rvalue::IsCancelled => "is_cancelled()".to_string(),
387        Rvalue::CurrentTask => "current_task()".to_string(),
388        Rvalue::Try(operand) => format!("try({})", print_operand(operand)),
389        Rvalue::EnumVariant { variant, fields } => {
390            if fields.is_empty() {
391                format!("enum_variant({})", variant)
392            } else {
393                let field_strs: Vec<String> = fields.iter().map(print_operand).collect();
394                format!("enum_variant({}, {})", variant, field_strs.join(", "))
395            }
396        }
397    }
398}
399
400/// Pretty print an operand
401pub fn print_operand(operand: &Operand) -> String {
402    match operand {
403        Operand::Copy(place) => format!("copy {}", print_place(place)),
404        Operand::Move(place) => format!("move {}", print_place(place)),
405        Operand::Constant(c) => format!("const {:?}", c.literal),
406    }
407}
408
409#[cfg(test)]
410mod tests {
411    use super::*;
412    use crate::Local;
413    use joule_common::Span;
414
415    #[test]
416    fn test_print_place() {
417        let place = Place::from_local(Local::new(1));
418        assert_eq!(print_place(&place), "_1");
419
420        let deref_place = place.deref();
421        assert_eq!(print_place(&deref_place), "_1.*");
422    }
423
424    #[test]
425    fn test_print_statement() {
426        let stmt = Statement::StorageLive {
427            local: Local::new(1),
428            span: Span::dummy(),
429        };
430        assert_eq!(print_statement(&stmt), "StorageLive(_1)");
431
432        let stmt = Statement::Nop;
433        assert_eq!(print_statement(&stmt), "nop");
434    }
435
436    #[test]
437    fn test_print_terminator() {
438        let term = Terminator::Return {
439            span: Span::dummy(),
440        };
441        assert_eq!(print_terminator(&term), "return");
442
443        let term = Terminator::Goto {
444            target: BasicBlockId::new(1),
445            span: Span::dummy(),
446        };
447        assert_eq!(print_terminator(&term), "goto bb1");
448    }
449
450    #[test]
451    fn test_print_operand() {
452        let op = Operand::Copy(Place::from_local(Local::new(1)));
453        assert_eq!(print_operand(&op), "copy _1");
454
455        let op = Operand::Move(Place::from_local(Local::new(2)));
456        assert_eq!(print_operand(&op), "move _2");
457    }
458}