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::{LabelValueLoc, 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: LabelValueLoc, regs: &RegInfo) -> fmt::Result { 282 match loc { 283 LabelValueLoc::ValueLoc(ValueLoc::Reg(r)) => write!(w, "{}", regs.display_regunit(r)), 284 LabelValueLoc::ValueLoc(ValueLoc::Stack(ss)) => write!(w, "{}", ss), 285 LabelValueLoc::ValueLoc(ValueLoc::Unassigned) => write!(w, "?"), 286 LabelValueLoc::Reg(r) => write!(w, "{:?}", r), 287 LabelValueLoc::SPOffset(off) => write!(w, "[sp+{}]", off), 288 } 289 } 290 291 fn write_value_range_markers( 292 w: &mut dyn Write, 293 val_ranges: &ValueLabelsRanges, 294 regs: &RegInfo, 295 offset: u32, 296 indent: usize, 297 ) -> fmt::Result { 298 let mut result = String::new(); 299 let mut shown = HashSet::new(); 300 for (val, rng) in val_ranges { 301 for i in (0..rng.len()).rev() { 302 if rng[i].start == offset { 303 write!(&mut result, " {}@", val)?; 304 write_valueloc(&mut result, rng[i].loc, regs)?; 305 shown.insert(val); 306 break; 307 } 308 } 309 } 310 for (val, rng) in val_ranges { 311 for i in (0..rng.len()).rev() { 312 if rng[i].end == offset && !shown.contains(val) { 313 write!(&mut result, " {}\u{2620}", val)?; 314 break; 315 } 316 } 317 } 318 if !result.is_empty() { 319 writeln!(w, ";{1:0$}; {2}", indent + 24, "", result)?; 320 } 321 Ok(()) 322 } 323 324 fn decorate_block<FW: FuncWriter>( 325 func_w: &mut FW, 326 w: &mut dyn Write, 327 func: &Function, 328 aliases: &SecondaryMap<Value, Vec<Value>>, 329 annotations: &DisplayFunctionAnnotations, 330 block: Block, 331 ) -> fmt::Result { 332 // Indent all instructions if any encodings are present. 333 let indent = if func.encodings.is_empty() && func.srclocs.is_empty() { 334 4 335 } else { 336 36 337 }; 338 let isa = annotations.isa; 339 340 func_w.write_block_header(w, func, isa, block, indent)?; 341 for a in func.dfg.block_params(block).iter().cloned() { 342 write_value_aliases(w, aliases, a, indent)?; 343 } 344 345 if let Some(isa) = isa { 346 if !func.offsets.is_empty() { 347 let encinfo = isa.encoding_info(); 348 let regs = &isa.register_info(); 349 for (offset, inst, size) in func.inst_offsets(block, &encinfo) { 350 func_w.write_instruction(w, func, aliases, Some(isa), inst, indent)?; 351 if size > 0 { 352 if let Some(val_ranges) = annotations.value_ranges { 353 write_value_range_markers(w, val_ranges, regs, offset + size, indent)?; 354 } 355 } 356 } 357 return Ok(()); 358 } 359 } 360 361 for inst in func.layout.block_insts(block) { 362 func_w.write_instruction(w, func, aliases, isa, inst, indent)?; 363 } 364 365 Ok(()) 366 } 367 368 //---------------------------------------------------------------------- 369 // 370 // Instructions 371 372 // Should `inst` be printed with a type suffix? 373 // 374 // Polymorphic instructions may need a suffix indicating the value of the controlling type variable 375 // if it can't be trivially inferred. 376 // 377 fn type_suffix(func: &Function, inst: Inst) -> Option<Type> { 378 let inst_data = &func.dfg[inst]; 379 let constraints = inst_data.opcode().constraints(); 380 381 if !constraints.is_polymorphic() { 382 return None; 383 } 384 385 // If the controlling type variable can be inferred from the type of the designated value input 386 // operand, we don't need the type suffix. 387 if constraints.use_typevar_operand() { 388 let ctrl_var = inst_data.typevar_operand(&func.dfg.value_lists).unwrap(); 389 let def_block = match func.dfg.value_def(ctrl_var) { 390 ValueDef::Result(instr, _) => func.layout.inst_block(instr), 391 ValueDef::Param(block, _) => Some(block), 392 }; 393 if def_block.is_some() && def_block == func.layout.inst_block(inst) { 394 return None; 395 } 396 } 397 398 let rtype = func.dfg.ctrl_typevar(inst); 399 assert!( 400 !rtype.is_invalid(), 401 "Polymorphic instruction must produce a result" 402 ); 403 Some(rtype) 404 } 405 406 /// Write out any aliases to the given target, including indirect aliases 407 fn write_value_aliases( 408 w: &mut dyn Write, 409 aliases: &SecondaryMap<Value, Vec<Value>>, 410 target: Value, 411 indent: usize, 412 ) -> fmt::Result { 413 let mut todo_stack = vec![target]; 414 while let Some(target) = todo_stack.pop() { 415 for &a in &aliases[target] { 416 writeln!(w, "{1:0$}{2} -> {3}", indent, "", a, target)?; 417 todo_stack.push(a); 418 } 419 } 420 421 Ok(()) 422 } 423 424 fn write_instruction( 425 w: &mut dyn Write, 426 func: &Function, 427 aliases: &SecondaryMap<Value, Vec<Value>>, 428 isa: Option<&dyn TargetIsa>, 429 inst: Inst, 430 indent: usize, 431 ) -> fmt::Result { 432 // Prefix containing source location, encoding, and value locations. 433 let mut s = String::with_capacity(16); 434 435 // Source location goes first. 436 let srcloc = func.srclocs[inst]; 437 if !srcloc.is_default() { 438 write!(s, "{} ", srcloc)?; 439 } 440 441 // Write out encoding info. 442 if let Some(enc) = func.encodings.get(inst).cloned() { 443 if let Some(isa) = isa { 444 write!(s, "[{}", isa.encoding_info().display(enc))?; 445 // Write value locations, if we have them. 446 if !func.locations.is_empty() { 447 let regs = isa.register_info(); 448 for &r in func.dfg.inst_results(inst) { 449 write!(s, ",{}", func.locations[r].display(®s))? 450 } 451 } 452 write!(s, "] ")?; 453 } else { 454 write!(s, "[{}] ", enc)?; 455 } 456 } 457 458 // Write out prefix and indent the instruction. 459 write!(w, "{1:0$}", indent, s)?; 460 461 // Write out the result values, if any. 462 let mut has_results = false; 463 for r in func.dfg.inst_results(inst) { 464 if !has_results { 465 has_results = true; 466 write!(w, "{}", r)?; 467 } else { 468 write!(w, ", {}", r)?; 469 } 470 } 471 if has_results { 472 write!(w, " = ")?; 473 } 474 475 // Then the opcode, possibly with a '.type' suffix. 476 let opcode = func.dfg[inst].opcode(); 477 478 match type_suffix(func, inst) { 479 Some(suf) => write!(w, "{}.{}", opcode, suf)?, 480 None => write!(w, "{}", opcode)?, 481 } 482 483 write_operands(w, &func.dfg, isa, inst)?; 484 writeln!(w)?; 485 486 // Value aliases come out on lines after the instruction defining the referent. 487 for r in func.dfg.inst_results(inst) { 488 write_value_aliases(w, aliases, *r, indent)?; 489 } 490 Ok(()) 491 } 492 493 /// Write the operands of `inst` to `w` with a prepended space. 494 pub fn write_operands( 495 w: &mut dyn Write, 496 dfg: &DataFlowGraph, 497 isa: Option<&dyn TargetIsa>, 498 inst: Inst, 499 ) -> fmt::Result { 500 let pool = &dfg.value_lists; 501 use crate::ir::instructions::InstructionData::*; 502 match dfg[inst] { 503 AtomicRmw { op, args, .. } => write!(w, " {}, {}, {}", op, args[0], args[1]), 504 AtomicCas { args, .. } => write!(w, " {}, {}, {}", args[0], args[1], args[2]), 505 LoadNoOffset { flags, arg, .. } => write!(w, "{} {}", flags, arg), 506 StoreNoOffset { flags, args, .. } => write!(w, "{} {}, {}", flags, args[0], args[1]), 507 Unary { arg, .. } => write!(w, " {}", arg), 508 UnaryImm { imm, .. } => write!(w, " {}", imm), 509 UnaryIeee32 { imm, .. } => write!(w, " {}", imm), 510 UnaryIeee64 { imm, .. } => write!(w, " {}", imm), 511 UnaryBool { imm, .. } => write!(w, " {}", imm), 512 UnaryGlobalValue { global_value, .. } => write!(w, " {}", global_value), 513 UnaryConst { 514 constant_handle, .. 515 } => write!(w, " {}", constant_handle), 516 Binary { args, .. } => write!(w, " {}, {}", args[0], args[1]), 517 BinaryImm8 { arg, imm, .. } => write!(w, " {}, {}", arg, imm), 518 BinaryImm64 { arg, imm, .. } => write!(w, " {}, {}", arg, imm), 519 Ternary { args, .. } => write!(w, " {}, {}, {}", args[0], args[1], args[2]), 520 MultiAry { ref args, .. } => { 521 if args.is_empty() { 522 write!(w, "") 523 } else { 524 write!(w, " {}", DisplayValues(args.as_slice(pool))) 525 } 526 } 527 NullAry { .. } => write!(w, " "), 528 TernaryImm8 { imm, args, .. } => write!(w, " {}, {}, {}", args[0], args[1], imm), 529 Shuffle { mask, args, .. } => { 530 let data = dfg.immediates.get(mask).expect( 531 "Expected the shuffle mask to already be inserted into the immediates table", 532 ); 533 write!(w, " {}, {}, {}", args[0], args[1], data) 534 } 535 IntCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]), 536 IntCompareImm { cond, arg, imm, .. } => write!(w, " {} {}, {}", cond, arg, imm), 537 IntCond { cond, arg, .. } => write!(w, " {} {}", cond, arg), 538 FloatCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]), 539 FloatCond { cond, arg, .. } => write!(w, " {} {}", cond, arg), 540 IntSelect { cond, args, .. } => { 541 write!(w, " {} {}, {}, {}", cond, args[0], args[1], args[2]) 542 } 543 Jump { 544 destination, 545 ref args, 546 .. 547 } => { 548 write!(w, " {}", destination)?; 549 write_block_args(w, args.as_slice(pool)) 550 } 551 Branch { 552 destination, 553 ref args, 554 .. 555 } => { 556 let args = args.as_slice(pool); 557 write!(w, " {}, {}", args[0], destination)?; 558 write_block_args(w, &args[1..]) 559 } 560 BranchInt { 561 cond, 562 destination, 563 ref args, 564 .. 565 } => { 566 let args = args.as_slice(pool); 567 write!(w, " {} {}, {}", cond, args[0], destination)?; 568 write_block_args(w, &args[1..]) 569 } 570 BranchFloat { 571 cond, 572 destination, 573 ref args, 574 .. 575 } => { 576 let args = args.as_slice(pool); 577 write!(w, " {} {}, {}", cond, args[0], destination)?; 578 write_block_args(w, &args[1..]) 579 } 580 BranchIcmp { 581 cond, 582 destination, 583 ref args, 584 .. 585 } => { 586 let args = args.as_slice(pool); 587 write!(w, " {} {}, {}, {}", cond, args[0], args[1], destination)?; 588 write_block_args(w, &args[2..]) 589 } 590 BranchTable { 591 arg, 592 destination, 593 table, 594 .. 595 } => write!(w, " {}, {}, {}", arg, destination, table), 596 BranchTableBase { table, .. } => write!(w, " {}", table), 597 BranchTableEntry { 598 args, imm, table, .. 599 } => write!(w, " {}, {}, {}, {}", args[0], args[1], imm, table), 600 IndirectJump { arg, table, .. } => write!(w, " {}, {}", arg, table), 601 Call { 602 func_ref, ref args, .. 603 } => write!(w, " {}({})", func_ref, DisplayValues(args.as_slice(pool))), 604 CallIndirect { 605 sig_ref, ref args, .. 606 } => { 607 let args = args.as_slice(pool); 608 write!( 609 w, 610 " {}, {}({})", 611 sig_ref, 612 args[0], 613 DisplayValues(&args[1..]) 614 ) 615 } 616 FuncAddr { func_ref, .. } => write!(w, " {}", func_ref), 617 StackLoad { 618 stack_slot, offset, .. 619 } => write!(w, " {}{}", stack_slot, offset), 620 StackStore { 621 arg, 622 stack_slot, 623 offset, 624 .. 625 } => write!(w, " {}, {}{}", arg, stack_slot, offset), 626 HeapAddr { heap, arg, imm, .. } => write!(w, " {}, {}, {}", heap, arg, imm), 627 TableAddr { table, arg, .. } => write!(w, " {}, {}", table, arg), 628 Load { 629 flags, arg, offset, .. 630 } => write!(w, "{} {}{}", flags, arg, offset), 631 LoadComplex { 632 flags, 633 ref args, 634 offset, 635 .. 636 } => { 637 let args = args.as_slice(pool); 638 write!( 639 w, 640 "{} {}{}", 641 flags, 642 DisplayValuesWithDelimiter(&args, '+'), 643 offset 644 ) 645 } 646 Store { 647 flags, 648 args, 649 offset, 650 .. 651 } => write!(w, "{} {}, {}{}", flags, args[0], args[1], offset), 652 StoreComplex { 653 flags, 654 ref args, 655 offset, 656 .. 657 } => { 658 let args = args.as_slice(pool); 659 write!( 660 w, 661 "{} {}, {}{}", 662 flags, 663 args[0], 664 DisplayValuesWithDelimiter(&args[1..], '+'), 665 offset 666 ) 667 } 668 RegMove { arg, src, dst, .. } => { 669 if let Some(isa) = isa { 670 let regs = isa.register_info(); 671 write!( 672 w, 673 " {}, {} -> {}", 674 arg, 675 regs.display_regunit(src), 676 regs.display_regunit(dst) 677 ) 678 } else { 679 write!(w, " {}, %{} -> %{}", arg, src, dst) 680 } 681 } 682 CopySpecial { src, dst, .. } => { 683 if let Some(isa) = isa { 684 let regs = isa.register_info(); 685 write!( 686 w, 687 " {} -> {}", 688 regs.display_regunit(src), 689 regs.display_regunit(dst) 690 ) 691 } else { 692 write!(w, " %{} -> %{}", src, dst) 693 } 694 } 695 CopyToSsa { src, .. } => { 696 if let Some(isa) = isa { 697 let regs = isa.register_info(); 698 write!(w, " {}", regs.display_regunit(src)) 699 } else { 700 write!(w, " %{}", src) 701 } 702 } 703 RegSpill { arg, src, dst, .. } => { 704 if let Some(isa) = isa { 705 let regs = isa.register_info(); 706 write!(w, " {}, {} -> {}", arg, regs.display_regunit(src), dst) 707 } else { 708 write!(w, " {}, %{} -> {}", arg, src, dst) 709 } 710 } 711 RegFill { arg, src, dst, .. } => { 712 if let Some(isa) = isa { 713 let regs = isa.register_info(); 714 write!(w, " {}, {} -> {}", arg, src, regs.display_regunit(dst)) 715 } else { 716 write!(w, " {}, {} -> %{}", arg, src, dst) 717 } 718 } 719 Trap { code, .. } => write!(w, " {}", code), 720 CondTrap { arg, code, .. } => write!(w, " {}, {}", arg, code), 721 IntCondTrap { 722 cond, arg, code, .. 723 } => write!(w, " {} {}, {}", cond, arg, code), 724 FloatCondTrap { 725 cond, arg, code, .. 726 } => write!(w, " {} {}, {}", cond, arg, code), 727 } 728 } 729 730 /// Write block args using optional parantheses. 731 fn write_block_args(w: &mut dyn Write, args: &[Value]) -> fmt::Result { 732 if args.is_empty() { 733 Ok(()) 734 } else { 735 write!(w, "({})", DisplayValues(args)) 736 } 737 } 738 739 /// Displayable slice of values. 740 struct DisplayValues<'a>(&'a [Value]); 741 742 impl<'a> fmt::Display for DisplayValues<'a> { 743 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 744 for (i, val) in self.0.iter().enumerate() { 745 if i == 0 { 746 write!(f, "{}", val)?; 747 } else { 748 write!(f, ", {}", val)?; 749 } 750 } 751 Ok(()) 752 } 753 } 754 755 struct DisplayValuesWithDelimiter<'a>(&'a [Value], char); 756 757 impl<'a> fmt::Display for DisplayValuesWithDelimiter<'a> { 758 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 759 for (i, val) in self.0.iter().enumerate() { 760 if i == 0 { 761 write!(f, "{}", val)?; 762 } else { 763 write!(f, "{}{}", self.1, val)?; 764 } 765 } 766 Ok(()) 767 } 768 } 769 770 #[cfg(test)] 771 mod tests { 772 use crate::cursor::{Cursor, CursorPosition, FuncCursor}; 773 use crate::ir::types; 774 use crate::ir::{ExternalName, Function, InstBuilder, StackSlotData, StackSlotKind}; 775 use alloc::string::ToString; 776 777 #[test] 778 fn basic() { 779 let mut f = Function::new(); 780 assert_eq!(f.to_string(), "function u0:0() fast {\n}\n"); 781 782 f.name = ExternalName::testcase("foo"); 783 assert_eq!(f.to_string(), "function %foo() fast {\n}\n"); 784 785 f.create_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 4)); 786 assert_eq!( 787 f.to_string(), 788 "function %foo() fast {\n ss0 = explicit_slot 4\n}\n" 789 ); 790 791 let block = f.dfg.make_block(); 792 f.layout.append_block(block); 793 assert_eq!( 794 f.to_string(), 795 "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0:\n}\n" 796 ); 797 798 f.dfg.append_block_param(block, types::I8); 799 assert_eq!( 800 f.to_string(), 801 "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8):\n}\n" 802 ); 803 804 f.dfg.append_block_param(block, types::F32.by(4).unwrap()); 805 assert_eq!( 806 f.to_string(), 807 "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8, v1: f32x4):\n}\n" 808 ); 809 810 { 811 let mut cursor = FuncCursor::new(&mut f); 812 cursor.set_position(CursorPosition::After(block)); 813 cursor.ins().return_(&[]) 814 }; 815 assert_eq!( 816 f.to_string(), 817 "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8, v1: f32x4):\n return\n}\n" 818 ); 819 } 820 821 #[test] 822 fn aliases() { 823 use crate::ir::InstBuilder; 824 825 let mut func = Function::new(); 826 { 827 let block0 = func.dfg.make_block(); 828 let mut pos = FuncCursor::new(&mut func); 829 pos.insert_block(block0); 830 831 // make some detached values for change_to_alias 832 let v0 = pos.func.dfg.append_block_param(block0, types::I32); 833 let v1 = pos.func.dfg.append_block_param(block0, types::I32); 834 let v2 = pos.func.dfg.append_block_param(block0, types::I32); 835 pos.func.dfg.detach_block_params(block0); 836 837 // alias to a param--will be printed at beginning of block defining param 838 let v3 = pos.func.dfg.append_block_param(block0, types::I32); 839 pos.func.dfg.change_to_alias(v0, v3); 840 841 // alias to an alias--should print attached to alias, not ultimate target 842 pos.func.dfg.make_value_alias_for_serialization(v0, v2); // v0 <- v2 843 844 // alias to a result--will be printed after instruction producing result 845 let _dummy0 = pos.ins().iconst(types::I32, 42); 846 let v4 = pos.ins().iadd(v0, v0); 847 pos.func.dfg.change_to_alias(v1, v4); 848 let _dummy1 = pos.ins().iconst(types::I32, 23); 849 let _v7 = pos.ins().iadd(v1, v1); 850 } 851 assert_eq!( 852 func.to_string(), 853 "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" 854 ); 855 } 856 } 857