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 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. 130 pub struct PlainWriter; 131 132 impl 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. 157 pub 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 162 fn 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. 176 pub 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. 201 pub 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 209 fn 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 /// 224 pub 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 255 fn 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 // 290 fn 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 321 fn 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 338 fn 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. 396 pub 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 } 504 TryCallIndirect { 505 ref args, 506 exception, 507 .. 508 } => { 509 let args = args.as_slice(pool); 510 write!( 511 w, 512 " {}({}), {}", 513 args[0], 514 DisplayValues(&args[1..]), 515 exception_tables[exception].display(pool), 516 ) 517 } 518 FuncAddr { func_ref, .. } => write!(w, " {func_ref}"), 519 StackLoad { 520 stack_slot, offset, .. 521 } => write!(w, " {stack_slot}{offset}"), 522 StackStore { 523 arg, 524 stack_slot, 525 offset, 526 .. 527 } => write!(w, " {arg}, {stack_slot}{offset}"), 528 DynamicStackLoad { 529 dynamic_stack_slot, .. 530 } => write!(w, " {dynamic_stack_slot}"), 531 DynamicStackStore { 532 arg, 533 dynamic_stack_slot, 534 .. 535 } => write!(w, " {arg}, {dynamic_stack_slot}"), 536 Load { 537 flags, arg, offset, .. 538 } => write!(w, "{flags} {arg}{offset}"), 539 Store { 540 flags, 541 args, 542 offset, 543 .. 544 } => write!(w, "{} {}, {}{}", flags, args[0], args[1], offset), 545 Trap { code, .. } => write!(w, " {code}"), 546 CondTrap { arg, code, .. } => write!(w, " {arg}, {code}"), 547 ExceptionHandlerAddress { block, imm, .. } => write!(w, " {block}, {imm}"), 548 }?; 549 550 let mut sep = " ; "; 551 for arg in dfg.inst_values(inst) { 552 if let ValueDef::Result(src, _) = dfg.value_def(arg) { 553 let imm = match dfg.insts[src] { 554 UnaryImm { imm, .. } => { 555 let mut imm = imm; 556 if dfg.ctrl_typevar(src).bits() != 0 { 557 imm = imm.sign_extend_from_width(dfg.ctrl_typevar(src).bits()); 558 } 559 imm.to_string() 560 } 561 UnaryIeee16 { imm, .. } => imm.to_string(), 562 UnaryIeee32 { imm, .. } => imm.to_string(), 563 UnaryIeee64 { imm, .. } => imm.to_string(), 564 UnaryConst { 565 constant_handle, 566 opcode: Opcode::F128const, 567 } => Ieee128::try_from(dfg.constants.get(constant_handle)) 568 .expect("16-byte f128 constant") 569 .to_string(), 570 UnaryConst { 571 constant_handle, .. 572 } => constant_handle.to_string(), 573 _ => continue, 574 }; 575 write!(w, "{sep}{arg} = {imm}")?; 576 sep = ", "; 577 } 578 } 579 Ok(()) 580 } 581 582 fn write_debug_tags( 583 w: &mut dyn Write, 584 func: &Function, 585 inst: Inst, 586 indent: &mut usize, 587 ) -> fmt::Result { 588 let tags = func.debug_tags.get(inst); 589 if !tags.is_empty() { 590 let tags = tags 591 .iter() 592 .map(|tag| format!("{tag}")) 593 .collect::<Vec<_>>() 594 .join(", "); 595 let s = format!("<{tags}> "); 596 write!(w, "{s}")?; 597 *indent = indent.saturating_sub(s.len()); 598 } 599 Ok(()) 600 } 601 602 fn write_user_stack_map_entries(w: &mut dyn Write, dfg: &DataFlowGraph, inst: Inst) -> fmt::Result { 603 let entries = match dfg.user_stack_map_entries(inst) { 604 None => return Ok(()), 605 Some(es) => es, 606 }; 607 write!(w, ", stack_map=[")?; 608 let mut need_comma = false; 609 for entry in entries { 610 if need_comma { 611 write!(w, ", ")?; 612 } 613 write!(w, "{} @ {}+{}", entry.ty, entry.slot, entry.offset)?; 614 need_comma = true; 615 } 616 write!(w, "]")?; 617 Ok(()) 618 } 619 620 /// Displayable slice of values. 621 struct DisplayValues<'a>(&'a [Value]); 622 623 impl<'a> fmt::Display for DisplayValues<'a> { 624 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 625 for (i, val) in self.0.iter().enumerate() { 626 if i == 0 { 627 write!(f, "{val}")?; 628 } else { 629 write!(f, ", {val}")?; 630 } 631 } 632 Ok(()) 633 } 634 } 635 636 #[cfg(test)] 637 mod tests { 638 use crate::cursor::{Cursor, CursorPosition, FuncCursor}; 639 use crate::ir::types; 640 use crate::ir::{Function, InstBuilder, StackSlotData, StackSlotKind, UserFuncName}; 641 use alloc::string::ToString; 642 643 #[test] 644 fn basic() { 645 let mut f = Function::new(); 646 assert_eq!(f.to_string(), "function u0:0() fast {\n}\n"); 647 648 f.name = UserFuncName::testcase("foo"); 649 assert_eq!(f.to_string(), "function %foo() fast {\n}\n"); 650 651 f.create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 4, 0)); 652 assert_eq!( 653 f.to_string(), 654 "function %foo() fast {\n ss0 = explicit_slot 4\n}\n" 655 ); 656 657 let block = f.dfg.make_block(); 658 f.layout.append_block(block); 659 assert_eq!( 660 f.to_string(), 661 "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0:\n}\n" 662 ); 663 664 f.dfg.append_block_param(block, types::I8); 665 assert_eq!( 666 f.to_string(), 667 "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8):\n}\n" 668 ); 669 670 f.dfg.append_block_param(block, types::F32.by(4).unwrap()); 671 assert_eq!( 672 f.to_string(), 673 "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8, v1: f32x4):\n}\n" 674 ); 675 676 { 677 let mut cursor = FuncCursor::new(&mut f); 678 cursor.set_position(CursorPosition::After(block)); 679 cursor.ins().return_(&[]) 680 }; 681 assert_eq!( 682 f.to_string(), 683 "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8, v1: f32x4):\n return\n}\n" 684 ); 685 686 let mut f = Function::new(); 687 f.create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 4, 2)); 688 assert_eq!( 689 f.to_string(), 690 "function u0:0() fast {\n ss0 = explicit_slot 4, align = 4\n}\n" 691 ); 692 } 693 694 #[test] 695 fn aliases() { 696 use crate::ir::InstBuilder; 697 698 let mut func = Function::new(); 699 { 700 let block0 = func.dfg.make_block(); 701 let mut pos = FuncCursor::new(&mut func); 702 pos.insert_block(block0); 703 704 // make some detached values for change_to_alias 705 let v0 = pos.func.dfg.append_block_param(block0, types::I32); 706 let v1 = pos.func.dfg.append_block_param(block0, types::I32); 707 let v2 = pos.func.dfg.append_block_param(block0, types::I32); 708 pos.func.dfg.detach_block_params(block0); 709 710 // alias to a param--will be printed at beginning of block defining param 711 let v3 = pos.func.dfg.append_block_param(block0, types::I32); 712 pos.func.dfg.change_to_alias(v0, v3); 713 714 // alias to an alias--should print attached to alias, not ultimate target 715 pos.func.dfg.make_value_alias_for_serialization(v0, v2); // v0 <- v2 716 717 // alias to a result--will be printed after instruction producing result 718 let _dummy0 = pos.ins().iconst(types::I32, 42); 719 let v4 = pos.ins().iadd(v0, v0); 720 pos.func.dfg.change_to_alias(v1, v4); 721 let _dummy1 = pos.ins().iconst(types::I32, 23); 722 let _v7 = pos.ins().iadd(v1, v1); 723 } 724 assert_eq!( 725 func.to_string(), 726 "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" 727 ); 728 } 729 730 #[test] 731 fn cold_blocks() { 732 let mut func = Function::new(); 733 { 734 let mut pos = FuncCursor::new(&mut func); 735 736 let block0 = pos.func.dfg.make_block(); 737 pos.insert_block(block0); 738 pos.func.layout.set_cold(block0); 739 740 let block1 = pos.func.dfg.make_block(); 741 pos.insert_block(block1); 742 pos.func.dfg.append_block_param(block1, types::I32); 743 pos.func.layout.set_cold(block1); 744 } 745 746 assert_eq!( 747 func.to_string(), 748 "function u0:0() fast {\nblock0 cold:\n\nblock1(v0: i32) cold:\n}\n" 749 ); 750 } 751 } 752