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