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::{ 9 Block, DataFlowGraph, DisplayFunctionAnnotations, Function, Inst, SigRef, Type, Value, 10 ValueDef, ValueLoc, 11 }; 12 use crate::isa::{RegInfo, TargetIsa}; 13 use crate::packed_option::ReservedValue; 14 use crate::value_label::ValueLabelsRanges; 15 use crate::HashSet; 16 use alloc::string::String; 17 use alloc::vec::Vec; 18 use core::fmt::{self, Write}; 19 20 /// A `FuncWriter` used to decorate functions during printing. 21 pub trait FuncWriter { 22 /// Write the basic block header for the current function. 23 fn write_block_header( 24 &mut self, 25 w: &mut dyn Write, 26 func: &Function, 27 isa: Option<&dyn TargetIsa>, 28 block: Block, 29 indent: usize, 30 ) -> fmt::Result; 31 32 /// Write the given `inst` to `w`. 33 fn write_instruction( 34 &mut self, 35 w: &mut dyn Write, 36 func: &Function, 37 aliases: &SecondaryMap<Value, Vec<Value>>, 38 isa: Option<&dyn TargetIsa>, 39 inst: Inst, 40 indent: usize, 41 ) -> fmt::Result; 42 43 /// Write the preamble to `w`. By default, this uses `write_entity_definition`. 44 fn write_preamble( 45 &mut self, 46 w: &mut dyn Write, 47 func: &Function, 48 regs: Option<&RegInfo>, 49 ) -> Result<bool, fmt::Error> { 50 self.super_preamble(w, func, regs) 51 } 52 53 /// Default impl of `write_preamble` 54 fn super_preamble( 55 &mut self, 56 w: &mut dyn Write, 57 func: &Function, 58 regs: Option<&RegInfo>, 59 ) -> Result<bool, fmt::Error> { 60 let mut any = false; 61 62 for (ss, slot) in func.stack_slots.iter() { 63 any = true; 64 self.write_entity_definition(w, func, ss.into(), slot)?; 65 } 66 67 for (gv, gv_data) in &func.global_values { 68 any = true; 69 self.write_entity_definition(w, func, gv.into(), gv_data)?; 70 } 71 72 for (heap, heap_data) in &func.heaps { 73 if !heap_data.index_type.is_invalid() { 74 any = true; 75 self.write_entity_definition(w, func, heap.into(), heap_data)?; 76 } 77 } 78 79 for (table, table_data) in &func.tables { 80 if !table_data.index_type.is_invalid() { 81 any = true; 82 self.write_entity_definition(w, func, table.into(), table_data)?; 83 } 84 } 85 86 // Write out all signatures before functions since function declarations can refer to 87 // signatures. 88 for (sig, sig_data) in &func.dfg.signatures { 89 any = true; 90 self.write_entity_definition(w, func, sig.into(), &sig_data.display(regs))?; 91 } 92 93 for (fnref, ext_func) in &func.dfg.ext_funcs { 94 if ext_func.signature != SigRef::reserved_value() { 95 any = true; 96 self.write_entity_definition(w, func, fnref.into(), ext_func)?; 97 } 98 } 99 100 for (jt, jt_data) in &func.jump_tables { 101 any = true; 102 self.write_entity_definition(w, func, jt.into(), jt_data)?; 103 } 104 105 for (&cref, cval) in func.dfg.constants.iter() { 106 any = true; 107 self.write_entity_definition(w, func, cref.into(), cval)?; 108 } 109 110 if let Some(limit) = func.stack_limit { 111 any = true; 112 self.write_entity_definition(w, func, AnyEntity::StackLimit, &limit)?; 113 } 114 115 Ok(any) 116 } 117 118 /// Write an entity definition defined in the preamble to `w`. 119 fn write_entity_definition( 120 &mut self, 121 w: &mut dyn Write, 122 func: &Function, 123 entity: AnyEntity, 124 value: &dyn fmt::Display, 125 ) -> fmt::Result { 126 self.super_entity_definition(w, func, entity, value) 127 } 128 129 /// Default impl of `write_entity_definition` 130 #[allow(unused_variables)] 131 fn super_entity_definition( 132 &mut self, 133 w: &mut dyn Write, 134 func: &Function, 135 entity: AnyEntity, 136 value: &dyn fmt::Display, 137 ) -> fmt::Result { 138 writeln!(w, " {} = {}", entity, value) 139 } 140 } 141 142 /// A `PlainWriter` that doesn't decorate the function. 143 pub struct PlainWriter; 144 145 impl FuncWriter for PlainWriter { 146 fn write_instruction( 147 &mut self, 148 w: &mut dyn Write, 149 func: &Function, 150 aliases: &SecondaryMap<Value, Vec<Value>>, 151 isa: Option<&dyn TargetIsa>, 152 inst: Inst, 153 indent: usize, 154 ) -> fmt::Result { 155 write_instruction(w, func, aliases, isa, inst, indent) 156 } 157 158 fn write_block_header( 159 &mut self, 160 w: &mut dyn Write, 161 func: &Function, 162 isa: Option<&dyn TargetIsa>, 163 block: Block, 164 indent: usize, 165 ) -> fmt::Result { 166 write_block_header(w, func, isa, block, indent) 167 } 168 } 169 170 /// Write `func` to `w` as equivalent text. 171 /// Use `isa` to emit ISA-dependent annotations. 172 pub fn write_function( 173 w: &mut dyn Write, 174 func: &Function, 175 annotations: &DisplayFunctionAnnotations, 176 ) -> fmt::Result { 177 decorate_function(&mut PlainWriter, w, func, annotations) 178 } 179 180 /// Create a reverse-alias map from a value to all aliases having that value as a direct target 181 fn alias_map(func: &Function) -> SecondaryMap<Value, Vec<Value>> { 182 let mut aliases = SecondaryMap::<_, Vec<_>>::new(); 183 for v in func.dfg.values() { 184 // VADFS returns the immediate target of an alias 185 if let Some(k) = func.dfg.value_alias_dest_for_serialization(v) { 186 aliases[k].push(v); 187 } 188 } 189 aliases 190 } 191 192 /// Writes `func` to `w` as text. 193 /// write_function_plain is passed as 'closure' to print instructions as text. 194 /// pretty_function_error is passed as 'closure' to add error decoration. 195 pub fn decorate_function<FW: FuncWriter>( 196 func_w: &mut FW, 197 w: &mut dyn Write, 198 func: &Function, 199 annotations: &DisplayFunctionAnnotations, 200 ) -> fmt::Result { 201 let regs = annotations.isa.map(TargetIsa::register_info); 202 let regs = regs.as_ref(); 203 204 write!(w, "function ")?; 205 write_spec(w, func, regs)?; 206 writeln!(w, " {{")?; 207 let aliases = alias_map(func); 208 let mut any = func_w.write_preamble(w, func, regs)?; 209 for block in &func.layout { 210 if any { 211 writeln!(w)?; 212 } 213 decorate_block(func_w, w, func, &aliases, annotations, block)?; 214 any = true; 215 } 216 writeln!(w, "}}") 217 } 218 219 //---------------------------------------------------------------------- 220 // 221 // Function spec. 222 223 fn write_spec(w: &mut dyn Write, func: &Function, regs: Option<&RegInfo>) -> fmt::Result { 224 write!(w, "{}{}", func.name, func.signature.display(regs)) 225 } 226 227 //---------------------------------------------------------------------- 228 // 229 // Basic blocks 230 231 fn write_arg( 232 w: &mut dyn Write, 233 func: &Function, 234 regs: Option<&RegInfo>, 235 arg: Value, 236 ) -> fmt::Result { 237 write!(w, "{}: {}", arg, func.dfg.value_type(arg))?; 238 let loc = func.locations[arg]; 239 if loc.is_assigned() { 240 write!(w, " [{}]", loc.display(regs))? 241 } 242 243 Ok(()) 244 } 245 246 /// Write out the basic block header, outdented: 247 /// 248 /// block1: 249 /// block1(v1: i32): 250 /// block10(v4: f64, v5: b1): 251 /// 252 pub fn write_block_header( 253 w: &mut dyn Write, 254 func: &Function, 255 isa: Option<&dyn TargetIsa>, 256 block: Block, 257 indent: usize, 258 ) -> fmt::Result { 259 // The `indent` is the instruction indentation. block headers are 4 spaces out from that. 260 write!(w, "{1:0$}{2}", indent - 4, "", block)?; 261 262 let regs = isa.map(TargetIsa::register_info); 263 let regs = regs.as_ref(); 264 265 let mut args = func.dfg.block_params(block).iter().cloned(); 266 match args.next() { 267 None => return writeln!(w, ":"), 268 Some(arg) => { 269 write!(w, "(")?; 270 write_arg(w, func, regs, arg)?; 271 } 272 } 273 // Remaining arguments. 274 for arg in args { 275 write!(w, ", ")?; 276 write_arg(w, func, regs, arg)?; 277 } 278 writeln!(w, "):") 279 } 280 281 fn write_valueloc(w: &mut dyn Write, loc: ValueLoc, regs: &RegInfo) -> fmt::Result { 282 match loc { 283 ValueLoc::Reg(r) => write!(w, "{}", regs.display_regunit(r)), 284 ValueLoc::Stack(ss) => write!(w, "{}", ss), 285 ValueLoc::Unassigned => write!(w, "?"), 286 } 287 } 288 289 fn write_value_range_markers( 290 w: &mut dyn Write, 291 val_ranges: &ValueLabelsRanges, 292 regs: &RegInfo, 293 offset: u32, 294 indent: usize, 295 ) -> fmt::Result { 296 let mut result = String::new(); 297 let mut shown = HashSet::new(); 298 for (val, rng) in val_ranges { 299 for i in (0..rng.len()).rev() { 300 if rng[i].start == offset { 301 write!(&mut result, " {}@", val)?; 302 write_valueloc(&mut result, rng[i].loc, regs)?; 303 shown.insert(val); 304 break; 305 } 306 } 307 } 308 for (val, rng) in val_ranges { 309 for i in (0..rng.len()).rev() { 310 if rng[i].end == offset && !shown.contains(val) { 311 write!(&mut result, " {}\u{2620}", val)?; 312 break; 313 } 314 } 315 } 316 if !result.is_empty() { 317 writeln!(w, ";{1:0$}; {2}", indent + 24, "", result)?; 318 } 319 Ok(()) 320 } 321 322 fn decorate_block<FW: FuncWriter>( 323 func_w: &mut FW, 324 w: &mut dyn Write, 325 func: &Function, 326 aliases: &SecondaryMap<Value, Vec<Value>>, 327 annotations: &DisplayFunctionAnnotations, 328 block: Block, 329 ) -> fmt::Result { 330 // Indent all instructions if any encodings are present. 331 let indent = if func.encodings.is_empty() && func.srclocs.is_empty() { 332 4 333 } else { 334 36 335 }; 336 let isa = annotations.isa; 337 338 func_w.write_block_header(w, func, isa, block, indent)?; 339 for a in func.dfg.block_params(block).iter().cloned() { 340 write_value_aliases(w, aliases, a, indent)?; 341 } 342 343 if let Some(isa) = isa { 344 if !func.offsets.is_empty() { 345 let encinfo = isa.encoding_info(); 346 let regs = &isa.register_info(); 347 for (offset, inst, size) in func.inst_offsets(block, &encinfo) { 348 func_w.write_instruction(w, func, aliases, Some(isa), inst, indent)?; 349 if size > 0 { 350 if let Some(val_ranges) = annotations.value_ranges { 351 write_value_range_markers(w, val_ranges, regs, offset + size, indent)?; 352 } 353 } 354 } 355 return Ok(()); 356 } 357 } 358 359 for inst in func.layout.block_insts(block) { 360 func_w.write_instruction(w, func, aliases, isa, inst, indent)?; 361 } 362 363 Ok(()) 364 } 365 366 //---------------------------------------------------------------------- 367 // 368 // Instructions 369 370 // Should `inst` be printed with a type suffix? 371 // 372 // Polymorphic instructions may need a suffix indicating the value of the controlling type variable 373 // if it can't be trivially inferred. 374 // 375 fn type_suffix(func: &Function, inst: Inst) -> Option<Type> { 376 let inst_data = &func.dfg[inst]; 377 let constraints = inst_data.opcode().constraints(); 378 379 if !constraints.is_polymorphic() { 380 return None; 381 } 382 383 // If the controlling type variable can be inferred from the type of the designated value input 384 // operand, we don't need the type suffix. 385 if constraints.use_typevar_operand() { 386 let ctrl_var = inst_data.typevar_operand(&func.dfg.value_lists).unwrap(); 387 let def_block = match func.dfg.value_def(ctrl_var) { 388 ValueDef::Result(instr, _) => func.layout.inst_block(instr), 389 ValueDef::Param(block, _) => Some(block), 390 }; 391 if def_block.is_some() && def_block == func.layout.inst_block(inst) { 392 return None; 393 } 394 } 395 396 let rtype = func.dfg.ctrl_typevar(inst); 397 assert!( 398 !rtype.is_invalid(), 399 "Polymorphic instruction must produce a result" 400 ); 401 Some(rtype) 402 } 403 404 /// Write out any aliases to the given target, including indirect aliases 405 fn write_value_aliases( 406 w: &mut dyn Write, 407 aliases: &SecondaryMap<Value, Vec<Value>>, 408 target: Value, 409 indent: usize, 410 ) -> fmt::Result { 411 let mut todo_stack = vec![target]; 412 while let Some(target) = todo_stack.pop() { 413 for &a in &aliases[target] { 414 writeln!(w, "{1:0$}{2} -> {3}", indent, "", a, target)?; 415 todo_stack.push(a); 416 } 417 } 418 419 Ok(()) 420 } 421 422 fn write_instruction( 423 w: &mut dyn Write, 424 func: &Function, 425 aliases: &SecondaryMap<Value, Vec<Value>>, 426 isa: Option<&dyn TargetIsa>, 427 inst: Inst, 428 indent: usize, 429 ) -> fmt::Result { 430 // Prefix containing source location, encoding, and value locations. 431 let mut s = String::with_capacity(16); 432 433 // Source location goes first. 434 let srcloc = func.srclocs[inst]; 435 if !srcloc.is_default() { 436 write!(s, "{} ", srcloc)?; 437 } 438 439 // Write out encoding info. 440 if let Some(enc) = func.encodings.get(inst).cloned() { 441 if let Some(isa) = isa { 442 write!(s, "[{}", isa.encoding_info().display(enc))?; 443 // Write value locations, if we have them. 444 if !func.locations.is_empty() { 445 let regs = isa.register_info(); 446 for &r in func.dfg.inst_results(inst) { 447 write!(s, ",{}", func.locations[r].display(®s))? 448 } 449 } 450 write!(s, "] ")?; 451 } else { 452 write!(s, "[{}] ", enc)?; 453 } 454 } 455 456 // Write out prefix and indent the instruction. 457 write!(w, "{1:0$}", indent, s)?; 458 459 // Write out the result values, if any. 460 let mut has_results = false; 461 for r in func.dfg.inst_results(inst) { 462 if !has_results { 463 has_results = true; 464 write!(w, "{}", r)?; 465 } else { 466 write!(w, ", {}", r)?; 467 } 468 } 469 if has_results { 470 write!(w, " = ")?; 471 } 472 473 // Then the opcode, possibly with a '.type' suffix. 474 let opcode = func.dfg[inst].opcode(); 475 476 match type_suffix(func, inst) { 477 Some(suf) => write!(w, "{}.{}", opcode, suf)?, 478 None => write!(w, "{}", opcode)?, 479 } 480 481 write_operands(w, &func.dfg, isa, inst)?; 482 writeln!(w)?; 483 484 // Value aliases come out on lines after the instruction defining the referent. 485 for r in func.dfg.inst_results(inst) { 486 write_value_aliases(w, aliases, *r, indent)?; 487 } 488 Ok(()) 489 } 490 491 /// Write the operands of `inst` to `w` with a prepended space. 492 pub fn write_operands( 493 w: &mut dyn Write, 494 dfg: &DataFlowGraph, 495 isa: Option<&dyn TargetIsa>, 496 inst: Inst, 497 ) -> fmt::Result { 498 let pool = &dfg.value_lists; 499 use crate::ir::instructions::InstructionData::*; 500 match dfg[inst] { 501 AtomicRmw { op, args, .. } => write!(w, " {}, {}, {}", op, args[0], args[1]), 502 AtomicCas { args, .. } => write!(w, " {}, {}, {}", args[0], args[1], args[2]), 503 LoadNoOffset { flags, arg, .. } => write!(w, "{} {}", flags, arg), 504 StoreNoOffset { flags, args, .. } => write!(w, "{} {}, {}", flags, args[0], args[1]), 505 Unary { arg, .. } => write!(w, " {}", arg), 506 UnaryImm { imm, .. } => write!(w, " {}", imm), 507 UnaryIeee32 { imm, .. } => write!(w, " {}", imm), 508 UnaryIeee64 { imm, .. } => write!(w, " {}", imm), 509 UnaryBool { imm, .. } => write!(w, " {}", imm), 510 UnaryGlobalValue { global_value, .. } => write!(w, " {}", global_value), 511 UnaryConst { 512 constant_handle, .. 513 } => write!(w, " {}", constant_handle), 514 Binary { args, .. } => write!(w, " {}, {}", args[0], args[1]), 515 BinaryImm8 { arg, imm, .. } => write!(w, " {}, {}", arg, imm), 516 BinaryImm64 { arg, imm, .. } => write!(w, " {}, {}", arg, imm), 517 Ternary { args, .. } => write!(w, " {}, {}, {}", args[0], args[1], args[2]), 518 MultiAry { ref args, .. } => { 519 if args.is_empty() { 520 write!(w, "") 521 } else { 522 write!(w, " {}", DisplayValues(args.as_slice(pool))) 523 } 524 } 525 NullAry { .. } => write!(w, " "), 526 TernaryImm8 { imm, args, .. } => write!(w, " {}, {}, {}", args[0], args[1], imm), 527 Shuffle { mask, args, .. } => { 528 let data = dfg.immediates.get(mask).expect( 529 "Expected the shuffle mask to already be inserted into the immediates table", 530 ); 531 write!(w, " {}, {}, {}", args[0], args[1], data) 532 } 533 IntCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]), 534 IntCompareImm { cond, arg, imm, .. } => write!(w, " {} {}, {}", cond, arg, imm), 535 IntCond { cond, arg, .. } => write!(w, " {} {}", cond, arg), 536 FloatCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]), 537 FloatCond { cond, arg, .. } => write!(w, " {} {}", cond, arg), 538 IntSelect { cond, args, .. } => { 539 write!(w, " {} {}, {}, {}", cond, args[0], args[1], args[2]) 540 } 541 Jump { 542 destination, 543 ref args, 544 .. 545 } => { 546 write!(w, " {}", destination)?; 547 write_block_args(w, args.as_slice(pool)) 548 } 549 Branch { 550 destination, 551 ref args, 552 .. 553 } => { 554 let args = args.as_slice(pool); 555 write!(w, " {}, {}", args[0], destination)?; 556 write_block_args(w, &args[1..]) 557 } 558 BranchInt { 559 cond, 560 destination, 561 ref args, 562 .. 563 } => { 564 let args = args.as_slice(pool); 565 write!(w, " {} {}, {}", cond, args[0], destination)?; 566 write_block_args(w, &args[1..]) 567 } 568 BranchFloat { 569 cond, 570 destination, 571 ref args, 572 .. 573 } => { 574 let args = args.as_slice(pool); 575 write!(w, " {} {}, {}", cond, args[0], destination)?; 576 write_block_args(w, &args[1..]) 577 } 578 BranchIcmp { 579 cond, 580 destination, 581 ref args, 582 .. 583 } => { 584 let args = args.as_slice(pool); 585 write!(w, " {} {}, {}, {}", cond, args[0], args[1], destination)?; 586 write_block_args(w, &args[2..]) 587 } 588 BranchTable { 589 arg, 590 destination, 591 table, 592 .. 593 } => write!(w, " {}, {}, {}", arg, destination, table), 594 BranchTableBase { table, .. } => write!(w, " {}", table), 595 BranchTableEntry { 596 args, imm, table, .. 597 } => write!(w, " {}, {}, {}, {}", args[0], args[1], imm, table), 598 IndirectJump { arg, table, .. } => write!(w, " {}, {}", arg, table), 599 Call { 600 func_ref, ref args, .. 601 } => write!(w, " {}({})", func_ref, DisplayValues(args.as_slice(pool))), 602 CallIndirect { 603 sig_ref, ref args, .. 604 } => { 605 let args = args.as_slice(pool); 606 write!( 607 w, 608 " {}, {}({})", 609 sig_ref, 610 args[0], 611 DisplayValues(&args[1..]) 612 ) 613 } 614 FuncAddr { func_ref, .. } => write!(w, " {}", func_ref), 615 StackLoad { 616 stack_slot, offset, .. 617 } => write!(w, " {}{}", stack_slot, offset), 618 StackStore { 619 arg, 620 stack_slot, 621 offset, 622 .. 623 } => write!(w, " {}, {}{}", arg, stack_slot, offset), 624 HeapAddr { heap, arg, imm, .. } => write!(w, " {}, {}, {}", heap, arg, imm), 625 TableAddr { table, arg, .. } => write!(w, " {}, {}", table, arg), 626 Load { 627 flags, arg, offset, .. 628 } => write!(w, "{} {}{}", flags, arg, offset), 629 LoadComplex { 630 flags, 631 ref args, 632 offset, 633 .. 634 } => { 635 let args = args.as_slice(pool); 636 write!( 637 w, 638 "{} {}{}", 639 flags, 640 DisplayValuesWithDelimiter(&args, '+'), 641 offset 642 ) 643 } 644 Store { 645 flags, 646 args, 647 offset, 648 .. 649 } => write!(w, "{} {}, {}{}", flags, args[0], args[1], offset), 650 StoreComplex { 651 flags, 652 ref args, 653 offset, 654 .. 655 } => { 656 let args = args.as_slice(pool); 657 write!( 658 w, 659 "{} {}, {}{}", 660 flags, 661 args[0], 662 DisplayValuesWithDelimiter(&args[1..], '+'), 663 offset 664 ) 665 } 666 RegMove { arg, src, dst, .. } => { 667 if let Some(isa) = isa { 668 let regs = isa.register_info(); 669 write!( 670 w, 671 " {}, {} -> {}", 672 arg, 673 regs.display_regunit(src), 674 regs.display_regunit(dst) 675 ) 676 } else { 677 write!(w, " {}, %{} -> %{}", arg, src, dst) 678 } 679 } 680 CopySpecial { src, dst, .. } => { 681 if let Some(isa) = isa { 682 let regs = isa.register_info(); 683 write!( 684 w, 685 " {} -> {}", 686 regs.display_regunit(src), 687 regs.display_regunit(dst) 688 ) 689 } else { 690 write!(w, " %{} -> %{}", src, dst) 691 } 692 } 693 CopyToSsa { src, .. } => { 694 if let Some(isa) = isa { 695 let regs = isa.register_info(); 696 write!(w, " {}", regs.display_regunit(src)) 697 } else { 698 write!(w, " %{}", src) 699 } 700 } 701 RegSpill { arg, src, dst, .. } => { 702 if let Some(isa) = isa { 703 let regs = isa.register_info(); 704 write!(w, " {}, {} -> {}", arg, regs.display_regunit(src), dst) 705 } else { 706 write!(w, " {}, %{} -> {}", arg, src, dst) 707 } 708 } 709 RegFill { arg, src, dst, .. } => { 710 if let Some(isa) = isa { 711 let regs = isa.register_info(); 712 write!(w, " {}, {} -> {}", arg, src, regs.display_regunit(dst)) 713 } else { 714 write!(w, " {}, {} -> %{}", arg, src, dst) 715 } 716 } 717 Trap { code, .. } => write!(w, " {}", code), 718 CondTrap { arg, code, .. } => write!(w, " {}, {}", arg, code), 719 IntCondTrap { 720 cond, arg, code, .. 721 } => write!(w, " {} {}, {}", cond, arg, code), 722 FloatCondTrap { 723 cond, arg, code, .. 724 } => write!(w, " {} {}, {}", cond, arg, code), 725 } 726 } 727 728 /// Write block args using optional parantheses. 729 fn write_block_args(w: &mut dyn Write, args: &[Value]) -> fmt::Result { 730 if args.is_empty() { 731 Ok(()) 732 } else { 733 write!(w, "({})", DisplayValues(args)) 734 } 735 } 736 737 /// Displayable slice of values. 738 struct DisplayValues<'a>(&'a [Value]); 739 740 impl<'a> fmt::Display for DisplayValues<'a> { 741 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 742 for (i, val) in self.0.iter().enumerate() { 743 if i == 0 { 744 write!(f, "{}", val)?; 745 } else { 746 write!(f, ", {}", val)?; 747 } 748 } 749 Ok(()) 750 } 751 } 752 753 struct DisplayValuesWithDelimiter<'a>(&'a [Value], char); 754 755 impl<'a> fmt::Display for DisplayValuesWithDelimiter<'a> { 756 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 757 for (i, val) in self.0.iter().enumerate() { 758 if i == 0 { 759 write!(f, "{}", val)?; 760 } else { 761 write!(f, "{}{}", self.1, val)?; 762 } 763 } 764 Ok(()) 765 } 766 } 767 768 #[cfg(test)] 769 mod tests { 770 use crate::cursor::{Cursor, CursorPosition, FuncCursor}; 771 use crate::ir::types; 772 use crate::ir::{ExternalName, Function, InstBuilder, StackSlotData, StackSlotKind}; 773 use alloc::string::ToString; 774 775 #[test] 776 fn basic() { 777 let mut f = Function::new(); 778 assert_eq!(f.to_string(), "function u0:0() fast {\n}\n"); 779 780 f.name = ExternalName::testcase("foo"); 781 assert_eq!(f.to_string(), "function %foo() fast {\n}\n"); 782 783 f.create_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 4)); 784 assert_eq!( 785 f.to_string(), 786 "function %foo() fast {\n ss0 = explicit_slot 4\n}\n" 787 ); 788 789 let block = f.dfg.make_block(); 790 f.layout.append_block(block); 791 assert_eq!( 792 f.to_string(), 793 "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0:\n}\n" 794 ); 795 796 f.dfg.append_block_param(block, types::I8); 797 assert_eq!( 798 f.to_string(), 799 "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8):\n}\n" 800 ); 801 802 f.dfg.append_block_param(block, types::F32.by(4).unwrap()); 803 assert_eq!( 804 f.to_string(), 805 "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8, v1: f32x4):\n}\n" 806 ); 807 808 { 809 let mut cursor = FuncCursor::new(&mut f); 810 cursor.set_position(CursorPosition::After(block)); 811 cursor.ins().return_(&[]) 812 }; 813 assert_eq!( 814 f.to_string(), 815 "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8, v1: f32x4):\n return\n}\n" 816 ); 817 } 818 819 #[test] 820 fn aliases() { 821 use crate::ir::InstBuilder; 822 823 let mut func = Function::new(); 824 { 825 let block0 = func.dfg.make_block(); 826 let mut pos = FuncCursor::new(&mut func); 827 pos.insert_block(block0); 828 829 // make some detached values for change_to_alias 830 let v0 = pos.func.dfg.append_block_param(block0, types::I32); 831 let v1 = pos.func.dfg.append_block_param(block0, types::I32); 832 let v2 = pos.func.dfg.append_block_param(block0, types::I32); 833 pos.func.dfg.detach_block_params(block0); 834 835 // alias to a param--will be printed at beginning of block defining param 836 let v3 = pos.func.dfg.append_block_param(block0, types::I32); 837 pos.func.dfg.change_to_alias(v0, v3); 838 839 // alias to an alias--should print attached to alias, not ultimate target 840 pos.func.dfg.make_value_alias_for_serialization(v0, v2); // v0 <- v2 841 842 // alias to a result--will be printed after instruction producing result 843 let _dummy0 = pos.ins().iconst(types::I32, 42); 844 let v4 = pos.ins().iadd(v0, v0); 845 pos.func.dfg.change_to_alias(v1, v4); 846 let _dummy1 = pos.ins().iconst(types::I32, 23); 847 let _v7 = pos.ins().iadd(v1, v1); 848 } 849 assert_eq!( 850 func.to_string(), 851 "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" 852 ); 853 } 854 } 855