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::{Block, DataFlowGraph, Function, Inst, Opcode, SigRef, Type, Value, ValueDef};
10 use crate::packed_option::ReservedValue;
11 use alloc::string::{String, ToString};
12 use alloc::vec::Vec;
13 use core::fmt::{self, Write};
14 
15 /// A `FuncWriter` used to decorate functions during printing.
16 pub trait FuncWriter {
17     /// Write the basic block header for the current function.
18     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     /// Write the given `inst` to `w`.
27     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     /// Write the preamble to `w`. By default, this uses `write_entity_definition`.
37     fn write_preamble(&mut self, w: &mut dyn Write, func: &Function) -> Result<bool, fmt::Error> {
38         self.super_preamble(w, func)
39     }
40 
41     /// Default impl of `write_preamble`
42     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         // Write out all signatures before functions since function declarations can refer to
61         // signatures.
62         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     /// Write an entity definition defined in the preamble to `w`.
93     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     /// Default impl of `write_entity_definition`
104     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 
115 /// A `PlainWriter` that doesn't decorate the function.
116 pub struct PlainWriter;
117 
118 impl 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 
141 /// Write `func` to `w` as equivalent text.
142 /// Use `isa` to emit ISA-dependent annotations.
143 pub fn write_function(w: &mut dyn Write, func: &Function) -> fmt::Result {
144     decorate_function(&mut PlainWriter, w, func)
145 }
146 
147 /// Create a reverse-alias map from a value to all aliases having that value as a direct target
148 fn alias_map(func: &Function) -> SecondaryMap<Value, Vec<Value>> {
149     let mut aliases = SecondaryMap::<_, Vec<_>>::new();
150     for v in func.dfg.values() {
151         // VADFS returns the immediate target of an alias
152         if let Some(k) = func.dfg.value_alias_dest_for_serialization(v) {
153             aliases[k].push(v);
154         }
155     }
156     aliases
157 }
158 
159 /// Writes `func` to `w` as text.
160 /// write_function_plain is passed as 'closure' to print instructions as text.
161 /// pretty_function_error is passed as 'closure' to add error decoration.
162 pub 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 
182 //----------------------------------------------------------------------
183 //
184 // Function spec.
185 
186 /// Writes the spec (name and signature) of 'func' to 'w' as text.
187 pub fn write_function_spec(w: &mut dyn Write, func: &Function) -> fmt::Result {
188     write!(w, "{}{}", func.name, func.signature)
189 }
190 
191 //----------------------------------------------------------------------
192 //
193 // Basic blocks
194 
195 fn 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 
200 /// Write out the basic block header, outdented:
201 ///
202 ///    block1:
203 ///    block1(v1: i32):
204 ///    block10(v4: f64, v5: i8):
205 ///
206 pub 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     // The `indent` is the instruction indentation. block headers are 4 spaces out from that.
219     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     // Remaining arguments.
230     for arg in args {
231         write!(w, ", ")?;
232         write_arg(w, func, arg)?;
233     }
234     writeln!(w, "){cold}:")
235 }
236 
237 fn 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     // Indent all instructions if any srclocs or debug tags are present.
245     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 
263 //----------------------------------------------------------------------
264 //
265 // Instructions
266 
267 // Should `inst` be printed with a type suffix?
268 //
269 // Polymorphic instructions may need a suffix indicating the value of the controlling type variable
270 // if it can't be trivially inferred.
271 //
272 fn 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 the controlling type variable can be inferred from the type of the designated value input
281     // operand, we don't need the type suffix.
282     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 
302 /// Write out any aliases to the given target, including indirect aliases
303 fn 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 
320 fn 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     // Prefix containing source location, encoding, and value locations.
328     let mut s = String::with_capacity(16);
329 
330     // Source location goes first.
331     let srcloc = func.srcloc(inst);
332     if !srcloc.is_default() {
333         write!(s, "{srcloc} ")?;
334     }
335 
336     // Write out any debug tags.
337     write_debug_tags(w, &func, inst, &mut indent)?;
338 
339     // Write out prefix and indent the instruction.
340     write!(w, "{s:indent$}")?;
341 
342     // Write out the result values, if any.
343     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     // Then the opcode, possibly with a '.type' suffix.
357     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     // Value aliases come out on lines after the instruction defining the referent.
368     for r in func.dfg.inst_results(inst) {
369         write_value_aliases(w, aliases, *r, indent)?;
370     }
371     Ok(())
372 }
373 
374 /// Write the operands of `inst` to `w` with a prepended space.
375 pub 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 
563 fn 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 
583 fn 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 
601 /// Displayable slice of values.
602 struct DisplayValues<'a>(&'a [Value]);
603 
604 impl<'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)]
618 mod 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             // make some detached values for change_to_alias
686             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             // alias to a param--will be printed at beginning of block defining param
692             let v3 = pos.func.dfg.append_block_param(block0, types::I32);
693             pos.func.dfg.change_to_alias(v0, v3);
694 
695             // alias to an alias--should print attached to alias, not ultimate target
696             pos.func.dfg.make_value_alias_for_serialization(v0, v2); // v0 <- v2
697 
698             // alias to a result--will be printed after instruction producing result
699             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 }
733