diff options
Diffstat (limited to 'third_party/rust/cranelift-codegen/src/write.rs')
-rw-r--r-- | third_party/rust/cranelift-codegen/src/write.rs | 854 |
1 files changed, 854 insertions, 0 deletions
diff --git a/third_party/rust/cranelift-codegen/src/write.rs b/third_party/rust/cranelift-codegen/src/write.rs new file mode 100644 index 0000000000..8d73e2d1e4 --- /dev/null +++ b/third_party/rust/cranelift-codegen/src/write.rs @@ -0,0 +1,854 @@ +//! Converting Cranelift IR to text. +//! +//! The `write` module provides the `write_function` function which converts an IR `Function` to an +//! equivalent textual form. This textual form can be read back by the `cranelift-reader` crate. + +use crate::entity::SecondaryMap; +use crate::ir::entities::AnyEntity; +use crate::ir::{ + Block, DataFlowGraph, DisplayFunctionAnnotations, Function, Inst, SigRef, Type, Value, + ValueDef, ValueLoc, +}; +use crate::isa::{RegInfo, TargetIsa}; +use crate::packed_option::ReservedValue; +use crate::value_label::ValueLabelsRanges; +use crate::HashSet; +use alloc::string::String; +use alloc::vec::Vec; +use core::fmt::{self, Write}; + +/// A `FuncWriter` used to decorate functions during printing. +pub trait FuncWriter { + /// Write the basic block header for the current function. + fn write_block_header( + &mut self, + w: &mut dyn Write, + func: &Function, + isa: Option<&dyn TargetIsa>, + block: Block, + indent: usize, + ) -> fmt::Result; + + /// Write the given `inst` to `w`. + fn write_instruction( + &mut self, + w: &mut dyn Write, + func: &Function, + aliases: &SecondaryMap<Value, Vec<Value>>, + isa: Option<&dyn TargetIsa>, + inst: Inst, + indent: usize, + ) -> fmt::Result; + + /// Write the preamble to `w`. By default, this uses `write_entity_definition`. + fn write_preamble( + &mut self, + w: &mut dyn Write, + func: &Function, + regs: Option<&RegInfo>, + ) -> Result<bool, fmt::Error> { + self.super_preamble(w, func, regs) + } + + /// Default impl of `write_preamble` + fn super_preamble( + &mut self, + w: &mut dyn Write, + func: &Function, + regs: Option<&RegInfo>, + ) -> Result<bool, fmt::Error> { + let mut any = false; + + for (ss, slot) in func.stack_slots.iter() { + any = true; + self.write_entity_definition(w, func, ss.into(), slot)?; + } + + for (gv, gv_data) in &func.global_values { + any = true; + self.write_entity_definition(w, func, gv.into(), gv_data)?; + } + + for (heap, heap_data) in &func.heaps { + if !heap_data.index_type.is_invalid() { + any = true; + self.write_entity_definition(w, func, heap.into(), heap_data)?; + } + } + + for (table, table_data) in &func.tables { + if !table_data.index_type.is_invalid() { + any = true; + self.write_entity_definition(w, func, table.into(), table_data)?; + } + } + + // Write out all signatures before functions since function declarations can refer to + // signatures. + for (sig, sig_data) in &func.dfg.signatures { + any = true; + self.write_entity_definition(w, func, sig.into(), &sig_data.display(regs))?; + } + + for (fnref, ext_func) in &func.dfg.ext_funcs { + if ext_func.signature != SigRef::reserved_value() { + any = true; + self.write_entity_definition(w, func, fnref.into(), ext_func)?; + } + } + + for (jt, jt_data) in &func.jump_tables { + any = true; + self.write_entity_definition(w, func, jt.into(), jt_data)?; + } + + for (&cref, cval) in func.dfg.constants.iter() { + any = true; + self.write_entity_definition(w, func, cref.into(), cval)?; + } + + if let Some(limit) = func.stack_limit { + any = true; + self.write_entity_definition(w, func, AnyEntity::StackLimit, &limit)?; + } + + Ok(any) + } + + /// Write an entity definition defined in the preamble to `w`. + fn write_entity_definition( + &mut self, + w: &mut dyn Write, + func: &Function, + entity: AnyEntity, + value: &dyn fmt::Display, + ) -> fmt::Result { + self.super_entity_definition(w, func, entity, value) + } + + /// Default impl of `write_entity_definition` + #[allow(unused_variables)] + fn super_entity_definition( + &mut self, + w: &mut dyn Write, + func: &Function, + entity: AnyEntity, + value: &dyn fmt::Display, + ) -> fmt::Result { + writeln!(w, " {} = {}", entity, value) + } +} + +/// A `PlainWriter` that doesn't decorate the function. +pub struct PlainWriter; + +impl FuncWriter for PlainWriter { + fn write_instruction( + &mut self, + w: &mut dyn Write, + func: &Function, + aliases: &SecondaryMap<Value, Vec<Value>>, + isa: Option<&dyn TargetIsa>, + inst: Inst, + indent: usize, + ) -> fmt::Result { + write_instruction(w, func, aliases, isa, inst, indent) + } + + fn write_block_header( + &mut self, + w: &mut dyn Write, + func: &Function, + isa: Option<&dyn TargetIsa>, + block: Block, + indent: usize, + ) -> fmt::Result { + write_block_header(w, func, isa, block, indent) + } +} + +/// Write `func` to `w` as equivalent text. +/// Use `isa` to emit ISA-dependent annotations. +pub fn write_function( + w: &mut dyn Write, + func: &Function, + annotations: &DisplayFunctionAnnotations, +) -> fmt::Result { + decorate_function(&mut PlainWriter, w, func, annotations) +} + +/// Create a reverse-alias map from a value to all aliases having that value as a direct target +fn alias_map(func: &Function) -> SecondaryMap<Value, Vec<Value>> { + let mut aliases = SecondaryMap::<_, Vec<_>>::new(); + for v in func.dfg.values() { + // VADFS returns the immediate target of an alias + if let Some(k) = func.dfg.value_alias_dest_for_serialization(v) { + aliases[k].push(v); + } + } + aliases +} + +/// Writes `func` to `w` as text. +/// write_function_plain is passed as 'closure' to print instructions as text. +/// pretty_function_error is passed as 'closure' to add error decoration. +pub fn decorate_function<FW: FuncWriter>( + func_w: &mut FW, + w: &mut dyn Write, + func: &Function, + annotations: &DisplayFunctionAnnotations, +) -> fmt::Result { + let regs = annotations.isa.map(TargetIsa::register_info); + let regs = regs.as_ref(); + + write!(w, "function ")?; + write_spec(w, func, regs)?; + writeln!(w, " {{")?; + let aliases = alias_map(func); + let mut any = func_w.write_preamble(w, func, regs)?; + for block in &func.layout { + if any { + writeln!(w)?; + } + decorate_block(func_w, w, func, &aliases, annotations, block)?; + any = true; + } + writeln!(w, "}}") +} + +//---------------------------------------------------------------------- +// +// Function spec. + +fn write_spec(w: &mut dyn Write, func: &Function, regs: Option<&RegInfo>) -> fmt::Result { + write!(w, "{}{}", func.name, func.signature.display(regs)) +} + +//---------------------------------------------------------------------- +// +// Basic blocks + +fn write_arg( + w: &mut dyn Write, + func: &Function, + regs: Option<&RegInfo>, + arg: Value, +) -> fmt::Result { + write!(w, "{}: {}", arg, func.dfg.value_type(arg))?; + let loc = func.locations[arg]; + if loc.is_assigned() { + write!(w, " [{}]", loc.display(regs))? + } + + Ok(()) +} + +/// Write out the basic block header, outdented: +/// +/// block1: +/// block1(v1: i32): +/// block10(v4: f64, v5: b1): +/// +pub fn write_block_header( + w: &mut dyn Write, + func: &Function, + isa: Option<&dyn TargetIsa>, + block: Block, + indent: usize, +) -> fmt::Result { + // The `indent` is the instruction indentation. block headers are 4 spaces out from that. + write!(w, "{1:0$}{2}", indent - 4, "", block)?; + + let regs = isa.map(TargetIsa::register_info); + let regs = regs.as_ref(); + + let mut args = func.dfg.block_params(block).iter().cloned(); + match args.next() { + None => return writeln!(w, ":"), + Some(arg) => { + write!(w, "(")?; + write_arg(w, func, regs, arg)?; + } + } + // Remaining arguments. + for arg in args { + write!(w, ", ")?; + write_arg(w, func, regs, arg)?; + } + writeln!(w, "):") +} + +fn write_valueloc(w: &mut dyn Write, loc: ValueLoc, regs: &RegInfo) -> fmt::Result { + match loc { + ValueLoc::Reg(r) => write!(w, "{}", regs.display_regunit(r)), + ValueLoc::Stack(ss) => write!(w, "{}", ss), + ValueLoc::Unassigned => write!(w, "?"), + } +} + +fn write_value_range_markers( + w: &mut dyn Write, + val_ranges: &ValueLabelsRanges, + regs: &RegInfo, + offset: u32, + indent: usize, +) -> fmt::Result { + let mut result = String::new(); + let mut shown = HashSet::new(); + for (val, rng) in val_ranges { + for i in (0..rng.len()).rev() { + if rng[i].start == offset { + write!(&mut result, " {}@", val)?; + write_valueloc(&mut result, rng[i].loc, regs)?; + shown.insert(val); + break; + } + } + } + for (val, rng) in val_ranges { + for i in (0..rng.len()).rev() { + if rng[i].end == offset && !shown.contains(val) { + write!(&mut result, " {}\u{2620}", val)?; + break; + } + } + } + if !result.is_empty() { + writeln!(w, ";{1:0$}; {2}", indent + 24, "", result)?; + } + Ok(()) +} + +fn decorate_block<FW: FuncWriter>( + func_w: &mut FW, + w: &mut dyn Write, + func: &Function, + aliases: &SecondaryMap<Value, Vec<Value>>, + annotations: &DisplayFunctionAnnotations, + block: Block, +) -> fmt::Result { + // Indent all instructions if any encodings are present. + let indent = if func.encodings.is_empty() && func.srclocs.is_empty() { + 4 + } else { + 36 + }; + let isa = annotations.isa; + + func_w.write_block_header(w, func, isa, block, indent)?; + for a in func.dfg.block_params(block).iter().cloned() { + write_value_aliases(w, aliases, a, indent)?; + } + + if let Some(isa) = isa { + if !func.offsets.is_empty() { + let encinfo = isa.encoding_info(); + let regs = &isa.register_info(); + for (offset, inst, size) in func.inst_offsets(block, &encinfo) { + func_w.write_instruction(w, func, aliases, Some(isa), inst, indent)?; + if size > 0 { + if let Some(val_ranges) = annotations.value_ranges { + write_value_range_markers(w, val_ranges, regs, offset + size, indent)?; + } + } + } + return Ok(()); + } + } + + for inst in func.layout.block_insts(block) { + func_w.write_instruction(w, func, aliases, isa, inst, indent)?; + } + + Ok(()) +} + +//---------------------------------------------------------------------- +// +// Instructions + +// Should `inst` be printed with a type suffix? +// +// Polymorphic instructions may need a suffix indicating the value of the controlling type variable +// if it can't be trivially inferred. +// +fn type_suffix(func: &Function, inst: Inst) -> Option<Type> { + let inst_data = &func.dfg[inst]; + let constraints = inst_data.opcode().constraints(); + + if !constraints.is_polymorphic() { + return None; + } + + // If the controlling type variable can be inferred from the type of the designated value input + // operand, we don't need the type suffix. + if constraints.use_typevar_operand() { + let ctrl_var = inst_data.typevar_operand(&func.dfg.value_lists).unwrap(); + let def_block = match func.dfg.value_def(ctrl_var) { + ValueDef::Result(instr, _) => func.layout.inst_block(instr), + ValueDef::Param(block, _) => Some(block), + }; + if def_block.is_some() && def_block == func.layout.inst_block(inst) { + return None; + } + } + + let rtype = func.dfg.ctrl_typevar(inst); + assert!( + !rtype.is_invalid(), + "Polymorphic instruction must produce a result" + ); + Some(rtype) +} + +/// Write out any aliases to the given target, including indirect aliases +fn write_value_aliases( + w: &mut dyn Write, + aliases: &SecondaryMap<Value, Vec<Value>>, + target: Value, + indent: usize, +) -> fmt::Result { + let mut todo_stack = vec![target]; + while let Some(target) = todo_stack.pop() { + for &a in &aliases[target] { + writeln!(w, "{1:0$}{2} -> {3}", indent, "", a, target)?; + todo_stack.push(a); + } + } + + Ok(()) +} + +fn write_instruction( + w: &mut dyn Write, + func: &Function, + aliases: &SecondaryMap<Value, Vec<Value>>, + isa: Option<&dyn TargetIsa>, + inst: Inst, + indent: usize, +) -> fmt::Result { + // Prefix containing source location, encoding, and value locations. + let mut s = String::with_capacity(16); + + // Source location goes first. + let srcloc = func.srclocs[inst]; + if !srcloc.is_default() { + write!(s, "{} ", srcloc)?; + } + + // Write out encoding info. + if let Some(enc) = func.encodings.get(inst).cloned() { + if let Some(isa) = isa { + write!(s, "[{}", isa.encoding_info().display(enc))?; + // Write value locations, if we have them. + if !func.locations.is_empty() { + let regs = isa.register_info(); + for &r in func.dfg.inst_results(inst) { + write!(s, ",{}", func.locations[r].display(®s))? + } + } + write!(s, "] ")?; + } else { + write!(s, "[{}] ", enc)?; + } + } + + // Write out prefix and indent the instruction. + write!(w, "{1:0$}", indent, s)?; + + // Write out the result values, if any. + let mut has_results = false; + for r in func.dfg.inst_results(inst) { + if !has_results { + has_results = true; + write!(w, "{}", r)?; + } else { + write!(w, ", {}", r)?; + } + } + if has_results { + write!(w, " = ")?; + } + + // Then the opcode, possibly with a '.type' suffix. + let opcode = func.dfg[inst].opcode(); + + match type_suffix(func, inst) { + Some(suf) => write!(w, "{}.{}", opcode, suf)?, + None => write!(w, "{}", opcode)?, + } + + write_operands(w, &func.dfg, isa, inst)?; + writeln!(w)?; + + // Value aliases come out on lines after the instruction defining the referent. + for r in func.dfg.inst_results(inst) { + write_value_aliases(w, aliases, *r, indent)?; + } + Ok(()) +} + +/// Write the operands of `inst` to `w` with a prepended space. +pub fn write_operands( + w: &mut dyn Write, + dfg: &DataFlowGraph, + isa: Option<&dyn TargetIsa>, + inst: Inst, +) -> fmt::Result { + let pool = &dfg.value_lists; + use crate::ir::instructions::InstructionData::*; + match dfg[inst] { + AtomicRmw { op, args, .. } => write!(w, " {}, {}, {}", op, args[0], args[1]), + AtomicCas { args, .. } => write!(w, " {}, {}, {}", args[0], args[1], args[2]), + LoadNoOffset { flags, arg, .. } => write!(w, "{} {}", flags, arg), + StoreNoOffset { flags, args, .. } => write!(w, "{} {}, {}", flags, args[0], args[1]), + Unary { arg, .. } => write!(w, " {}", arg), + UnaryImm { imm, .. } => write!(w, " {}", imm), + UnaryIeee32 { imm, .. } => write!(w, " {}", imm), + UnaryIeee64 { imm, .. } => write!(w, " {}", imm), + UnaryBool { imm, .. } => write!(w, " {}", imm), + UnaryGlobalValue { global_value, .. } => write!(w, " {}", global_value), + UnaryConst { + constant_handle, .. + } => write!(w, " {}", constant_handle), + Binary { args, .. } => write!(w, " {}, {}", args[0], args[1]), + BinaryImm8 { arg, imm, .. } => write!(w, " {}, {}", arg, imm), + BinaryImm64 { arg, imm, .. } => write!(w, " {}, {}", arg, imm), + Ternary { args, .. } => write!(w, " {}, {}, {}", args[0], args[1], args[2]), + MultiAry { ref args, .. } => { + if args.is_empty() { + write!(w, "") + } else { + write!(w, " {}", DisplayValues(args.as_slice(pool))) + } + } + NullAry { .. } => write!(w, " "), + TernaryImm8 { imm, args, .. } => write!(w, " {}, {}, {}", args[0], args[1], imm), + Shuffle { mask, args, .. } => { + let data = dfg.immediates.get(mask).expect( + "Expected the shuffle mask to already be inserted into the immediates table", + ); + write!(w, " {}, {}, {}", args[0], args[1], data) + } + IntCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]), + IntCompareImm { cond, arg, imm, .. } => write!(w, " {} {}, {}", cond, arg, imm), + IntCond { cond, arg, .. } => write!(w, " {} {}", cond, arg), + FloatCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]), + FloatCond { cond, arg, .. } => write!(w, " {} {}", cond, arg), + IntSelect { cond, args, .. } => { + write!(w, " {} {}, {}, {}", cond, args[0], args[1], args[2]) + } + Jump { + destination, + ref args, + .. + } => { + write!(w, " {}", destination)?; + write_block_args(w, args.as_slice(pool)) + } + Branch { + destination, + ref args, + .. + } => { + let args = args.as_slice(pool); + write!(w, " {}, {}", args[0], destination)?; + write_block_args(w, &args[1..]) + } + BranchInt { + cond, + destination, + ref args, + .. + } => { + let args = args.as_slice(pool); + write!(w, " {} {}, {}", cond, args[0], destination)?; + write_block_args(w, &args[1..]) + } + BranchFloat { + cond, + destination, + ref args, + .. + } => { + let args = args.as_slice(pool); + write!(w, " {} {}, {}", cond, args[0], destination)?; + write_block_args(w, &args[1..]) + } + BranchIcmp { + cond, + destination, + ref args, + .. + } => { + let args = args.as_slice(pool); + write!(w, " {} {}, {}, {}", cond, args[0], args[1], destination)?; + write_block_args(w, &args[2..]) + } + BranchTable { + arg, + destination, + table, + .. + } => write!(w, " {}, {}, {}", arg, destination, table), + BranchTableBase { table, .. } => write!(w, " {}", table), + BranchTableEntry { + args, imm, table, .. + } => write!(w, " {}, {}, {}, {}", args[0], args[1], imm, table), + IndirectJump { arg, table, .. } => write!(w, " {}, {}", arg, table), + Call { + func_ref, ref args, .. + } => write!(w, " {}({})", func_ref, DisplayValues(args.as_slice(pool))), + CallIndirect { + sig_ref, ref args, .. + } => { + let args = args.as_slice(pool); + write!( + w, + " {}, {}({})", + sig_ref, + args[0], + DisplayValues(&args[1..]) + ) + } + FuncAddr { func_ref, .. } => write!(w, " {}", func_ref), + StackLoad { + stack_slot, offset, .. + } => write!(w, " {}{}", stack_slot, offset), + StackStore { + arg, + stack_slot, + offset, + .. + } => write!(w, " {}, {}{}", arg, stack_slot, offset), + HeapAddr { heap, arg, imm, .. } => write!(w, " {}, {}, {}", heap, arg, imm), + TableAddr { table, arg, .. } => write!(w, " {}, {}", table, arg), + Load { + flags, arg, offset, .. + } => write!(w, "{} {}{}", flags, arg, offset), + LoadComplex { + flags, + ref args, + offset, + .. + } => { + let args = args.as_slice(pool); + write!( + w, + "{} {}{}", + flags, + DisplayValuesWithDelimiter(&args, '+'), + offset + ) + } + Store { + flags, + args, + offset, + .. + } => write!(w, "{} {}, {}{}", flags, args[0], args[1], offset), + StoreComplex { + flags, + ref args, + offset, + .. + } => { + let args = args.as_slice(pool); + write!( + w, + "{} {}, {}{}", + flags, + args[0], + DisplayValuesWithDelimiter(&args[1..], '+'), + offset + ) + } + RegMove { arg, src, dst, .. } => { + if let Some(isa) = isa { + let regs = isa.register_info(); + write!( + w, + " {}, {} -> {}", + arg, + regs.display_regunit(src), + regs.display_regunit(dst) + ) + } else { + write!(w, " {}, %{} -> %{}", arg, src, dst) + } + } + CopySpecial { src, dst, .. } => { + if let Some(isa) = isa { + let regs = isa.register_info(); + write!( + w, + " {} -> {}", + regs.display_regunit(src), + regs.display_regunit(dst) + ) + } else { + write!(w, " %{} -> %{}", src, dst) + } + } + CopyToSsa { src, .. } => { + if let Some(isa) = isa { + let regs = isa.register_info(); + write!(w, " {}", regs.display_regunit(src)) + } else { + write!(w, " %{}", src) + } + } + RegSpill { arg, src, dst, .. } => { + if let Some(isa) = isa { + let regs = isa.register_info(); + write!(w, " {}, {} -> {}", arg, regs.display_regunit(src), dst) + } else { + write!(w, " {}, %{} -> {}", arg, src, dst) + } + } + RegFill { arg, src, dst, .. } => { + if let Some(isa) = isa { + let regs = isa.register_info(); + write!(w, " {}, {} -> {}", arg, src, regs.display_regunit(dst)) + } else { + write!(w, " {}, {} -> %{}", arg, src, dst) + } + } + Trap { code, .. } => write!(w, " {}", code), + CondTrap { arg, code, .. } => write!(w, " {}, {}", arg, code), + IntCondTrap { + cond, arg, code, .. + } => write!(w, " {} {}, {}", cond, arg, code), + FloatCondTrap { + cond, arg, code, .. + } => write!(w, " {} {}, {}", cond, arg, code), + } +} + +/// Write block args using optional parantheses. +fn write_block_args(w: &mut dyn Write, args: &[Value]) -> fmt::Result { + if args.is_empty() { + Ok(()) + } else { + write!(w, "({})", DisplayValues(args)) + } +} + +/// Displayable slice of values. +struct DisplayValues<'a>(&'a [Value]); + +impl<'a> fmt::Display for DisplayValues<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for (i, val) in self.0.iter().enumerate() { + if i == 0 { + write!(f, "{}", val)?; + } else { + write!(f, ", {}", val)?; + } + } + Ok(()) + } +} + +struct DisplayValuesWithDelimiter<'a>(&'a [Value], char); + +impl<'a> fmt::Display for DisplayValuesWithDelimiter<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for (i, val) in self.0.iter().enumerate() { + if i == 0 { + write!(f, "{}", val)?; + } else { + write!(f, "{}{}", self.1, val)?; + } + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use crate::cursor::{Cursor, CursorPosition, FuncCursor}; + use crate::ir::types; + use crate::ir::{ExternalName, Function, InstBuilder, StackSlotData, StackSlotKind}; + use alloc::string::ToString; + + #[test] + fn basic() { + let mut f = Function::new(); + assert_eq!(f.to_string(), "function u0:0() fast {\n}\n"); + + f.name = ExternalName::testcase("foo"); + assert_eq!(f.to_string(), "function %foo() fast {\n}\n"); + + f.create_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 4)); + assert_eq!( + f.to_string(), + "function %foo() fast {\n ss0 = explicit_slot 4\n}\n" + ); + + let block = f.dfg.make_block(); + f.layout.append_block(block); + assert_eq!( + f.to_string(), + "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0:\n}\n" + ); + + f.dfg.append_block_param(block, types::I8); + assert_eq!( + f.to_string(), + "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8):\n}\n" + ); + + f.dfg.append_block_param(block, types::F32.by(4).unwrap()); + assert_eq!( + f.to_string(), + "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8, v1: f32x4):\n}\n" + ); + + { + let mut cursor = FuncCursor::new(&mut f); + cursor.set_position(CursorPosition::After(block)); + cursor.ins().return_(&[]) + }; + assert_eq!( + f.to_string(), + "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8, v1: f32x4):\n return\n}\n" + ); + } + + #[test] + fn aliases() { + use crate::ir::InstBuilder; + + let mut func = Function::new(); + { + let block0 = func.dfg.make_block(); + let mut pos = FuncCursor::new(&mut func); + pos.insert_block(block0); + + // make some detached values for change_to_alias + let v0 = pos.func.dfg.append_block_param(block0, types::I32); + let v1 = pos.func.dfg.append_block_param(block0, types::I32); + let v2 = pos.func.dfg.append_block_param(block0, types::I32); + pos.func.dfg.detach_block_params(block0); + + // alias to a param--will be printed at beginning of block defining param + let v3 = pos.func.dfg.append_block_param(block0, types::I32); + pos.func.dfg.change_to_alias(v0, v3); + + // alias to an alias--should print attached to alias, not ultimate target + pos.func.dfg.make_value_alias_for_serialization(v0, v2); // v0 <- v2 + + // alias to a result--will be printed after instruction producing result + let _dummy0 = pos.ins().iconst(types::I32, 42); + let v4 = pos.ins().iadd(v0, v0); + pos.func.dfg.change_to_alias(v1, v4); + let _dummy1 = pos.ins().iconst(types::I32, 23); + let _v7 = pos.ins().iadd(v1, v1); + } + assert_eq!( + func.to_string(), + "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" + ); + } +} |