1 //! Converting Cranelift IR to text.
2 //!
3 //! The `write` module provides the `write_function` function which converts an IR `Function` to an
4 //! equivalent textual form. This textual form can be read back by the `cranelift-reader` crate.
5 
6 use crate::entity::SecondaryMap;
7 use crate::ir::entities::AnyEntity;
8 use crate::ir::immediates::Ieee128;
9 use crate::ir::pcc::Fact;
10 use crate::ir::{Block, DataFlowGraph, Function, Inst, Opcode, SigRef, Type, Value, ValueDef};
11 use crate::packed_option::ReservedValue;
12 use alloc::string::{String, ToString};
13 use alloc::vec::Vec;
14 use core::fmt::{self, Write};
15 
16 /// A `FuncWriter` used to decorate functions during printing.
17 pub trait FuncWriter {
18     /// Write the basic block header for the current function.
19     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     /// Write the given `inst` to `w`.
28     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     /// Write the preamble to `w`. By default, this uses `write_entity_definition`.
38     fn write_preamble(&mut self, w: &mut dyn Write, func: &Function) -> Result<bool, fmt::Error> {
39         self.super_preamble(w, func)
40     }
41 
42     /// Default impl of `write_preamble`
43     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         // Write out all signatures before functions since function declarations can refer to
68         // signatures.
69         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     /// Write an entity definition defined in the preamble to `w`.
101     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     /// Default impl of `write_entity_definition`
113     #[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 
130 /// A `PlainWriter` that doesn't decorate the function.
131 pub struct PlainWriter;
132 
133 impl 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 
156 /// Write `func` to `w` as equivalent text.
157 /// Use `isa` to emit ISA-dependent annotations.
158 pub fn write_function(w: &mut dyn Write, func: &Function) -> fmt::Result {
159     decorate_function(&mut PlainWriter, w, func)
160 }
161 
162 /// Create a reverse-alias map from a value to all aliases having that value as a direct target
163 fn alias_map(func: &Function) -> SecondaryMap<Value, Vec<Value>> {
164     let mut aliases = SecondaryMap::<_, Vec<_>>::new();
165     for v in func.dfg.values() {
166         // VADFS returns the immediate target of an alias
167         if let Some(k) = func.dfg.value_alias_dest_for_serialization(v) {
168             aliases[k].push(v);
169         }
170     }
171     aliases
172 }
173 
174 /// Writes `func` to `w` as text.
175 /// write_function_plain is passed as 'closure' to print instructions as text.
176 /// pretty_function_error is passed as 'closure' to add error decoration.
177 pub 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_function_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 
197 //----------------------------------------------------------------------
198 //
199 // Function spec.
200 
201 /// Writes the spec (name and signature) of 'func' to 'w' as text.
202 pub fn write_function_spec(w: &mut dyn Write, func: &Function) -> fmt::Result {
203     write!(w, "{}{}", func.name, func.signature)
204 }
205 
206 //----------------------------------------------------------------------
207 //
208 // Basic blocks
209 
210 fn write_arg(w: &mut dyn Write, func: &Function, arg: Value) -> fmt::Result {
211     let ty = func.dfg.value_type(arg);
212     if let Some(f) = &func.dfg.facts[arg] {
213         write!(w, "{arg} ! {f}: {ty}")
214     } else {
215         write!(w, "{arg}: {ty}")
216     }
217 }
218 
219 /// Write out the basic block header, outdented:
220 ///
221 ///    block1:
222 ///    block1(v1: i32):
223 ///    block10(v4: f64, v5: i8):
224 ///
225 pub fn write_block_header(
226     w: &mut dyn Write,
227     func: &Function,
228     block: Block,
229     indent: usize,
230 ) -> fmt::Result {
231     let cold = if func.layout.is_cold(block) {
232         " cold"
233     } else {
234         ""
235     };
236 
237     // The `indent` is the instruction indentation. block headers are 4 spaces out from that.
238     write!(w, "{1:0$}{2}", indent - 4, "", block)?;
239 
240     let mut args = func.dfg.block_params(block).iter().cloned();
241     match args.next() {
242         None => return writeln!(w, "{cold}:"),
243         Some(arg) => {
244             write!(w, "(")?;
245             write_arg(w, func, arg)?;
246         }
247     }
248     // Remaining arguments.
249     for arg in args {
250         write!(w, ", ")?;
251         write_arg(w, func, arg)?;
252     }
253     writeln!(w, "){cold}:")
254 }
255 
256 fn decorate_block<FW: FuncWriter>(
257     func_w: &mut FW,
258     w: &mut dyn Write,
259     func: &Function,
260     aliases: &SecondaryMap<Value, Vec<Value>>,
261     block: Block,
262 ) -> fmt::Result {
263     // Indent all instructions if any srclocs are present.
264     let indent = if func.rel_srclocs().is_empty() { 4 } else { 36 };
265 
266     func_w.write_block_header(w, func, block, indent)?;
267     for a in func.dfg.block_params(block).iter().cloned() {
268         write_value_aliases(w, aliases, a, indent)?;
269     }
270 
271     for inst in func.layout.block_insts(block) {
272         func_w.write_instruction(w, func, aliases, inst, indent)?;
273     }
274 
275     Ok(())
276 }
277 
278 //----------------------------------------------------------------------
279 //
280 // Instructions
281 
282 // Should `inst` be printed with a type suffix?
283 //
284 // Polymorphic instructions may need a suffix indicating the value of the controlling type variable
285 // if it can't be trivially inferred.
286 //
287 fn type_suffix(func: &Function, inst: Inst) -> Option<Type> {
288     let inst_data = &func.dfg.insts[inst];
289     let constraints = inst_data.opcode().constraints();
290 
291     if !constraints.is_polymorphic() {
292         return None;
293     }
294 
295     // If the controlling type variable can be inferred from the type of the designated value input
296     // operand, we don't need the type suffix.
297     if constraints.use_typevar_operand() {
298         let ctrl_var = inst_data.typevar_operand(&func.dfg.value_lists).unwrap();
299         let def_block = match func.dfg.value_def(ctrl_var) {
300             ValueDef::Result(instr, _) => func.layout.inst_block(instr),
301             ValueDef::Param(block, _) => Some(block),
302             ValueDef::Union(..) => None,
303         };
304         if def_block.is_some() && def_block == func.layout.inst_block(inst) {
305             return None;
306         }
307     }
308 
309     let rtype = func.dfg.ctrl_typevar(inst);
310     assert!(
311         !rtype.is_invalid(),
312         "Polymorphic instruction must produce a result"
313     );
314     Some(rtype)
315 }
316 
317 /// Write out any aliases to the given target, including indirect aliases
318 fn write_value_aliases(
319     w: &mut dyn Write,
320     aliases: &SecondaryMap<Value, Vec<Value>>,
321     target: Value,
322     indent: usize,
323 ) -> fmt::Result {
324     let mut todo_stack = vec![target];
325     while let Some(target) = todo_stack.pop() {
326         for &a in &aliases[target] {
327             writeln!(w, "{1:0$}{2} -> {3}", indent, "", a, target)?;
328             todo_stack.push(a);
329         }
330     }
331 
332     Ok(())
333 }
334 
335 fn write_instruction(
336     w: &mut dyn Write,
337     func: &Function,
338     aliases: &SecondaryMap<Value, Vec<Value>>,
339     inst: Inst,
340     indent: usize,
341 ) -> fmt::Result {
342     // Prefix containing source location, encoding, and value locations.
343     let mut s = String::with_capacity(16);
344 
345     // Source location goes first.
346     let srcloc = func.srcloc(inst);
347     if !srcloc.is_default() {
348         write!(s, "{srcloc} ")?;
349     }
350 
351     // Write out prefix and indent the instruction.
352     write!(w, "{s:indent$}")?;
353 
354     // Write out the result values, if any.
355     let mut has_results = false;
356     for r in func.dfg.inst_results(inst) {
357         if !has_results {
358             has_results = true;
359             write!(w, "{r}")?;
360         } else {
361             write!(w, ", {r}")?;
362         }
363         if let Some(f) = &func.dfg.facts[*r] {
364             write!(w, " ! {f}")?;
365         }
366     }
367     if has_results {
368         write!(w, " = ")?;
369     }
370 
371     // Then the opcode, possibly with a '.type' suffix.
372     let opcode = func.dfg.insts[inst].opcode();
373 
374     match type_suffix(func, inst) {
375         Some(suf) => write!(w, "{opcode}.{suf}")?,
376         None => write!(w, "{opcode}")?,
377     }
378 
379     write_operands(w, &func.dfg, inst)?;
380     writeln!(w)?;
381 
382     // Value aliases come out on lines after the instruction defining the referent.
383     for r in func.dfg.inst_results(inst) {
384         write_value_aliases(w, aliases, *r, indent)?;
385     }
386     Ok(())
387 }
388 
389 /// Write the operands of `inst` to `w` with a prepended space.
390 pub fn write_operands(w: &mut dyn Write, dfg: &DataFlowGraph, inst: Inst) -> fmt::Result {
391     let pool = &dfg.value_lists;
392     let jump_tables = &dfg.jump_tables;
393     let exception_tables = &dfg.exception_tables;
394     use crate::ir::instructions::InstructionData::*;
395     let ctrl_ty = dfg.ctrl_typevar(inst);
396     match dfg.insts[inst] {
397         AtomicRmw { op, args, .. } => write!(w, " {} {}, {}", op, args[0], args[1]),
398         AtomicCas { args, .. } => write!(w, " {}, {}, {}", args[0], args[1], args[2]),
399         LoadNoOffset { flags, arg, .. } => write!(w, "{flags} {arg}"),
400         StoreNoOffset { flags, args, .. } => write!(w, "{} {}, {}", flags, args[0], args[1]),
401         Unary { arg, .. } => write!(w, " {arg}"),
402         UnaryImm { imm, .. } => write!(w, " {}", {
403             let mut imm = imm;
404             if ctrl_ty.bits() != 0 {
405                 imm = imm.sign_extend_from_width(ctrl_ty.bits());
406             }
407             imm
408         }),
409         UnaryIeee16 { imm, .. } => write!(w, " {imm}"),
410         UnaryIeee32 { imm, .. } => write!(w, " {imm}"),
411         UnaryIeee64 { imm, .. } => write!(w, " {imm}"),
412         UnaryGlobalValue { global_value, .. } => write!(w, " {global_value}"),
413         UnaryConst {
414             constant_handle, ..
415         } => write!(w, " {constant_handle}"),
416         Binary { args, .. } => write!(w, " {}, {}", args[0], args[1]),
417         BinaryImm8 { arg, imm, .. } => write!(w, " {arg}, {imm}"),
418         BinaryImm64 { arg, imm, .. } => write!(w, " {}, {}", arg, {
419             let mut imm = imm;
420             if ctrl_ty.bits() != 0 {
421                 imm = imm.sign_extend_from_width(ctrl_ty.bits());
422             }
423             imm
424         }),
425         Ternary { args, .. } => write!(w, " {}, {}, {}", args[0], args[1], args[2]),
426         MultiAry { ref args, .. } => {
427             if args.is_empty() {
428                 write!(w, "")
429             } else {
430                 write!(w, " {}", DisplayValues(args.as_slice(pool)))
431             }
432         }
433         NullAry { .. } => write!(w, " "),
434         TernaryImm8 { imm, args, .. } => write!(w, " {}, {}, {}", args[0], args[1], imm),
435         Shuffle { imm, args, .. } => {
436             let data = dfg.immediates.get(imm).expect(
437                 "Expected the shuffle mask to already be inserted into the immediates table",
438             );
439             write!(w, " {}, {}, {}", args[0], args[1], data)
440         }
441         IntCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]),
442         IntCompareImm { cond, arg, imm, .. } => write!(w, " {} {}, {}", cond, arg, {
443             let mut imm = imm;
444             if ctrl_ty.bits() != 0 {
445                 imm = imm.sign_extend_from_width(ctrl_ty.bits());
446             }
447             imm
448         }),
449         IntAddTrap { args, code, .. } => write!(w, " {}, {}, {}", args[0], args[1], code),
450         FloatCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]),
451         Jump { destination, .. } => {
452             write!(w, " {}", destination.display(pool))
453         }
454         Brif {
455             arg,
456             blocks: [block_then, block_else],
457             ..
458         } => {
459             write!(w, " {}, {}", arg, block_then.display(pool))?;
460             write!(w, ", {}", block_else.display(pool))
461         }
462         BranchTable { arg, table, .. } => {
463             write!(w, " {}, {}", arg, jump_tables[table].display(pool))
464         }
465         Call {
466             func_ref, ref args, ..
467         } => {
468             write!(w, " {}({})", func_ref, DisplayValues(args.as_slice(pool)))?;
469             write_user_stack_map_entries(w, dfg, inst)
470         }
471         CallIndirect {
472             sig_ref, ref args, ..
473         } => {
474             let args = args.as_slice(pool);
475             write!(
476                 w,
477                 " {}, {}({})",
478                 sig_ref,
479                 args[0],
480                 DisplayValues(&args[1..])
481             )?;
482             write_user_stack_map_entries(w, dfg, inst)
483         }
484         TryCall {
485             func_ref,
486             ref args,
487             exception,
488             ..
489         } => {
490             write!(
491                 w,
492                 " {}({}), {}",
493                 func_ref,
494                 DisplayValues(args.as_slice(pool)),
495                 exception_tables[exception].display(pool),
496             )
497         }
498         TryCallIndirect {
499             ref args,
500             exception,
501             ..
502         } => {
503             let args = args.as_slice(pool);
504             write!(
505                 w,
506                 " {}({}), {}",
507                 args[0],
508                 DisplayValues(&args[1..]),
509                 exception_tables[exception].display(pool),
510             )
511         }
512         FuncAddr { func_ref, .. } => write!(w, " {func_ref}"),
513         StackLoad {
514             stack_slot, offset, ..
515         } => write!(w, " {stack_slot}{offset}"),
516         StackStore {
517             arg,
518             stack_slot,
519             offset,
520             ..
521         } => write!(w, " {arg}, {stack_slot}{offset}"),
522         DynamicStackLoad {
523             dynamic_stack_slot, ..
524         } => write!(w, " {dynamic_stack_slot}"),
525         DynamicStackStore {
526             arg,
527             dynamic_stack_slot,
528             ..
529         } => write!(w, " {arg}, {dynamic_stack_slot}"),
530         Load {
531             flags, arg, offset, ..
532         } => write!(w, "{flags} {arg}{offset}"),
533         Store {
534             flags,
535             args,
536             offset,
537             ..
538         } => write!(w, "{} {}, {}{}", flags, args[0], args[1], offset),
539         Trap { code, .. } => write!(w, " {code}"),
540         CondTrap { arg, code, .. } => write!(w, " {arg}, {code}"),
541     }?;
542 
543     let mut sep = "  ; ";
544     for arg in dfg.inst_values(inst) {
545         if let ValueDef::Result(src, _) = dfg.value_def(arg) {
546             let imm = match dfg.insts[src] {
547                 UnaryImm { imm, .. } => {
548                     let mut imm = imm;
549                     if dfg.ctrl_typevar(src).bits() != 0 {
550                         imm = imm.sign_extend_from_width(dfg.ctrl_typevar(src).bits());
551                     }
552                     imm.to_string()
553                 }
554                 UnaryIeee16 { imm, .. } => imm.to_string(),
555                 UnaryIeee32 { imm, .. } => imm.to_string(),
556                 UnaryIeee64 { imm, .. } => imm.to_string(),
557                 UnaryConst {
558                     constant_handle,
559                     opcode: Opcode::F128const,
560                 } => Ieee128::try_from(dfg.constants.get(constant_handle))
561                     .expect("16-byte f128 constant")
562                     .to_string(),
563                 UnaryConst {
564                     constant_handle, ..
565                 } => constant_handle.to_string(),
566                 _ => continue,
567             };
568             write!(w, "{sep}{arg} = {imm}")?;
569             sep = ", ";
570         }
571     }
572     Ok(())
573 }
574 
575 fn write_user_stack_map_entries(w: &mut dyn Write, dfg: &DataFlowGraph, inst: Inst) -> fmt::Result {
576     let entries = match dfg.user_stack_map_entries(inst) {
577         None => return Ok(()),
578         Some(es) => es,
579     };
580     write!(w, ", stack_map=[")?;
581     let mut need_comma = false;
582     for entry in entries {
583         if need_comma {
584             write!(w, ", ")?;
585         }
586         write!(w, "{} @ {}+{}", entry.ty, entry.slot, entry.offset)?;
587         need_comma = true;
588     }
589     write!(w, "]")?;
590     Ok(())
591 }
592 
593 /// Displayable slice of values.
594 struct DisplayValues<'a>(&'a [Value]);
595 
596 impl<'a> fmt::Display for DisplayValues<'a> {
597     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
598         for (i, val) in self.0.iter().enumerate() {
599             if i == 0 {
600                 write!(f, "{val}")?;
601             } else {
602                 write!(f, ", {val}")?;
603             }
604         }
605         Ok(())
606     }
607 }
608 
609 #[cfg(test)]
610 mod tests {
611     use crate::cursor::{Cursor, CursorPosition, FuncCursor};
612     use crate::ir::types;
613     use crate::ir::{Function, InstBuilder, StackSlotData, StackSlotKind, UserFuncName};
614     use alloc::string::ToString;
615 
616     #[test]
617     fn basic() {
618         let mut f = Function::new();
619         assert_eq!(f.to_string(), "function u0:0() fast {\n}\n");
620 
621         f.name = UserFuncName::testcase("foo");
622         assert_eq!(f.to_string(), "function %foo() fast {\n}\n");
623 
624         f.create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 4, 0));
625         assert_eq!(
626             f.to_string(),
627             "function %foo() fast {\n    ss0 = explicit_slot 4\n}\n"
628         );
629 
630         let block = f.dfg.make_block();
631         f.layout.append_block(block);
632         assert_eq!(
633             f.to_string(),
634             "function %foo() fast {\n    ss0 = explicit_slot 4\n\nblock0:\n}\n"
635         );
636 
637         f.dfg.append_block_param(block, types::I8);
638         assert_eq!(
639             f.to_string(),
640             "function %foo() fast {\n    ss0 = explicit_slot 4\n\nblock0(v0: i8):\n}\n"
641         );
642 
643         f.dfg.append_block_param(block, types::F32.by(4).unwrap());
644         assert_eq!(
645             f.to_string(),
646             "function %foo() fast {\n    ss0 = explicit_slot 4\n\nblock0(v0: i8, v1: f32x4):\n}\n"
647         );
648 
649         {
650             let mut cursor = FuncCursor::new(&mut f);
651             cursor.set_position(CursorPosition::After(block));
652             cursor.ins().return_(&[])
653         };
654         assert_eq!(
655             f.to_string(),
656             "function %foo() fast {\n    ss0 = explicit_slot 4\n\nblock0(v0: i8, v1: f32x4):\n    return\n}\n"
657         );
658 
659         let mut f = Function::new();
660         f.create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 4, 2));
661         assert_eq!(
662             f.to_string(),
663             "function u0:0() fast {\n    ss0 = explicit_slot 4, align = 4\n}\n"
664         );
665     }
666 
667     #[test]
668     fn aliases() {
669         use crate::ir::InstBuilder;
670 
671         let mut func = Function::new();
672         {
673             let block0 = func.dfg.make_block();
674             let mut pos = FuncCursor::new(&mut func);
675             pos.insert_block(block0);
676 
677             // make some detached values for change_to_alias
678             let v0 = pos.func.dfg.append_block_param(block0, types::I32);
679             let v1 = pos.func.dfg.append_block_param(block0, types::I32);
680             let v2 = pos.func.dfg.append_block_param(block0, types::I32);
681             pos.func.dfg.detach_block_params(block0);
682 
683             // alias to a param--will be printed at beginning of block defining param
684             let v3 = pos.func.dfg.append_block_param(block0, types::I32);
685             pos.func.dfg.change_to_alias(v0, v3);
686 
687             // alias to an alias--should print attached to alias, not ultimate target
688             pos.func.dfg.make_value_alias_for_serialization(v0, v2); // v0 <- v2
689 
690             // alias to a result--will be printed after instruction producing result
691             let _dummy0 = pos.ins().iconst(types::I32, 42);
692             let v4 = pos.ins().iadd(v0, v0);
693             pos.func.dfg.change_to_alias(v1, v4);
694             let _dummy1 = pos.ins().iconst(types::I32, 23);
695             let _v7 = pos.ins().iadd(v1, v1);
696         }
697         assert_eq!(
698             func.to_string(),
699             "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"
700         );
701     }
702 
703     #[test]
704     fn cold_blocks() {
705         let mut func = Function::new();
706         {
707             let mut pos = FuncCursor::new(&mut func);
708 
709             let block0 = pos.func.dfg.make_block();
710             pos.insert_block(block0);
711             pos.func.layout.set_cold(block0);
712 
713             let block1 = pos.func.dfg.make_block();
714             pos.insert_block(block1);
715             pos.func.dfg.append_block_param(block1, types::I32);
716             pos.func.layout.set_cold(block1);
717         }
718 
719         assert_eq!(
720             func.to_string(),
721             "function u0:0() fast {\nblock0 cold:\n\nblock1(v0: i32) cold:\n}\n"
722         );
723     }
724 }
725