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