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::{HeapImmData, Uimm32}; 9 use crate::ir::{Block, DataFlowGraph, Function, Inst, 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 for (heap, heap_data) in &func.heaps { 61 if !heap_data.index_type.is_invalid() { 62 any = true; 63 self.write_entity_definition(w, func, heap.into(), heap_data)?; 64 } 65 } 66 67 for (table, table_data) in &func.tables { 68 if !table_data.index_type.is_invalid() { 69 any = true; 70 self.write_entity_definition(w, func, table.into(), table_data)?; 71 } 72 } 73 74 // Write out all signatures before functions since function declarations can refer to 75 // signatures. 76 for (sig, sig_data) in &func.dfg.signatures { 77 any = true; 78 self.write_entity_definition(w, func, sig.into(), &sig_data)?; 79 } 80 81 for (fnref, ext_func) in &func.dfg.ext_funcs { 82 if ext_func.signature != SigRef::reserved_value() { 83 any = true; 84 self.write_entity_definition( 85 w, 86 func, 87 fnref.into(), 88 &ext_func.display(Some(&func.params)), 89 )?; 90 } 91 } 92 93 for (jt, jt_data) in &func.jump_tables { 94 any = true; 95 self.write_entity_definition(w, func, jt.into(), jt_data)?; 96 } 97 98 for (&cref, cval) in func.dfg.constants.iter() { 99 any = true; 100 self.write_entity_definition(w, func, cref.into(), cval)?; 101 } 102 103 if let Some(limit) = func.stack_limit { 104 any = true; 105 self.write_entity_definition(w, func, AnyEntity::StackLimit, &limit)?; 106 } 107 108 Ok(any) 109 } 110 111 /// Write an entity definition defined in the preamble to `w`. 112 fn write_entity_definition( 113 &mut self, 114 w: &mut dyn Write, 115 func: &Function, 116 entity: AnyEntity, 117 value: &dyn fmt::Display, 118 ) -> fmt::Result { 119 self.super_entity_definition(w, func, entity, value) 120 } 121 122 /// Default impl of `write_entity_definition` 123 #[allow(unused_variables)] 124 fn super_entity_definition( 125 &mut self, 126 w: &mut dyn Write, 127 func: &Function, 128 entity: AnyEntity, 129 value: &dyn fmt::Display, 130 ) -> fmt::Result { 131 writeln!(w, " {} = {}", entity, value) 132 } 133 } 134 135 /// A `PlainWriter` that doesn't decorate the function. 136 pub struct PlainWriter; 137 138 impl FuncWriter for PlainWriter { 139 fn write_instruction( 140 &mut self, 141 w: &mut dyn Write, 142 func: &Function, 143 aliases: &SecondaryMap<Value, Vec<Value>>, 144 inst: Inst, 145 indent: usize, 146 ) -> fmt::Result { 147 write_instruction(w, func, aliases, inst, indent) 148 } 149 150 fn write_block_header( 151 &mut self, 152 w: &mut dyn Write, 153 func: &Function, 154 block: Block, 155 indent: usize, 156 ) -> fmt::Result { 157 write_block_header(w, func, block, indent) 158 } 159 } 160 161 /// Write `func` to `w` as equivalent text. 162 /// Use `isa` to emit ISA-dependent annotations. 163 pub fn write_function(w: &mut dyn Write, func: &Function) -> fmt::Result { 164 decorate_function(&mut PlainWriter, w, func) 165 } 166 167 /// Create a reverse-alias map from a value to all aliases having that value as a direct target 168 fn alias_map(func: &Function) -> SecondaryMap<Value, Vec<Value>> { 169 let mut aliases = SecondaryMap::<_, Vec<_>>::new(); 170 for v in func.dfg.values() { 171 // VADFS returns the immediate target of an alias 172 if let Some(k) = func.dfg.value_alias_dest_for_serialization(v) { 173 aliases[k].push(v); 174 } 175 } 176 aliases 177 } 178 179 /// Writes `func` to `w` as text. 180 /// write_function_plain is passed as 'closure' to print instructions as text. 181 /// pretty_function_error is passed as 'closure' to add error decoration. 182 pub fn decorate_function<FW: FuncWriter>( 183 func_w: &mut FW, 184 w: &mut dyn Write, 185 func: &Function, 186 ) -> fmt::Result { 187 write!(w, "function ")?; 188 write_spec(w, func)?; 189 writeln!(w, " {{")?; 190 let aliases = alias_map(func); 191 let mut any = func_w.write_preamble(w, func)?; 192 for block in &func.layout { 193 if any { 194 writeln!(w)?; 195 } 196 decorate_block(func_w, w, func, &aliases, block)?; 197 any = true; 198 } 199 writeln!(w, "}}") 200 } 201 202 //---------------------------------------------------------------------- 203 // 204 // Function spec. 205 206 fn write_spec(w: &mut dyn Write, func: &Function) -> fmt::Result { 207 write!(w, "{}{}", func.name, func.signature) 208 } 209 210 //---------------------------------------------------------------------- 211 // 212 // Basic blocks 213 214 fn write_arg(w: &mut dyn Write, func: &Function, arg: Value) -> fmt::Result { 215 write!(w, "{}: {}", arg, func.dfg.value_type(arg)) 216 } 217 218 /// Write out the basic block header, outdented: 219 /// 220 /// block1: 221 /// block1(v1: i32): 222 /// block10(v4: f64, v5: b1): 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 are present. 263 let indent = if func.rel_srclocs().is_empty() { 4 } else { 36 }; 264 265 func_w.write_block_header(w, func, block, indent)?; 266 for a in func.dfg.block_params(block).iter().cloned() { 267 write_value_aliases(w, aliases, a, indent)?; 268 } 269 270 for inst in func.layout.block_insts(block) { 271 func_w.write_instruction(w, func, aliases, inst, indent)?; 272 } 273 274 Ok(()) 275 } 276 277 //---------------------------------------------------------------------- 278 // 279 // Instructions 280 281 // Should `inst` be printed with a type suffix? 282 // 283 // Polymorphic instructions may need a suffix indicating the value of the controlling type variable 284 // if it can't be trivially inferred. 285 // 286 fn type_suffix(func: &Function, inst: Inst) -> Option<Type> { 287 let inst_data = &func.dfg[inst]; 288 let constraints = inst_data.opcode().constraints(); 289 290 if !constraints.is_polymorphic() { 291 return None; 292 } 293 294 // If the controlling type variable can be inferred from the type of the designated value input 295 // operand, we don't need the type suffix. 296 if constraints.use_typevar_operand() { 297 let ctrl_var = inst_data.typevar_operand(&func.dfg.value_lists).unwrap(); 298 let def_block = match func.dfg.value_def(ctrl_var) { 299 ValueDef::Result(instr, _) => func.layout.inst_block(instr), 300 ValueDef::Param(block, _) => Some(block), 301 }; 302 if def_block.is_some() && def_block == func.layout.inst_block(inst) { 303 return None; 304 } 305 } 306 307 let rtype = func.dfg.ctrl_typevar(inst); 308 assert!( 309 !rtype.is_invalid(), 310 "Polymorphic instruction must produce a result" 311 ); 312 Some(rtype) 313 } 314 315 /// Write out any aliases to the given target, including indirect aliases 316 fn write_value_aliases( 317 w: &mut dyn Write, 318 aliases: &SecondaryMap<Value, Vec<Value>>, 319 target: Value, 320 indent: usize, 321 ) -> fmt::Result { 322 let mut todo_stack = vec![target]; 323 while let Some(target) = todo_stack.pop() { 324 for &a in &aliases[target] { 325 writeln!(w, "{1:0$}{2} -> {3}", indent, "", a, target)?; 326 todo_stack.push(a); 327 } 328 } 329 330 Ok(()) 331 } 332 333 fn write_instruction( 334 w: &mut dyn Write, 335 func: &Function, 336 aliases: &SecondaryMap<Value, Vec<Value>>, 337 inst: Inst, 338 indent: usize, 339 ) -> fmt::Result { 340 // Prefix containing source location, encoding, and value locations. 341 let mut s = String::with_capacity(16); 342 343 // Source location goes first. 344 let srcloc = func.srcloc(inst); 345 if !srcloc.is_default() { 346 write!(s, "{} ", srcloc)?; 347 } 348 349 // Write out prefix and indent the instruction. 350 write!(w, "{1:0$}", indent, s)?; 351 352 // Write out the result values, if any. 353 let mut has_results = false; 354 for r in func.dfg.inst_results(inst) { 355 if !has_results { 356 has_results = true; 357 write!(w, "{}", r)?; 358 } else { 359 write!(w, ", {}", r)?; 360 } 361 } 362 if has_results { 363 write!(w, " = ")?; 364 } 365 366 // Then the opcode, possibly with a '.type' suffix. 367 let opcode = func.dfg[inst].opcode(); 368 369 match type_suffix(func, inst) { 370 Some(suf) => write!(w, "{}.{}", opcode, suf)?, 371 None => write!(w, "{}", opcode)?, 372 } 373 374 write_operands(w, &func.dfg, inst)?; 375 writeln!(w)?; 376 377 // Value aliases come out on lines after the instruction defining the referent. 378 for r in func.dfg.inst_results(inst) { 379 write_value_aliases(w, aliases, *r, indent)?; 380 } 381 Ok(()) 382 } 383 384 /// Write the operands of `inst` to `w` with a prepended space. 385 pub fn write_operands(w: &mut dyn Write, dfg: &DataFlowGraph, inst: Inst) -> fmt::Result { 386 let pool = &dfg.value_lists; 387 use crate::ir::instructions::InstructionData::*; 388 match dfg[inst] { 389 AtomicRmw { op, args, .. } => write!(w, " {} {}, {}", op, args[0], args[1]), 390 AtomicCas { args, .. } => write!(w, " {}, {}, {}", args[0], args[1], args[2]), 391 LoadNoOffset { flags, arg, .. } => write!(w, "{} {}", flags, arg), 392 StoreNoOffset { flags, args, .. } => write!(w, "{} {}, {}", flags, args[0], args[1]), 393 Unary { arg, .. } => write!(w, " {}", arg), 394 UnaryImm { 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, imm), 404 Ternary { args, .. } => write!(w, " {}, {}, {}", args[0], args[1], args[2]), 405 MultiAry { ref args, .. } => { 406 if args.is_empty() { 407 write!(w, "") 408 } else { 409 write!(w, " {}", DisplayValues(args.as_slice(pool))) 410 } 411 } 412 NullAry { .. } => write!(w, " "), 413 TernaryImm8 { imm, args, .. } => write!(w, " {}, {}, {}", args[0], args[1], imm), 414 Shuffle { imm, args, .. } => { 415 let data = dfg.immediates.get(imm).expect( 416 "Expected the shuffle mask to already be inserted into the immediates table", 417 ); 418 write!(w, " {}, {}, {}", args[0], args[1], data) 419 } 420 IntCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]), 421 IntCompareImm { cond, arg, imm, .. } => write!(w, " {} {}, {}", cond, arg, imm), 422 IntAddTrap { args, code, .. } => write!(w, " {}, {}, {}", args[0], args[1], code), 423 FloatCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]), 424 Jump { 425 destination, 426 ref args, 427 .. 428 } => { 429 write!(w, " {}", destination)?; 430 write_block_args(w, args.as_slice(pool)) 431 } 432 Branch { 433 destination, 434 ref args, 435 .. 436 } => { 437 let args = args.as_slice(pool); 438 write!(w, " {}, {}", args[0], destination)?; 439 write_block_args(w, &args[1..]) 440 } 441 BranchTable { 442 arg, 443 destination, 444 table, 445 .. 446 } => write!(w, " {}, {}, {}", arg, destination, table), 447 Call { 448 func_ref, ref args, .. 449 } => write!(w, " {}({})", func_ref, DisplayValues(args.as_slice(pool))), 450 CallIndirect { 451 sig_ref, ref args, .. 452 } => { 453 let args = args.as_slice(pool); 454 write!( 455 w, 456 " {}, {}({})", 457 sig_ref, 458 args[0], 459 DisplayValues(&args[1..]) 460 ) 461 } 462 FuncAddr { func_ref, .. } => write!(w, " {}", func_ref), 463 StackLoad { 464 stack_slot, offset, .. 465 } => write!(w, " {}{}", stack_slot, offset), 466 StackStore { 467 arg, 468 stack_slot, 469 offset, 470 .. 471 } => write!(w, " {}, {}{}", arg, stack_slot, offset), 472 DynamicStackLoad { 473 dynamic_stack_slot, .. 474 } => write!(w, " {}", dynamic_stack_slot), 475 DynamicStackStore { 476 arg, 477 dynamic_stack_slot, 478 .. 479 } => write!(w, " {}, {}", arg, dynamic_stack_slot), 480 HeapLoad { 481 opcode: _, 482 heap_imm, 483 arg, 484 } => { 485 let HeapImmData { 486 flags, 487 heap, 488 offset, 489 } = dfg.heap_imms[heap_imm]; 490 write!( 491 w, 492 " {heap} {flags} {arg}{optional_offset}", 493 optional_offset = if offset == Uimm32::from(0) { 494 "".to_string() 495 } else { 496 format!("+{offset}") 497 } 498 ) 499 } 500 HeapStore { 501 opcode: _, 502 heap_imm, 503 args, 504 } => { 505 let HeapImmData { 506 flags, 507 heap, 508 offset, 509 } = dfg.heap_imms[heap_imm]; 510 let [index, value] = args; 511 write!( 512 w, 513 " {heap} {flags} {index}{optional_offset}, {value}", 514 optional_offset = if offset == Uimm32::from(0) { 515 "".to_string() 516 } else { 517 format!("+{offset}") 518 } 519 ) 520 } 521 HeapAddr { 522 heap, 523 arg, 524 offset, 525 size, 526 .. 527 } => write!(w, " {}, {}, {}, {}", heap, arg, offset, size), 528 TableAddr { table, arg, .. } => write!(w, " {}, {}", table, arg), 529 Load { 530 flags, arg, offset, .. 531 } => write!(w, "{} {}{}", flags, arg, offset), 532 Store { 533 flags, 534 args, 535 offset, 536 .. 537 } => write!(w, "{} {}, {}{}", flags, args[0], args[1], offset), 538 Trap { code, .. } => write!(w, " {}", code), 539 CondTrap { arg, code, .. } => write!(w, " {}, {}", arg, code), 540 }?; 541 542 let mut sep = " ; "; 543 for &arg in dfg.inst_args(inst) { 544 if let ValueDef::Result(src, _) = dfg.value_def(arg) { 545 let imm = match dfg[src] { 546 UnaryImm { imm, .. } => imm.to_string(), 547 UnaryIeee32 { imm, .. } => imm.to_string(), 548 UnaryIeee64 { imm, .. } => imm.to_string(), 549 UnaryConst { 550 constant_handle, .. 551 } => constant_handle.to_string(), 552 _ => continue, 553 }; 554 write!(w, "{}{} = {}", sep, arg, imm)?; 555 sep = ", "; 556 } 557 } 558 Ok(()) 559 } 560 561 /// Write block args using optional parantheses. 562 fn write_block_args(w: &mut dyn Write, args: &[Value]) -> fmt::Result { 563 if args.is_empty() { 564 Ok(()) 565 } else { 566 write!(w, "({})", DisplayValues(args)) 567 } 568 } 569 570 /// Displayable slice of values. 571 struct DisplayValues<'a>(&'a [Value]); 572 573 impl<'a> fmt::Display for DisplayValues<'a> { 574 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 575 for (i, val) in self.0.iter().enumerate() { 576 if i == 0 { 577 write!(f, "{}", val)?; 578 } else { 579 write!(f, ", {}", val)?; 580 } 581 } 582 Ok(()) 583 } 584 } 585 586 struct DisplayValuesWithDelimiter<'a>(&'a [Value], char); 587 588 impl<'a> fmt::Display for DisplayValuesWithDelimiter<'a> { 589 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 590 for (i, val) in self.0.iter().enumerate() { 591 if i == 0 { 592 write!(f, "{}", val)?; 593 } else { 594 write!(f, "{}{}", self.1, val)?; 595 } 596 } 597 Ok(()) 598 } 599 } 600 601 #[cfg(test)] 602 mod tests { 603 use crate::cursor::{Cursor, CursorPosition, FuncCursor}; 604 use crate::ir::types; 605 use crate::ir::{Function, InstBuilder, StackSlotData, StackSlotKind, UserFuncName}; 606 use alloc::string::ToString; 607 608 #[test] 609 fn basic() { 610 let mut f = Function::new(); 611 assert_eq!(f.to_string(), "function u0:0() fast {\n}\n"); 612 613 f.name = UserFuncName::testcase("foo"); 614 assert_eq!(f.to_string(), "function %foo() fast {\n}\n"); 615 616 f.create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 4)); 617 assert_eq!( 618 f.to_string(), 619 "function %foo() fast {\n ss0 = explicit_slot 4\n}\n" 620 ); 621 622 let block = f.dfg.make_block(); 623 f.layout.append_block(block); 624 assert_eq!( 625 f.to_string(), 626 "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0:\n}\n" 627 ); 628 629 f.dfg.append_block_param(block, types::I8); 630 assert_eq!( 631 f.to_string(), 632 "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8):\n}\n" 633 ); 634 635 f.dfg.append_block_param(block, types::F32.by(4).unwrap()); 636 assert_eq!( 637 f.to_string(), 638 "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8, v1: f32x4):\n}\n" 639 ); 640 641 { 642 let mut cursor = FuncCursor::new(&mut f); 643 cursor.set_position(CursorPosition::After(block)); 644 cursor.ins().return_(&[]) 645 }; 646 assert_eq!( 647 f.to_string(), 648 "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8, v1: f32x4):\n return\n}\n" 649 ); 650 } 651 652 #[test] 653 fn aliases() { 654 use crate::ir::InstBuilder; 655 656 let mut func = Function::new(); 657 { 658 let block0 = func.dfg.make_block(); 659 let mut pos = FuncCursor::new(&mut func); 660 pos.insert_block(block0); 661 662 // make some detached values for change_to_alias 663 let v0 = pos.func.dfg.append_block_param(block0, types::I32); 664 let v1 = pos.func.dfg.append_block_param(block0, types::I32); 665 let v2 = pos.func.dfg.append_block_param(block0, types::I32); 666 pos.func.dfg.detach_block_params(block0); 667 668 // alias to a param--will be printed at beginning of block defining param 669 let v3 = pos.func.dfg.append_block_param(block0, types::I32); 670 pos.func.dfg.change_to_alias(v0, v3); 671 672 // alias to an alias--should print attached to alias, not ultimate target 673 pos.func.dfg.make_value_alias_for_serialization(v0, v2); // v0 <- v2 674 675 // alias to a result--will be printed after instruction producing result 676 let _dummy0 = pos.ins().iconst(types::I32, 42); 677 let v4 = pos.ins().iadd(v0, v0); 678 pos.func.dfg.change_to_alias(v1, v4); 679 let _dummy1 = pos.ins().iconst(types::I32, 23); 680 let _v7 = pos.ins().iadd(v1, v1); 681 } 682 assert_eq!( 683 func.to_string(), 684 "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" 685 ); 686 } 687 688 #[test] 689 fn cold_blocks() { 690 let mut func = Function::new(); 691 { 692 let mut pos = FuncCursor::new(&mut func); 693 694 let block0 = pos.func.dfg.make_block(); 695 pos.insert_block(block0); 696 pos.func.layout.set_cold(block0); 697 698 let block1 = pos.func.dfg.make_block(); 699 pos.insert_block(block1); 700 pos.func.dfg.append_block_param(block1, types::I32); 701 pos.func.layout.set_cold(block1); 702 } 703 704 assert_eq!( 705 func.to_string(), 706 "function u0:0() fast {\nblock0 cold:\n\nblock1(v0: i32) cold:\n}\n" 707 ); 708 } 709 } 710