1use crate::entity::SecondaryMap;
7use crate::ir::entities::AnyEntity;
8use crate::ir::{
9 DataFlowGraph, DisplayFunctionAnnotations, Ebb, Function, Inst, SigRef, Type, Value, ValueDef,
10 ValueLoc,
11};
12use crate::isa::{RegInfo, TargetIsa};
13use crate::packed_option::ReservedValue;
14use crate::value_label::ValueLabelsRanges;
15use crate::HashSet;
16use alloc::string::String;
17use alloc::vec::Vec;
18use core::fmt::{self, Write};
19
20pub trait FuncWriter {
22 fn write_ebb_header(
24 &mut self,
25 w: &mut dyn Write,
26 func: &Function,
27 isa: Option<&dyn TargetIsa>,
28 ebb: Ebb,
29 indent: usize,
30 ) -> fmt::Result;
31
32 fn write_instruction(
34 &mut self,
35 w: &mut dyn Write,
36 func: &Function,
37 aliases: &SecondaryMap<Value, Vec<Value>>,
38 isa: Option<&dyn TargetIsa>,
39 inst: Inst,
40 indent: usize,
41 ) -> fmt::Result;
42
43 fn write_preamble(
45 &mut self,
46 w: &mut dyn Write,
47 func: &Function,
48 regs: Option<&RegInfo>,
49 ) -> Result<bool, fmt::Error> {
50 self.super_preamble(w, func, regs)
51 }
52
53 fn super_preamble(
55 &mut self,
56 w: &mut dyn Write,
57 func: &Function,
58 regs: Option<&RegInfo>,
59 ) -> Result<bool, fmt::Error> {
60 let mut any = false;
61
62 for (ss, slot) in func.stack_slots.iter() {
63 any = true;
64 self.write_entity_definition(w, func, ss.into(), slot)?;
65 }
66
67 for (gv, gv_data) in &func.global_values {
68 any = true;
69 self.write_entity_definition(w, func, gv.into(), gv_data)?;
70 }
71
72 for (heap, heap_data) in &func.heaps {
73 if !heap_data.index_type.is_invalid() {
74 any = true;
75 self.write_entity_definition(w, func, heap.into(), heap_data)?;
76 }
77 }
78
79 for (table, table_data) in &func.tables {
80 if !table_data.index_type.is_invalid() {
81 any = true;
82 self.write_entity_definition(w, func, table.into(), table_data)?;
83 }
84 }
85
86 for (sig, sig_data) in &func.dfg.signatures {
89 any = true;
90 self.write_entity_definition(w, func, sig.into(), &sig_data.display(regs))?;
91 }
92
93 for (fnref, ext_func) in &func.dfg.ext_funcs {
94 if ext_func.signature != SigRef::reserved_value() {
95 any = true;
96 self.write_entity_definition(w, func, fnref.into(), ext_func)?;
97 }
98 }
99
100 for (jt, jt_data) in &func.jump_tables {
101 any = true;
102 self.write_entity_definition(w, func, jt.into(), jt_data)?;
103 }
104
105 Ok(any)
106 }
107
108 fn write_entity_definition(
110 &mut self,
111 w: &mut dyn Write,
112 func: &Function,
113 entity: AnyEntity,
114 value: &dyn fmt::Display,
115 ) -> fmt::Result {
116 self.super_entity_definition(w, func, entity, value)
117 }
118
119 #[allow(unused_variables)]
121 fn super_entity_definition(
122 &mut self,
123 w: &mut dyn Write,
124 func: &Function,
125 entity: AnyEntity,
126 value: &dyn fmt::Display,
127 ) -> fmt::Result {
128 writeln!(w, " {} = {}", entity, value)
129 }
130}
131
132pub struct PlainWriter;
134
135impl FuncWriter for PlainWriter {
136 fn write_instruction(
137 &mut self,
138 w: &mut dyn Write,
139 func: &Function,
140 aliases: &SecondaryMap<Value, Vec<Value>>,
141 isa: Option<&dyn TargetIsa>,
142 inst: Inst,
143 indent: usize,
144 ) -> fmt::Result {
145 write_instruction(w, func, aliases, isa, inst, indent)
146 }
147
148 fn write_ebb_header(
149 &mut self,
150 w: &mut dyn Write,
151 func: &Function,
152 isa: Option<&dyn TargetIsa>,
153 ebb: Ebb,
154 indent: usize,
155 ) -> fmt::Result {
156 write_ebb_header(w, func, isa, ebb, indent)
157 }
158}
159
160pub fn write_function(
163 w: &mut dyn Write,
164 func: &Function,
165 annotations: &DisplayFunctionAnnotations,
166) -> fmt::Result {
167 decorate_function(&mut PlainWriter, w, func, annotations)
168}
169
170fn alias_map(func: &Function) -> SecondaryMap<Value, Vec<Value>> {
172 let mut aliases = SecondaryMap::<_, Vec<_>>::new();
173 for v in func.dfg.values() {
174 if let Some(k) = func.dfg.value_alias_dest_for_serialization(v) {
176 aliases[k].push(v);
177 }
178 }
179 aliases
180}
181
182pub fn decorate_function<FW: FuncWriter>(
186 func_w: &mut FW,
187 w: &mut dyn Write,
188 func: &Function,
189 annotations: &DisplayFunctionAnnotations,
190) -> fmt::Result {
191 let regs = annotations.isa.map(TargetIsa::register_info);
192 let regs = regs.as_ref();
193
194 write!(w, "function ")?;
195 write_spec(w, func, regs)?;
196 writeln!(w, " {{")?;
197 let aliases = alias_map(func);
198 let mut any = func_w.write_preamble(w, func, regs)?;
199 for ebb in &func.layout {
200 if any {
201 writeln!(w)?;
202 }
203 decorate_ebb(func_w, w, func, &aliases, annotations, ebb)?;
204 any = true;
205 }
206 writeln!(w, "}}")
207}
208
209fn write_spec(w: &mut dyn Write, func: &Function, regs: Option<&RegInfo>) -> fmt::Result {
214 write!(w, "{}{}", func.name, func.signature.display(regs))
215}
216
217fn write_arg(
222 w: &mut dyn Write,
223 func: &Function,
224 regs: Option<&RegInfo>,
225 arg: Value,
226) -> fmt::Result {
227 write!(w, "{}: {}", arg, func.dfg.value_type(arg))?;
228 let loc = func.locations[arg];
229 if loc.is_assigned() {
230 write!(w, " [{}]", loc.display(regs))?
231 }
232
233 Ok(())
234}
235
236pub fn write_ebb_header(
243 w: &mut dyn Write,
244 func: &Function,
245 isa: Option<&dyn TargetIsa>,
246 ebb: Ebb,
247 indent: usize,
248) -> fmt::Result {
249 write!(w, "{1:0$}{2}", indent - 4, "", ebb)?;
251
252 let regs = isa.map(TargetIsa::register_info);
253 let regs = regs.as_ref();
254
255 let mut args = func.dfg.ebb_params(ebb).iter().cloned();
256 match args.next() {
257 None => return writeln!(w, ":"),
258 Some(arg) => {
259 write!(w, "(")?;
260 write_arg(w, func, regs, arg)?;
261 }
262 }
263 for arg in args {
265 write!(w, ", ")?;
266 write_arg(w, func, regs, arg)?;
267 }
268 writeln!(w, "):")
269}
270
271fn write_valueloc(w: &mut dyn Write, loc: ValueLoc, regs: &RegInfo) -> fmt::Result {
272 match loc {
273 ValueLoc::Reg(r) => write!(w, "{}", regs.display_regunit(r)),
274 ValueLoc::Stack(ss) => write!(w, "{}", ss),
275 ValueLoc::Unassigned => write!(w, "?"),
276 }
277}
278
279fn write_value_range_markers(
280 w: &mut dyn Write,
281 val_ranges: &ValueLabelsRanges,
282 regs: &RegInfo,
283 offset: u32,
284 indent: usize,
285) -> fmt::Result {
286 let mut result = String::new();
287 let mut shown = HashSet::new();
288 for (val, rng) in val_ranges {
289 for i in (0..rng.len()).rev() {
290 if rng[i].start == offset {
291 write!(&mut result, " {}@", val)?;
292 write_valueloc(&mut result, rng[i].loc, regs)?;
293 shown.insert(val);
294 break;
295 }
296 }
297 }
298 for (val, rng) in val_ranges {
299 for i in (0..rng.len()).rev() {
300 if rng[i].end == offset && !shown.contains(val) {
301 write!(&mut result, " {}\u{2620}", val)?;
302 break;
303 }
304 }
305 }
306 if !result.is_empty() {
307 writeln!(w, ";{1:0$}; {2}", indent + 24, "", result)?;
308 }
309 Ok(())
310}
311
312fn decorate_ebb<FW: FuncWriter>(
313 func_w: &mut FW,
314 w: &mut dyn Write,
315 func: &Function,
316 aliases: &SecondaryMap<Value, Vec<Value>>,
317 annotations: &DisplayFunctionAnnotations,
318 ebb: Ebb,
319) -> fmt::Result {
320 let indent = if func.encodings.is_empty() && func.srclocs.is_empty() {
322 4
323 } else {
324 36
325 };
326 let isa = annotations.isa;
327
328 func_w.write_ebb_header(w, func, isa, ebb, indent)?;
329 for a in func.dfg.ebb_params(ebb).iter().cloned() {
330 write_value_aliases(w, aliases, a, indent)?;
331 }
332
333 if let Some(isa) = isa {
334 if !func.offsets.is_empty() {
335 let encinfo = isa.encoding_info();
336 let regs = &isa.register_info();
337 for (offset, inst, size) in func.inst_offsets(ebb, &encinfo) {
338 func_w.write_instruction(w, func, aliases, Some(isa), inst, indent)?;
339 if size > 0 {
340 if let Some(val_ranges) = annotations.value_ranges {
341 write_value_range_markers(w, val_ranges, regs, offset + size, indent)?;
342 }
343 }
344 }
345 return Ok(());
346 }
347 }
348
349 for inst in func.layout.ebb_insts(ebb) {
350 func_w.write_instruction(w, func, aliases, isa, inst, indent)?;
351 }
352
353 Ok(())
354}
355
356fn type_suffix(func: &Function, inst: Inst) -> Option<Type> {
366 let inst_data = &func.dfg[inst];
367 let constraints = inst_data.opcode().constraints();
368
369 if !constraints.is_polymorphic() {
370 return None;
371 }
372
373 if constraints.use_typevar_operand() {
376 let ctrl_var = inst_data.typevar_operand(&func.dfg.value_lists).unwrap();
377 let def_ebb = match func.dfg.value_def(ctrl_var) {
378 ValueDef::Result(instr, _) => func.layout.inst_ebb(instr),
379 ValueDef::Param(ebb, _) => Some(ebb),
380 };
381 if def_ebb.is_some() && def_ebb == func.layout.inst_ebb(inst) {
382 return None;
383 }
384 }
385
386 let rtype = func.dfg.ctrl_typevar(inst);
387 assert!(
388 !rtype.is_invalid(),
389 "Polymorphic instruction must produce a result"
390 );
391 Some(rtype)
392}
393
394fn write_value_aliases(
396 w: &mut dyn Write,
397 aliases: &SecondaryMap<Value, Vec<Value>>,
398 target: Value,
399 indent: usize,
400) -> fmt::Result {
401 let mut todo_stack = vec![target];
402 while let Some(target) = todo_stack.pop() {
403 for &a in &aliases[target] {
404 writeln!(w, "{1:0$}{2} -> {3}", indent, "", a, target)?;
405 todo_stack.push(a);
406 }
407 }
408
409 Ok(())
410}
411
412fn write_instruction(
413 w: &mut dyn Write,
414 func: &Function,
415 aliases: &SecondaryMap<Value, Vec<Value>>,
416 isa: Option<&dyn TargetIsa>,
417 inst: Inst,
418 indent: usize,
419) -> fmt::Result {
420 let mut s = String::with_capacity(16);
422
423 let srcloc = func.srclocs[inst];
425 if !srcloc.is_default() {
426 write!(s, "{} ", srcloc)?;
427 }
428
429 if let Some(enc) = func.encodings.get(inst).cloned() {
431 if let Some(isa) = isa {
432 write!(s, "[{}", isa.encoding_info().display(enc))?;
433 if !func.locations.is_empty() {
435 let regs = isa.register_info();
436 for &r in func.dfg.inst_results(inst) {
437 write!(s, ",{}", func.locations[r].display(®s))?
438 }
439 }
440 write!(s, "] ")?;
441 } else {
442 write!(s, "[{}] ", enc)?;
443 }
444 }
445
446 write!(w, "{1:0$}", indent, s)?;
448
449 let mut has_results = false;
451 for r in func.dfg.inst_results(inst) {
452 if !has_results {
453 has_results = true;
454 write!(w, "{}", r)?;
455 } else {
456 write!(w, ", {}", r)?;
457 }
458 }
459 if has_results {
460 write!(w, " = ")?;
461 }
462
463 let opcode = func.dfg[inst].opcode();
465
466 match type_suffix(func, inst) {
467 Some(suf) => write!(w, "{}.{}", opcode, suf)?,
468 None => write!(w, "{}", opcode)?,
469 }
470
471 write_operands(w, &func.dfg, isa, inst)?;
472 writeln!(w)?;
473
474 for r in func.dfg.inst_results(inst) {
476 write_value_aliases(w, aliases, *r, indent)?;
477 }
478 Ok(())
479}
480
481pub fn write_operands(
483 w: &mut dyn Write,
484 dfg: &DataFlowGraph,
485 isa: Option<&dyn TargetIsa>,
486 inst: Inst,
487) -> fmt::Result {
488 let pool = &dfg.value_lists;
489 use crate::ir::instructions::InstructionData::*;
490 match dfg[inst] {
491 Unary { arg, .. } => write!(w, " {}", arg),
492 UnaryImm { imm, .. } => write!(w, " {}", imm),
493 UnaryIeee32 { imm, .. } => write!(w, " {}", imm),
494 UnaryIeee64 { imm, .. } => write!(w, " {}", imm),
495 UnaryBool { imm, .. } => write!(w, " {}", imm),
496 UnaryGlobalValue { global_value, .. } => write!(w, " {}", global_value),
497 Binary { args, .. } => write!(w, " {}, {}", args[0], args[1]),
498 BinaryImm { arg, imm, .. } => write!(w, " {}, {}", arg, imm),
499 Ternary { args, .. } => write!(w, " {}, {}, {}", args[0], args[1], args[2]),
500 MultiAry { ref args, .. } => {
501 if args.is_empty() {
502 write!(w, "")
503 } else {
504 write!(w, " {}", DisplayValues(args.as_slice(pool)))
505 }
506 }
507 NullAry { .. } => write!(w, " "),
508 InsertLane { lane, args, .. } => write!(w, " {}, {}, {}", args[0], lane, args[1]),
509 ExtractLane { lane, arg, .. } => write!(w, " {}, {}", arg, lane),
510 UnaryConst {
511 constant_handle, ..
512 } => {
513 let constant_data = dfg.constants.get(constant_handle);
514 write!(w, " {}", constant_data)
515 }
516 Shuffle { mask, args, .. } => {
517 let data = dfg.immediates.get(mask).expect(
518 "Expected the shuffle mask to already be inserted into the immediates table",
519 );
520 write!(w, " {}, {}, {}", args[0], args[1], data)
521 }
522 IntCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]),
523 IntCompareImm { cond, arg, imm, .. } => write!(w, " {} {}, {}", cond, arg, imm),
524 IntCond { cond, arg, .. } => write!(w, " {} {}", cond, arg),
525 FloatCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]),
526 FloatCond { cond, arg, .. } => write!(w, " {} {}", cond, arg),
527 IntSelect { cond, args, .. } => {
528 write!(w, " {} {}, {}, {}", cond, args[0], args[1], args[2])
529 }
530 Jump {
531 destination,
532 ref args,
533 ..
534 } => {
535 write!(w, " {}", destination)?;
536 write_ebb_args(w, args.as_slice(pool))
537 }
538 Branch {
539 destination,
540 ref args,
541 ..
542 } => {
543 let args = args.as_slice(pool);
544 write!(w, " {}, {}", args[0], destination)?;
545 write_ebb_args(w, &args[1..])
546 }
547 BranchInt {
548 cond,
549 destination,
550 ref args,
551 ..
552 } => {
553 let args = args.as_slice(pool);
554 write!(w, " {} {}, {}", cond, args[0], destination)?;
555 write_ebb_args(w, &args[1..])
556 }
557 BranchFloat {
558 cond,
559 destination,
560 ref args,
561 ..
562 } => {
563 let args = args.as_slice(pool);
564 write!(w, " {} {}, {}", cond, args[0], destination)?;
565 write_ebb_args(w, &args[1..])
566 }
567 BranchIcmp {
568 cond,
569 destination,
570 ref args,
571 ..
572 } => {
573 let args = args.as_slice(pool);
574 write!(w, " {} {}, {}, {}", cond, args[0], args[1], destination)?;
575 write_ebb_args(w, &args[2..])
576 }
577 BranchTable {
578 arg,
579 destination,
580 table,
581 ..
582 } => write!(w, " {}, {}, {}", arg, destination, table),
583 BranchTableBase { table, .. } => write!(w, " {}", table),
584 BranchTableEntry {
585 args, imm, table, ..
586 } => write!(w, " {}, {}, {}, {}", args[0], args[1], imm, table),
587 IndirectJump { arg, table, .. } => write!(w, " {}, {}", arg, table),
588 Call {
589 func_ref, ref args, ..
590 } => write!(w, " {}({})", func_ref, DisplayValues(args.as_slice(pool))),
591 CallIndirect {
592 sig_ref, ref args, ..
593 } => {
594 let args = args.as_slice(pool);
595 write!(
596 w,
597 " {}, {}({})",
598 sig_ref,
599 args[0],
600 DisplayValues(&args[1..])
601 )
602 }
603 FuncAddr { func_ref, .. } => write!(w, " {}", func_ref),
604 StackLoad {
605 stack_slot, offset, ..
606 } => write!(w, " {}{}", stack_slot, offset),
607 StackStore {
608 arg,
609 stack_slot,
610 offset,
611 ..
612 } => write!(w, " {}, {}{}", arg, stack_slot, offset),
613 HeapAddr { heap, arg, imm, .. } => write!(w, " {}, {}, {}", heap, arg, imm),
614 TableAddr { table, arg, .. } => write!(w, " {}, {}", table, arg),
615 Load {
616 flags, arg, offset, ..
617 } => write!(w, "{} {}{}", flags, arg, offset),
618 LoadComplex {
619 flags,
620 ref args,
621 offset,
622 ..
623 } => {
624 let args = args.as_slice(pool);
625 write!(
626 w,
627 "{} {}{}",
628 flags,
629 DisplayValuesWithDelimiter(&args, '+'),
630 offset
631 )
632 }
633 Store {
634 flags,
635 args,
636 offset,
637 ..
638 } => write!(w, "{} {}, {}{}", flags, args[0], args[1], offset),
639 StoreComplex {
640 flags,
641 ref args,
642 offset,
643 ..
644 } => {
645 let args = args.as_slice(pool);
646 write!(
647 w,
648 "{} {}, {}{}",
649 flags,
650 args[0],
651 DisplayValuesWithDelimiter(&args[1..], '+'),
652 offset
653 )
654 }
655 RegMove { arg, src, dst, .. } => {
656 if let Some(isa) = isa {
657 let regs = isa.register_info();
658 write!(
659 w,
660 " {}, {} -> {}",
661 arg,
662 regs.display_regunit(src),
663 regs.display_regunit(dst)
664 )
665 } else {
666 write!(w, " {}, %{} -> %{}", arg, src, dst)
667 }
668 }
669 CopySpecial { src, dst, .. } => {
670 if let Some(isa) = isa {
671 let regs = isa.register_info();
672 write!(
673 w,
674 " {} -> {}",
675 regs.display_regunit(src),
676 regs.display_regunit(dst)
677 )
678 } else {
679 write!(w, " %{} -> %{}", src, dst)
680 }
681 }
682 CopyToSsa { src, .. } => {
683 if let Some(isa) = isa {
684 let regs = isa.register_info();
685 write!(w, " {}", regs.display_regunit(src))
686 } else {
687 write!(w, " %{}", src)
688 }
689 }
690 RegSpill { arg, src, dst, .. } => {
691 if let Some(isa) = isa {
692 let regs = isa.register_info();
693 write!(w, " {}, {} -> {}", arg, regs.display_regunit(src), dst)
694 } else {
695 write!(w, " {}, %{} -> {}", arg, src, dst)
696 }
697 }
698 RegFill { arg, src, dst, .. } => {
699 if let Some(isa) = isa {
700 let regs = isa.register_info();
701 write!(w, " {}, {} -> {}", arg, src, regs.display_regunit(dst))
702 } else {
703 write!(w, " {}, {} -> %{}", arg, src, dst)
704 }
705 }
706 Trap { code, .. } => write!(w, " {}", code),
707 CondTrap { arg, code, .. } => write!(w, " {}, {}", arg, code),
708 IntCondTrap {
709 cond, arg, code, ..
710 } => write!(w, " {} {}, {}", cond, arg, code),
711 FloatCondTrap {
712 cond, arg, code, ..
713 } => write!(w, " {} {}, {}", cond, arg, code),
714 }
715}
716
717fn write_ebb_args(w: &mut dyn Write, args: &[Value]) -> fmt::Result {
719 if args.is_empty() {
720 Ok(())
721 } else {
722 write!(w, "({})", DisplayValues(args))
723 }
724}
725
726struct DisplayValues<'a>(&'a [Value]);
728
729impl<'a> fmt::Display for DisplayValues<'a> {
730 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
731 for (i, val) in self.0.iter().enumerate() {
732 if i == 0 {
733 write!(f, "{}", val)?;
734 } else {
735 write!(f, ", {}", val)?;
736 }
737 }
738 Ok(())
739 }
740}
741
742struct DisplayValuesWithDelimiter<'a>(&'a [Value], char);
743
744impl<'a> fmt::Display for DisplayValuesWithDelimiter<'a> {
745 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
746 for (i, val) in self.0.iter().enumerate() {
747 if i == 0 {
748 write!(f, "{}", val)?;
749 } else {
750 write!(f, "{}{}", self.1, val)?;
751 }
752 }
753 Ok(())
754 }
755}
756
757#[cfg(test)]
758mod tests {
759 use crate::cursor::{Cursor, CursorPosition, FuncCursor};
760 use crate::ir::types;
761 use crate::ir::{ExternalName, Function, InstBuilder, StackSlotData, StackSlotKind};
762 use alloc::string::ToString;
763
764 #[test]
765 fn basic() {
766 let mut f = Function::new();
767 assert_eq!(f.to_string(), "function u0:0() fast {\n}\n");
768
769 f.name = ExternalName::testcase("foo");
770 assert_eq!(f.to_string(), "function %foo() fast {\n}\n");
771
772 f.create_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 4));
773 assert_eq!(
774 f.to_string(),
775 "function %foo() fast {\n ss0 = explicit_slot 4\n}\n"
776 );
777
778 let ebb = f.dfg.make_ebb();
779 f.layout.append_ebb(ebb);
780 assert_eq!(
781 f.to_string(),
782 "function %foo() fast {\n ss0 = explicit_slot 4\n\nebb0:\n}\n"
783 );
784
785 f.dfg.append_ebb_param(ebb, types::I8);
786 assert_eq!(
787 f.to_string(),
788 "function %foo() fast {\n ss0 = explicit_slot 4\n\nebb0(v0: i8):\n}\n"
789 );
790
791 f.dfg.append_ebb_param(ebb, types::F32.by(4).unwrap());
792 assert_eq!(
793 f.to_string(),
794 "function %foo() fast {\n ss0 = explicit_slot 4\n\nebb0(v0: i8, v1: f32x4):\n}\n"
795 );
796
797 {
798 let mut cursor = FuncCursor::new(&mut f);
799 cursor.set_position(CursorPosition::After(ebb));
800 cursor.ins().return_(&[])
801 };
802 assert_eq!(
803 f.to_string(),
804 "function %foo() fast {\n ss0 = explicit_slot 4\n\nebb0(v0: i8, v1: f32x4):\n return\n}\n"
805 );
806 }
807
808 #[test]
809 fn aliases() {
810 use crate::ir::InstBuilder;
811
812 let mut func = Function::new();
813 {
814 let ebb0 = func.dfg.make_ebb();
815 let mut pos = FuncCursor::new(&mut func);
816 pos.insert_ebb(ebb0);
817
818 let v0 = pos.func.dfg.append_ebb_param(ebb0, types::I32);
820 let v1 = pos.func.dfg.append_ebb_param(ebb0, types::I32);
821 let v2 = pos.func.dfg.append_ebb_param(ebb0, types::I32);
822 pos.func.dfg.detach_ebb_params(ebb0);
823
824 let v3 = pos.func.dfg.append_ebb_param(ebb0, types::I32);
826 pos.func.dfg.change_to_alias(v0, v3);
827
828 pos.func.dfg.make_value_alias_for_serialization(v0, v2); let _dummy0 = pos.ins().iconst(types::I32, 42);
833 let v4 = pos.ins().iadd(v0, v0);
834 pos.func.dfg.change_to_alias(v1, v4);
835 let _dummy1 = pos.ins().iconst(types::I32, 23);
836 let _v7 = pos.ins().iadd(v1, v1);
837 }
838 assert_eq!(
839 func.to_string(),
840 "function u0:0() fast {\nebb0(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"
841 );
842 }
843}