1use crate::entity::SecondaryMap;
7use crate::ir::entities::AnyEntity;
8use crate::ir::immediates::Ieee128;
9use crate::ir::{Block, DataFlowGraph, Function, Inst, Opcode, SigRef, Type, Value, ValueDef};
10use crate::packed_option::ReservedValue;
11use alloc::string::{String, ToString};
12use alloc::vec::Vec;
13use core::fmt::{self, Write};
14
15pub trait FuncWriter {
17 fn write_block_header(
19 &mut self,
20 w: &mut dyn Write,
21 func: &Function,
22 block: Block,
23 indent: usize,
24 ) -> fmt::Result;
25
26 fn write_instruction(
28 &mut self,
29 w: &mut dyn Write,
30 func: &Function,
31 aliases: &SecondaryMap<Value, Vec<Value>>,
32 inst: Inst,
33 indent: usize,
34 ) -> fmt::Result;
35
36 fn write_preamble(&mut self, w: &mut dyn Write, func: &Function) -> Result<bool, fmt::Error> {
38 self.super_preamble(w, func)
39 }
40
41 fn super_preamble(&mut self, w: &mut dyn Write, func: &Function) -> Result<bool, fmt::Error> {
43 let mut any = false;
44
45 for (ss, slot) in func.dynamic_stack_slots.iter() {
46 any = true;
47 self.write_entity_definition(w, func, ss.into(), slot)?;
48 }
49
50 for (ss, slot) in func.sized_stack_slots.iter() {
51 any = true;
52 self.write_entity_definition(w, func, ss.into(), slot)?;
53 }
54
55 for (gv, gv_data) in &func.global_values {
56 any = true;
57 self.write_entity_definition(w, func, gv.into(), gv_data)?;
58 }
59
60 for (sig, sig_data) in &func.dfg.signatures {
63 any = true;
64 self.write_entity_definition(w, func, sig.into(), &sig_data)?;
65 }
66
67 for (fnref, ext_func) in &func.dfg.ext_funcs {
68 if ext_func.signature != SigRef::reserved_value() {
69 any = true;
70 self.write_entity_definition(
71 w,
72 func,
73 fnref.into(),
74 &ext_func.display(Some(&func.params)),
75 )?;
76 }
77 }
78
79 for (&cref, cval) in func.dfg.constants.iter() {
80 any = true;
81 self.write_entity_definition(w, func, cref.into(), cval)?;
82 }
83
84 if let Some(limit) = func.stack_limit {
85 any = true;
86 self.write_entity_definition(w, func, AnyEntity::StackLimit, &limit)?;
87 }
88
89 Ok(any)
90 }
91
92 fn write_entity_definition(
94 &mut self,
95 w: &mut dyn Write,
96 func: &Function,
97 entity: AnyEntity,
98 value: &dyn fmt::Display,
99 ) -> fmt::Result {
100 self.super_entity_definition(w, func, entity, value)
101 }
102
103 fn super_entity_definition(
105 &mut self,
106 w: &mut dyn Write,
107 _func: &Function,
108 entity: AnyEntity,
109 value: &dyn fmt::Display,
110 ) -> fmt::Result {
111 writeln!(w, " {entity} = {value}")
112 }
113}
114
115pub struct PlainWriter;
117
118impl FuncWriter for PlainWriter {
119 fn write_instruction(
120 &mut self,
121 w: &mut dyn Write,
122 func: &Function,
123 aliases: &SecondaryMap<Value, Vec<Value>>,
124 inst: Inst,
125 indent: usize,
126 ) -> fmt::Result {
127 write_instruction(w, func, aliases, inst, indent)
128 }
129
130 fn write_block_header(
131 &mut self,
132 w: &mut dyn Write,
133 func: &Function,
134 block: Block,
135 indent: usize,
136 ) -> fmt::Result {
137 write_block_header(w, func, block, indent)
138 }
139}
140
141pub fn write_function(w: &mut dyn Write, func: &Function) -> fmt::Result {
144 decorate_function(&mut PlainWriter, w, func)
145}
146
147fn alias_map(func: &Function) -> SecondaryMap<Value, Vec<Value>> {
149 let mut aliases = SecondaryMap::<_, Vec<_>>::new();
150 for v in func.dfg.values() {
151 if let Some(k) = func.dfg.value_alias_dest_for_serialization(v) {
153 aliases[k].push(v);
154 }
155 }
156 aliases
157}
158
159pub fn decorate_function<FW: FuncWriter>(
163 func_w: &mut FW,
164 w: &mut dyn Write,
165 func: &Function,
166) -> fmt::Result {
167 write!(w, "function ")?;
168 write_function_spec(w, func)?;
169 writeln!(w, " {{")?;
170 let aliases = alias_map(func);
171 let mut any = func_w.write_preamble(w, func)?;
172 for block in &func.layout {
173 if any {
174 writeln!(w)?;
175 }
176 decorate_block(func_w, w, func, &aliases, block)?;
177 any = true;
178 }
179 writeln!(w, "}}")
180}
181
182pub fn write_function_spec(w: &mut dyn Write, func: &Function) -> fmt::Result {
188 write!(w, "{}{}", func.name, func.signature)
189}
190
191fn write_arg(w: &mut dyn Write, func: &Function, arg: Value) -> fmt::Result {
196 let ty = func.dfg.value_type(arg);
197 write!(w, "{arg}: {ty}")
198}
199
200pub fn write_block_header(
207 w: &mut dyn Write,
208 func: &Function,
209 block: Block,
210 indent: usize,
211) -> fmt::Result {
212 let cold = if func.layout.is_cold(block) {
213 " cold"
214 } else {
215 ""
216 };
217
218 write!(w, "{1:0$}{2}", indent - 4, "", block)?;
220
221 let mut args = func.dfg.block_params(block).iter().cloned();
222 match args.next() {
223 None => return writeln!(w, "{cold}:"),
224 Some(arg) => {
225 write!(w, "(")?;
226 write_arg(w, func, arg)?;
227 }
228 }
229 for arg in args {
231 write!(w, ", ")?;
232 write_arg(w, func, arg)?;
233 }
234 writeln!(w, "){cold}:")
235}
236
237fn decorate_block<FW: FuncWriter>(
238 func_w: &mut FW,
239 w: &mut dyn Write,
240 func: &Function,
241 aliases: &SecondaryMap<Value, Vec<Value>>,
242 block: Block,
243) -> fmt::Result {
244 let indent = if func.rel_srclocs().is_empty() && func.debug_tags.is_empty() {
246 4
247 } else {
248 36
249 };
250
251 func_w.write_block_header(w, func, block, indent)?;
252 for a in func.dfg.block_params(block).iter().cloned() {
253 write_value_aliases(w, aliases, a, indent)?;
254 }
255
256 for inst in func.layout.block_insts(block) {
257 func_w.write_instruction(w, func, aliases, inst, indent)?;
258 }
259
260 Ok(())
261}
262
263fn type_suffix(func: &Function, inst: Inst) -> Option<Type> {
273 let inst_data = &func.dfg.insts[inst];
274 let constraints = inst_data.opcode().constraints();
275
276 if !constraints.is_polymorphic() {
277 return None;
278 }
279
280 if constraints.use_typevar_operand() {
283 let ctrl_var = inst_data.typevar_operand(&func.dfg.value_lists).unwrap();
284 let def_block = match func.dfg.value_def(ctrl_var) {
285 ValueDef::Result(instr, _) => func.layout.inst_block(instr),
286 ValueDef::Param(block, _) => Some(block),
287 ValueDef::Union(..) => None,
288 };
289 if def_block.is_some() && def_block == func.layout.inst_block(inst) {
290 return None;
291 }
292 }
293
294 let rtype = func.dfg.ctrl_typevar(inst);
295 assert!(
296 !rtype.is_invalid(),
297 "Polymorphic instruction must produce a result"
298 );
299 Some(rtype)
300}
301
302fn write_value_aliases(
304 w: &mut dyn Write,
305 aliases: &SecondaryMap<Value, Vec<Value>>,
306 target: Value,
307 indent: usize,
308) -> fmt::Result {
309 let mut todo_stack = vec![target];
310 while let Some(target) = todo_stack.pop() {
311 for &a in &aliases[target] {
312 writeln!(w, "{1:0$}{2} -> {3}", indent, "", a, target)?;
313 todo_stack.push(a);
314 }
315 }
316
317 Ok(())
318}
319
320fn write_instruction(
321 w: &mut dyn Write,
322 func: &Function,
323 aliases: &SecondaryMap<Value, Vec<Value>>,
324 inst: Inst,
325 mut indent: usize,
326) -> fmt::Result {
327 let mut s = String::with_capacity(16);
329
330 let srcloc = func.srcloc(inst);
332 if !srcloc.is_default() {
333 write!(s, "{srcloc} ")?;
334 }
335
336 write_debug_tags(w, &func, inst, &mut indent)?;
338
339 write!(w, "{s:indent$}")?;
341
342 let mut has_results = false;
344 for r in func.dfg.inst_results(inst) {
345 if !has_results {
346 has_results = true;
347 write!(w, "{r}")?;
348 } else {
349 write!(w, ", {r}")?;
350 }
351 }
352 if has_results {
353 write!(w, " = ")?;
354 }
355
356 let opcode = func.dfg.insts[inst].opcode();
358
359 match type_suffix(func, inst) {
360 Some(suf) => write!(w, "{opcode}.{suf}")?,
361 None => write!(w, "{opcode}")?,
362 }
363
364 write_operands(w, &func.dfg, inst)?;
365 writeln!(w)?;
366
367 for r in func.dfg.inst_results(inst) {
369 write_value_aliases(w, aliases, *r, indent)?;
370 }
371 Ok(())
372}
373
374pub fn write_operands(w: &mut dyn Write, dfg: &DataFlowGraph, inst: Inst) -> fmt::Result {
376 let pool = &dfg.value_lists;
377 let jump_tables = &dfg.jump_tables;
378 let exception_tables = &dfg.exception_tables;
379 use crate::ir::instructions::InstructionData::*;
380 let ctrl_ty = dfg.ctrl_typevar(inst);
381 match dfg.insts[inst] {
382 AtomicRmw { op, args, .. } => write!(w, " {} {}, {}", op, args[0], args[1]),
383 AtomicCas { args, .. } => write!(w, " {}, {}, {}", args[0], args[1], args[2]),
384 LoadNoOffset { flags, arg, .. } => write!(w, "{flags} {arg}"),
385 StoreNoOffset { flags, args, .. } => write!(w, "{} {}, {}", flags, args[0], args[1]),
386 Unary { arg, .. } => write!(w, " {arg}"),
387 UnaryImm { imm, .. } => write!(w, " {}", {
388 let mut imm = imm;
389 if ctrl_ty.bits() != 0 {
390 imm = imm.sign_extend_from_width(ctrl_ty.bits());
391 }
392 imm
393 }),
394 UnaryIeee16 { imm, .. } => write!(w, " {imm}"),
395 UnaryIeee32 { imm, .. } => write!(w, " {imm}"),
396 UnaryIeee64 { imm, .. } => write!(w, " {imm}"),
397 UnaryGlobalValue { global_value, .. } => write!(w, " {global_value}"),
398 UnaryConst {
399 constant_handle, ..
400 } => write!(w, " {constant_handle}"),
401 Binary { args, .. } => write!(w, " {}, {}", args[0], args[1]),
402 BinaryImm8 { arg, imm, .. } => write!(w, " {arg}, {imm}"),
403 BinaryImm64 { arg, imm, .. } => write!(w, " {}, {}", arg, {
404 let mut imm = imm;
405 if ctrl_ty.bits() != 0 {
406 imm = imm.sign_extend_from_width(ctrl_ty.bits());
407 }
408 imm
409 }),
410 Ternary { args, .. } => write!(w, " {}, {}, {}", args[0], args[1], args[2]),
411 MultiAry { ref args, .. } => {
412 if args.is_empty() {
413 write!(w, "")
414 } else {
415 write!(w, " {}", DisplayValues(args.as_slice(pool)))
416 }
417 }
418 NullAry { .. } => write!(w, " "),
419 TernaryImm8 { imm, args, .. } => write!(w, " {}, {}, {}", args[0], args[1], imm),
420 Shuffle { imm, args, .. } => {
421 let data = dfg.immediates.get(imm).expect(
422 "Expected the shuffle mask to already be inserted into the immediates table",
423 );
424 write!(w, " {}, {}, {}", args[0], args[1], data)
425 }
426 IntCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]),
427 IntCompareImm { cond, arg, imm, .. } => write!(w, " {} {}, {}", cond, arg, {
428 let mut imm = imm;
429 if ctrl_ty.bits() != 0 {
430 imm = imm.sign_extend_from_width(ctrl_ty.bits());
431 }
432 imm
433 }),
434 IntAddTrap { args, code, .. } => write!(w, " {}, {}, {}", args[0], args[1], code),
435 FloatCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]),
436 Jump { destination, .. } => {
437 write!(w, " {}", destination.display(pool))
438 }
439 Brif {
440 arg,
441 blocks: [block_then, block_else],
442 ..
443 } => {
444 write!(w, " {}, {}", arg, block_then.display(pool))?;
445 write!(w, ", {}", block_else.display(pool))
446 }
447 BranchTable { arg, table, .. } => {
448 write!(w, " {}, {}", arg, jump_tables[table].display(pool))
449 }
450 Call {
451 func_ref, ref args, ..
452 } => {
453 write!(w, " {}({})", func_ref, DisplayValues(args.as_slice(pool)))?;
454 write_user_stack_map_entries(w, dfg, inst)
455 }
456 CallIndirect {
457 sig_ref, ref args, ..
458 } => {
459 let args = args.as_slice(pool);
460 write!(
461 w,
462 " {}, {}({})",
463 sig_ref,
464 args[0],
465 DisplayValues(&args[1..])
466 )?;
467 write_user_stack_map_entries(w, dfg, inst)
468 }
469 TryCall {
470 func_ref,
471 ref args,
472 exception,
473 ..
474 } => {
475 write!(
476 w,
477 " {}({}), {}",
478 func_ref,
479 DisplayValues(args.as_slice(pool)),
480 exception_tables[exception].display(pool),
481 )?;
482 write_user_stack_map_entries(w, dfg, inst)
483 }
484 TryCallIndirect {
485 ref args,
486 exception,
487 ..
488 } => {
489 let args = args.as_slice(pool);
490 write!(
491 w,
492 " {}({}), {}",
493 args[0],
494 DisplayValues(&args[1..]),
495 exception_tables[exception].display(pool),
496 )?;
497 write_user_stack_map_entries(w, dfg, inst)
498 }
499 FuncAddr { func_ref, .. } => write!(w, " {func_ref}"),
500 StackLoad {
501 stack_slot, offset, ..
502 } => write!(w, " {stack_slot}{offset}"),
503 StackStore {
504 arg,
505 stack_slot,
506 offset,
507 ..
508 } => write!(w, " {arg}, {stack_slot}{offset}"),
509 DynamicStackLoad {
510 dynamic_stack_slot, ..
511 } => write!(w, " {dynamic_stack_slot}"),
512 DynamicStackStore {
513 arg,
514 dynamic_stack_slot,
515 ..
516 } => write!(w, " {arg}, {dynamic_stack_slot}"),
517 Load {
518 flags, arg, offset, ..
519 } => write!(w, "{flags} {arg}{offset}"),
520 Store {
521 flags,
522 args,
523 offset,
524 ..
525 } => write!(w, "{} {}, {}{}", flags, args[0], args[1], offset),
526 Trap { code, .. } => write!(w, " {code}"),
527 CondTrap { arg, code, .. } => write!(w, " {arg}, {code}"),
528 ExceptionHandlerAddress { block, imm, .. } => write!(w, " {block}, {imm}"),
529 }?;
530
531 let mut sep = " ; ";
532 for arg in dfg.inst_values(inst) {
533 if let ValueDef::Result(src, _) = dfg.value_def(arg) {
534 let imm = match dfg.insts[src] {
535 UnaryImm { imm, .. } => {
536 let mut imm = imm;
537 if dfg.ctrl_typevar(src).bits() != 0 {
538 imm = imm.sign_extend_from_width(dfg.ctrl_typevar(src).bits());
539 }
540 imm.to_string()
541 }
542 UnaryIeee16 { imm, .. } => imm.to_string(),
543 UnaryIeee32 { imm, .. } => imm.to_string(),
544 UnaryIeee64 { imm, .. } => imm.to_string(),
545 UnaryConst {
546 constant_handle,
547 opcode: Opcode::F128const,
548 } => Ieee128::try_from(dfg.constants.get(constant_handle))
549 .expect("16-byte f128 constant")
550 .to_string(),
551 UnaryConst {
552 constant_handle, ..
553 } => constant_handle.to_string(),
554 _ => continue,
555 };
556 write!(w, "{sep}{arg} = {imm}")?;
557 sep = ", ";
558 }
559 }
560 Ok(())
561}
562
563fn write_debug_tags(
564 w: &mut dyn Write,
565 func: &Function,
566 inst: Inst,
567 indent: &mut usize,
568) -> fmt::Result {
569 let tags = func.debug_tags.get(inst);
570 if !tags.is_empty() {
571 let tags = tags
572 .iter()
573 .map(|tag| format!("{tag}"))
574 .collect::<Vec<_>>()
575 .join(", ");
576 let s = format!("<{tags}> ");
577 write!(w, "{s}")?;
578 *indent = indent.saturating_sub(s.len());
579 }
580 Ok(())
581}
582
583fn write_user_stack_map_entries(w: &mut dyn Write, dfg: &DataFlowGraph, inst: Inst) -> fmt::Result {
584 let entries = match dfg.user_stack_map_entries(inst) {
585 None => return Ok(()),
586 Some(es) => es,
587 };
588 write!(w, ", stack_map=[")?;
589 let mut need_comma = false;
590 for entry in entries {
591 if need_comma {
592 write!(w, ", ")?;
593 }
594 write!(w, "{} @ {}+{}", entry.ty, entry.slot, entry.offset)?;
595 need_comma = true;
596 }
597 write!(w, "]")?;
598 Ok(())
599}
600
601struct DisplayValues<'a>(&'a [Value]);
603
604impl<'a> fmt::Display for DisplayValues<'a> {
605 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
606 for (i, val) in self.0.iter().enumerate() {
607 if i == 0 {
608 write!(f, "{val}")?;
609 } else {
610 write!(f, ", {val}")?;
611 }
612 }
613 Ok(())
614 }
615}
616
617#[cfg(test)]
618mod tests {
619 use crate::cursor::{Cursor, CursorPosition, FuncCursor};
620 use crate::ir::types;
621 use crate::ir::{Function, InstBuilder, StackSlotData, StackSlotKind, UserFuncName};
622 use alloc::string::ToString;
623
624 #[test]
625 fn basic() {
626 let mut f = Function::new();
627 assert_eq!(f.to_string(), "function u0:0() fast {\n}\n");
628
629 f.name = UserFuncName::testcase("foo");
630 assert_eq!(f.to_string(), "function %foo() fast {\n}\n");
631
632 f.create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 4, 0));
633 assert_eq!(
634 f.to_string(),
635 "function %foo() fast {\n ss0 = explicit_slot 4\n}\n"
636 );
637
638 let block = f.dfg.make_block();
639 f.layout.append_block(block);
640 assert_eq!(
641 f.to_string(),
642 "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0:\n}\n"
643 );
644
645 f.dfg.append_block_param(block, types::I8);
646 assert_eq!(
647 f.to_string(),
648 "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8):\n}\n"
649 );
650
651 f.dfg.append_block_param(block, types::F32.by(4).unwrap());
652 assert_eq!(
653 f.to_string(),
654 "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8, v1: f32x4):\n}\n"
655 );
656
657 {
658 let mut cursor = FuncCursor::new(&mut f);
659 cursor.set_position(CursorPosition::After(block));
660 cursor.ins().return_(&[])
661 };
662 assert_eq!(
663 f.to_string(),
664 "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8, v1: f32x4):\n return\n}\n"
665 );
666
667 let mut f = Function::new();
668 f.create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 4, 2));
669 assert_eq!(
670 f.to_string(),
671 "function u0:0() fast {\n ss0 = explicit_slot 4, align = 4\n}\n"
672 );
673 }
674
675 #[test]
676 fn aliases() {
677 use crate::ir::InstBuilder;
678
679 let mut func = Function::new();
680 {
681 let block0 = func.dfg.make_block();
682 let mut pos = FuncCursor::new(&mut func);
683 pos.insert_block(block0);
684
685 let v0 = pos.func.dfg.append_block_param(block0, types::I32);
687 let v1 = pos.func.dfg.append_block_param(block0, types::I32);
688 let v2 = pos.func.dfg.append_block_param(block0, types::I32);
689 pos.func.dfg.detach_block_params(block0);
690
691 let v3 = pos.func.dfg.append_block_param(block0, types::I32);
693 pos.func.dfg.change_to_alias(v0, v3);
694
695 pos.func.dfg.make_value_alias_for_serialization(v0, v2); let _dummy0 = pos.ins().iconst(types::I32, 42);
700 let v4 = pos.ins().iadd(v0, v0);
701 pos.func.dfg.change_to_alias(v1, v4);
702 let _dummy1 = pos.ins().iconst(types::I32, 23);
703 let _v7 = pos.ins().iadd(v1, v1);
704 }
705 assert_eq!(
706 func.to_string(),
707 "function u0:0() fast {\nblock0(v3: i32):\n v0 -> v3\n v2 -> v0\n v4 = iconst.i32 42\n v5 = iadd v0, v0\n v1 -> v5\n v6 = iconst.i32 23\n v7 = iadd v1, v1\n}\n"
708 );
709 }
710
711 #[test]
712 fn cold_blocks() {
713 let mut func = Function::new();
714 {
715 let mut pos = FuncCursor::new(&mut func);
716
717 let block0 = pos.func.dfg.make_block();
718 pos.insert_block(block0);
719 pos.func.layout.set_cold(block0);
720
721 let block1 = pos.func.dfg.make_block();
722 pos.insert_block(block1);
723 pos.func.dfg.append_block_param(block1, types::I32);
724 pos.func.layout.set_cold(block1);
725 }
726
727 assert_eq!(
728 func.to_string(),
729 "function u0:0() fast {\nblock0 cold:\n\nblock1(v0: i32) cold:\n}\n"
730 );
731 }
732}