Skip to main content

cranelift_codegen/
write.rs

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