1use crate::{
7 BasicBlock, BasicBlockId, FunctionMIR, MirContext, Operand, Place, Rvalue, Statement,
8 Terminator,
9};
10use std::fmt::Write;
11
12pub 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
23pub fn print_function(func: &FunctionMIR) -> String {
25 let mut output = String::new();
26
27 let _ = writeln!(output, "fn {} -> {:?} {{", func.name, func.return_ty);
29
30 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 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
59pub 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
75pub 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
95pub 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 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
245pub 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
265pub 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 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
400pub 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}