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