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::{Block, DataFlowGraph, Function, Inst, SigRef, Type, Value, ValueDef}; 9 use crate::packed_option::ReservedValue; 10 use alloc::string::String; 11 use alloc::vec::Vec; 12 use core::fmt::{self, Write}; 13 14 /// A `FuncWriter` used to decorate functions during printing. 15 pub trait FuncWriter { 16 /// Write the basic block header for the current function. 17 fn write_block_header( 18 &mut self, 19 w: &mut dyn Write, 20 func: &Function, 21 block: Block, 22 indent: usize, 23 ) -> fmt::Result; 24 25 /// Write the given `inst` to `w`. 26 fn write_instruction( 27 &mut self, 28 w: &mut dyn Write, 29 func: &Function, 30 aliases: &SecondaryMap<Value, Vec<Value>>, 31 inst: Inst, 32 indent: usize, 33 ) -> fmt::Result; 34 35 /// Write the preamble to `w`. By default, this uses `write_entity_definition`. 36 fn write_preamble(&mut self, w: &mut dyn Write, func: &Function) -> Result<bool, fmt::Error> { 37 self.super_preamble(w, func) 38 } 39 40 /// Default impl of `write_preamble` 41 fn super_preamble(&mut self, w: &mut dyn Write, func: &Function) -> Result<bool, fmt::Error> { 42 let mut any = false; 43 44 for (ss, slot) in func.dynamic_stack_slots.iter() { 45 any = true; 46 self.write_entity_definition(w, func, ss.into(), slot)?; 47 } 48 49 for (ss, slot) in func.sized_stack_slots.iter() { 50 any = true; 51 self.write_entity_definition(w, func, ss.into(), slot)?; 52 } 53 54 for (gv, gv_data) in &func.global_values { 55 any = true; 56 self.write_entity_definition(w, func, gv.into(), gv_data)?; 57 } 58 59 for (heap, heap_data) in &func.heaps { 60 if !heap_data.index_type.is_invalid() { 61 any = true; 62 self.write_entity_definition(w, func, heap.into(), heap_data)?; 63 } 64 } 65 66 for (table, table_data) in &func.tables { 67 if !table_data.index_type.is_invalid() { 68 any = true; 69 self.write_entity_definition(w, func, table.into(), table_data)?; 70 } 71 } 72 73 // Write out all signatures before functions since function declarations can refer to 74 // signatures. 75 for (sig, sig_data) in &func.dfg.signatures { 76 any = true; 77 self.write_entity_definition(w, func, sig.into(), &sig_data)?; 78 } 79 80 for (fnref, ext_func) in &func.dfg.ext_funcs { 81 if ext_func.signature != SigRef::reserved_value() { 82 any = true; 83 self.write_entity_definition(w, func, fnref.into(), ext_func)?; 84 } 85 } 86 87 for (jt, jt_data) in &func.jump_tables { 88 any = true; 89 self.write_entity_definition(w, func, jt.into(), jt_data)?; 90 } 91 92 for (&cref, cval) in func.dfg.constants.iter() { 93 any = true; 94 self.write_entity_definition(w, func, cref.into(), cval)?; 95 } 96 97 if let Some(limit) = func.stack_limit { 98 any = true; 99 self.write_entity_definition(w, func, AnyEntity::StackLimit, &limit)?; 100 } 101 102 Ok(any) 103 } 104 105 /// Write an entity definition defined in the preamble to `w`. 106 fn write_entity_definition( 107 &mut self, 108 w: &mut dyn Write, 109 func: &Function, 110 entity: AnyEntity, 111 value: &dyn fmt::Display, 112 ) -> fmt::Result { 113 self.super_entity_definition(w, func, entity, value) 114 } 115 116 /// Default impl of `write_entity_definition` 117 #[allow(unused_variables)] 118 fn super_entity_definition( 119 &mut self, 120 w: &mut dyn Write, 121 func: &Function, 122 entity: AnyEntity, 123 value: &dyn fmt::Display, 124 ) -> fmt::Result { 125 writeln!(w, " {} = {}", entity, value) 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_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 fn write_spec(w: &mut dyn Write, func: &Function) -> fmt::Result { 201 write!(w, "{}{}", func.name, func.signature) 202 } 203 204 //---------------------------------------------------------------------- 205 // 206 // Basic blocks 207 208 fn write_arg(w: &mut dyn Write, func: &Function, arg: Value) -> fmt::Result { 209 write!(w, "{}: {}", arg, func.dfg.value_type(arg)) 210 } 211 212 /// Write out the basic block header, outdented: 213 /// 214 /// block1: 215 /// block1(v1: i32): 216 /// block10(v4: f64, v5: b1): 217 /// 218 pub fn write_block_header( 219 w: &mut dyn Write, 220 func: &Function, 221 block: Block, 222 indent: usize, 223 ) -> fmt::Result { 224 let cold = if func.layout.is_cold(block) { 225 " cold" 226 } else { 227 "" 228 }; 229 230 // The `indent` is the instruction indentation. block headers are 4 spaces out from that. 231 write!(w, "{1:0$}{2}", indent - 4, "", block)?; 232 233 let mut args = func.dfg.block_params(block).iter().cloned(); 234 match args.next() { 235 None => return writeln!(w, "{}:", cold), 236 Some(arg) => { 237 write!(w, "(")?; 238 write_arg(w, func, arg)?; 239 } 240 } 241 // Remaining arguments. 242 for arg in args { 243 write!(w, ", ")?; 244 write_arg(w, func, arg)?; 245 } 246 writeln!(w, "){}:", cold) 247 } 248 249 fn decorate_block<FW: FuncWriter>( 250 func_w: &mut FW, 251 w: &mut dyn Write, 252 func: &Function, 253 aliases: &SecondaryMap<Value, Vec<Value>>, 254 block: Block, 255 ) -> fmt::Result { 256 // Indent all instructions if any srclocs are present. 257 let indent = if func.srclocs.is_empty() { 4 } else { 36 }; 258 259 func_w.write_block_header(w, func, block, indent)?; 260 for a in func.dfg.block_params(block).iter().cloned() { 261 write_value_aliases(w, aliases, a, indent)?; 262 } 263 264 for inst in func.layout.block_insts(block) { 265 func_w.write_instruction(w, func, aliases, inst, indent)?; 266 } 267 268 Ok(()) 269 } 270 271 //---------------------------------------------------------------------- 272 // 273 // Instructions 274 275 // Should `inst` be printed with a type suffix? 276 // 277 // Polymorphic instructions may need a suffix indicating the value of the controlling type variable 278 // if it can't be trivially inferred. 279 // 280 fn type_suffix(func: &Function, inst: Inst) -> Option<Type> { 281 let inst_data = &func.dfg[inst]; 282 let constraints = inst_data.opcode().constraints(); 283 284 if !constraints.is_polymorphic() { 285 return None; 286 } 287 288 // If the controlling type variable can be inferred from the type of the designated value input 289 // operand, we don't need the type suffix. 290 if constraints.use_typevar_operand() { 291 let ctrl_var = inst_data.typevar_operand(&func.dfg.value_lists).unwrap(); 292 let def_block = match func.dfg.value_def(ctrl_var) { 293 ValueDef::Result(instr, _) => func.layout.inst_block(instr), 294 ValueDef::Param(block, _) => Some(block), 295 }; 296 if def_block.is_some() && def_block == func.layout.inst_block(inst) { 297 return None; 298 } 299 } 300 301 let rtype = func.dfg.ctrl_typevar(inst); 302 assert!( 303 !rtype.is_invalid(), 304 "Polymorphic instruction must produce a result" 305 ); 306 Some(rtype) 307 } 308 309 /// Write out any aliases to the given target, including indirect aliases 310 fn write_value_aliases( 311 w: &mut dyn Write, 312 aliases: &SecondaryMap<Value, Vec<Value>>, 313 target: Value, 314 indent: usize, 315 ) -> fmt::Result { 316 let mut todo_stack = vec![target]; 317 while let Some(target) = todo_stack.pop() { 318 for &a in &aliases[target] { 319 writeln!(w, "{1:0$}{2} -> {3}", indent, "", a, target)?; 320 todo_stack.push(a); 321 } 322 } 323 324 Ok(()) 325 } 326 327 fn write_instruction( 328 w: &mut dyn Write, 329 func: &Function, 330 aliases: &SecondaryMap<Value, Vec<Value>>, 331 inst: Inst, 332 indent: usize, 333 ) -> fmt::Result { 334 // Prefix containing source location, encoding, and value locations. 335 let mut s = String::with_capacity(16); 336 337 // Source location goes first. 338 let srcloc = func.srclocs[inst]; 339 if !srcloc.is_default() { 340 write!(s, "{} ", srcloc)?; 341 } 342 343 // Write out prefix and indent the instruction. 344 write!(w, "{1:0$}", indent, s)?; 345 346 // Write out the result values, if any. 347 let mut has_results = false; 348 for r in func.dfg.inst_results(inst) { 349 if !has_results { 350 has_results = true; 351 write!(w, "{}", r)?; 352 } else { 353 write!(w, ", {}", r)?; 354 } 355 } 356 if has_results { 357 write!(w, " = ")?; 358 } 359 360 // Then the opcode, possibly with a '.type' suffix. 361 let opcode = func.dfg[inst].opcode(); 362 363 match type_suffix(func, inst) { 364 Some(suf) => write!(w, "{}.{}", opcode, suf)?, 365 None => write!(w, "{}", opcode)?, 366 } 367 368 write_operands(w, &func.dfg, inst)?; 369 writeln!(w)?; 370 371 // Value aliases come out on lines after the instruction defining the referent. 372 for r in func.dfg.inst_results(inst) { 373 write_value_aliases(w, aliases, *r, indent)?; 374 } 375 Ok(()) 376 } 377 378 /// Write the operands of `inst` to `w` with a prepended space. 379 pub fn write_operands(w: &mut dyn Write, dfg: &DataFlowGraph, inst: Inst) -> fmt::Result { 380 let pool = &dfg.value_lists; 381 use crate::ir::instructions::InstructionData::*; 382 match dfg[inst] { 383 AtomicRmw { op, args, .. } => write!(w, " {} {}, {}", op, args[0], args[1]), 384 AtomicCas { args, .. } => write!(w, " {}, {}, {}", args[0], args[1], args[2]), 385 LoadNoOffset { flags, arg, .. } => write!(w, "{} {}", flags, arg), 386 StoreNoOffset { flags, args, .. } => write!(w, "{} {}, {}", flags, args[0], args[1]), 387 Unary { arg, .. } => write!(w, " {}", arg), 388 UnaryImm { imm, .. } => write!(w, " {}", imm), 389 UnaryIeee32 { imm, .. } => write!(w, " {}", imm), 390 UnaryIeee64 { imm, .. } => write!(w, " {}", imm), 391 UnaryBool { imm, .. } => write!(w, " {}", imm), 392 UnaryGlobalValue { global_value, .. } => write!(w, " {}", global_value), 393 UnaryConst { 394 constant_handle, .. 395 } => write!(w, " {}", constant_handle), 396 Binary { args, .. } => write!(w, " {}, {}", args[0], args[1]), 397 BinaryImm8 { arg, imm, .. } => write!(w, " {}, {}", arg, imm), 398 BinaryImm64 { arg, imm, .. } => write!(w, " {}, {}", arg, imm), 399 Ternary { args, .. } => write!(w, " {}, {}, {}", args[0], args[1], args[2]), 400 MultiAry { ref args, .. } => { 401 if args.is_empty() { 402 write!(w, "") 403 } else { 404 write!(w, " {}", DisplayValues(args.as_slice(pool))) 405 } 406 } 407 NullAry { .. } => write!(w, " "), 408 TernaryImm8 { imm, args, .. } => write!(w, " {}, {}, {}", args[0], args[1], imm), 409 Shuffle { imm, args, .. } => { 410 let data = dfg.immediates.get(imm).expect( 411 "Expected the shuffle mask to already be inserted into the immediates table", 412 ); 413 write!(w, " {}, {}, {}", args[0], args[1], data) 414 } 415 IntCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]), 416 IntCompareImm { cond, arg, imm, .. } => write!(w, " {} {}, {}", cond, arg, imm), 417 IntCond { cond, arg, .. } => write!(w, " {} {}", cond, arg), 418 FloatCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]), 419 FloatCond { cond, arg, .. } => write!(w, " {} {}", cond, arg), 420 IntSelect { cond, args, .. } => { 421 write!(w, " {} {}, {}, {}", cond, args[0], args[1], args[2]) 422 } 423 Jump { 424 destination, 425 ref args, 426 .. 427 } => { 428 write!(w, " {}", destination)?; 429 write_block_args(w, args.as_slice(pool)) 430 } 431 Branch { 432 destination, 433 ref args, 434 .. 435 } => { 436 let args = args.as_slice(pool); 437 write!(w, " {}, {}", args[0], destination)?; 438 write_block_args(w, &args[1..]) 439 } 440 BranchInt { 441 cond, 442 destination, 443 ref args, 444 .. 445 } => { 446 let args = args.as_slice(pool); 447 write!(w, " {} {}, {}", cond, args[0], destination)?; 448 write_block_args(w, &args[1..]) 449 } 450 BranchFloat { 451 cond, 452 destination, 453 ref args, 454 .. 455 } => { 456 let args = args.as_slice(pool); 457 write!(w, " {} {}, {}", cond, args[0], destination)?; 458 write_block_args(w, &args[1..]) 459 } 460 BranchIcmp { 461 cond, 462 destination, 463 ref args, 464 .. 465 } => { 466 let args = args.as_slice(pool); 467 write!(w, " {} {}, {}, {}", cond, args[0], args[1], destination)?; 468 write_block_args(w, &args[2..]) 469 } 470 BranchTable { 471 arg, 472 destination, 473 table, 474 .. 475 } => write!(w, " {}, {}, {}", arg, destination, table), 476 Call { 477 func_ref, ref args, .. 478 } => write!(w, " {}({})", func_ref, DisplayValues(args.as_slice(pool))), 479 CallIndirect { 480 sig_ref, ref args, .. 481 } => { 482 let args = args.as_slice(pool); 483 write!( 484 w, 485 " {}, {}({})", 486 sig_ref, 487 args[0], 488 DisplayValues(&args[1..]) 489 ) 490 } 491 FuncAddr { func_ref, .. } => write!(w, " {}", func_ref), 492 StackLoad { 493 stack_slot, offset, .. 494 } => write!(w, " {}{}", stack_slot, offset), 495 StackStore { 496 arg, 497 stack_slot, 498 offset, 499 .. 500 } => write!(w, " {}, {}{}", arg, stack_slot, offset), 501 DynamicStackLoad { 502 dynamic_stack_slot, .. 503 } => write!(w, " {}", dynamic_stack_slot), 504 DynamicStackStore { 505 arg, 506 dynamic_stack_slot, 507 .. 508 } => write!(w, " {}, {}", arg, dynamic_stack_slot), 509 HeapAddr { heap, arg, imm, .. } => write!(w, " {}, {}, {}", heap, arg, imm), 510 TableAddr { table, arg, .. } => write!(w, " {}, {}", table, arg), 511 Load { 512 flags, arg, offset, .. 513 } => write!(w, "{} {}{}", flags, arg, offset), 514 Store { 515 flags, 516 args, 517 offset, 518 .. 519 } => write!(w, "{} {}, {}{}", flags, args[0], args[1], offset), 520 Trap { code, .. } => write!(w, " {}", code), 521 CondTrap { arg, code, .. } => write!(w, " {}, {}", arg, code), 522 IntCondTrap { 523 cond, arg, code, .. 524 } => write!(w, " {} {}, {}", cond, arg, code), 525 FloatCondTrap { 526 cond, arg, code, .. 527 } => write!(w, " {} {}, {}", cond, arg, code), 528 } 529 } 530 531 /// Write block args using optional parantheses. 532 fn write_block_args(w: &mut dyn Write, args: &[Value]) -> fmt::Result { 533 if args.is_empty() { 534 Ok(()) 535 } else { 536 write!(w, "({})", DisplayValues(args)) 537 } 538 } 539 540 /// Displayable slice of values. 541 struct DisplayValues<'a>(&'a [Value]); 542 543 impl<'a> fmt::Display for DisplayValues<'a> { 544 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 545 for (i, val) in self.0.iter().enumerate() { 546 if i == 0 { 547 write!(f, "{}", val)?; 548 } else { 549 write!(f, ", {}", val)?; 550 } 551 } 552 Ok(()) 553 } 554 } 555 556 struct DisplayValuesWithDelimiter<'a>(&'a [Value], char); 557 558 impl<'a> fmt::Display for DisplayValuesWithDelimiter<'a> { 559 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 560 for (i, val) in self.0.iter().enumerate() { 561 if i == 0 { 562 write!(f, "{}", val)?; 563 } else { 564 write!(f, "{}{}", self.1, val)?; 565 } 566 } 567 Ok(()) 568 } 569 } 570 571 #[cfg(test)] 572 mod tests { 573 use crate::cursor::{Cursor, CursorPosition, FuncCursor}; 574 use crate::ir::types; 575 use crate::ir::{ExternalName, Function, InstBuilder, StackSlotData, StackSlotKind}; 576 use alloc::string::ToString; 577 578 #[test] 579 fn basic() { 580 let mut f = Function::new(); 581 assert_eq!(f.to_string(), "function u0:0() fast {\n}\n"); 582 583 f.name = ExternalName::testcase("foo"); 584 assert_eq!(f.to_string(), "function %foo() fast {\n}\n"); 585 586 f.create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 4)); 587 assert_eq!( 588 f.to_string(), 589 "function %foo() fast {\n ss0 = explicit_slot 4\n}\n" 590 ); 591 592 let block = f.dfg.make_block(); 593 f.layout.append_block(block); 594 assert_eq!( 595 f.to_string(), 596 "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0:\n}\n" 597 ); 598 599 f.dfg.append_block_param(block, types::I8); 600 assert_eq!( 601 f.to_string(), 602 "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8):\n}\n" 603 ); 604 605 f.dfg.append_block_param(block, types::F32.by(4).unwrap()); 606 assert_eq!( 607 f.to_string(), 608 "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8, v1: f32x4):\n}\n" 609 ); 610 611 { 612 let mut cursor = FuncCursor::new(&mut f); 613 cursor.set_position(CursorPosition::After(block)); 614 cursor.ins().return_(&[]) 615 }; 616 assert_eq!( 617 f.to_string(), 618 "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8, v1: f32x4):\n return\n}\n" 619 ); 620 } 621 622 #[test] 623 fn aliases() { 624 use crate::ir::InstBuilder; 625 626 let mut func = Function::new(); 627 { 628 let block0 = func.dfg.make_block(); 629 let mut pos = FuncCursor::new(&mut func); 630 pos.insert_block(block0); 631 632 // make some detached values for change_to_alias 633 let v0 = pos.func.dfg.append_block_param(block0, types::I32); 634 let v1 = pos.func.dfg.append_block_param(block0, types::I32); 635 let v2 = pos.func.dfg.append_block_param(block0, types::I32); 636 pos.func.dfg.detach_block_params(block0); 637 638 // alias to a param--will be printed at beginning of block defining param 639 let v3 = pos.func.dfg.append_block_param(block0, types::I32); 640 pos.func.dfg.change_to_alias(v0, v3); 641 642 // alias to an alias--should print attached to alias, not ultimate target 643 pos.func.dfg.make_value_alias_for_serialization(v0, v2); // v0 <- v2 644 645 // alias to a result--will be printed after instruction producing result 646 let _dummy0 = pos.ins().iconst(types::I32, 42); 647 let v4 = pos.ins().iadd(v0, v0); 648 pos.func.dfg.change_to_alias(v1, v4); 649 let _dummy1 = pos.ins().iconst(types::I32, 23); 650 let _v7 = pos.ins().iadd(v1, v1); 651 } 652 assert_eq!( 653 func.to_string(), 654 "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" 655 ); 656 } 657 658 #[test] 659 fn cold_blocks() { 660 let mut func = Function::new(); 661 { 662 let mut pos = FuncCursor::new(&mut func); 663 664 let block0 = pos.func.dfg.make_block(); 665 pos.insert_block(block0); 666 pos.func.layout.set_cold(block0); 667 668 let block1 = pos.func.dfg.make_block(); 669 pos.insert_block(block1); 670 pos.func.dfg.append_block_param(block1, types::I32); 671 pos.func.layout.set_cold(block1); 672 } 673 674 assert_eq!( 675 func.to_string(), 676 "function u0:0() fast {\nblock0 cold:\n\nblock1(v0: i32) cold:\n}\n" 677 ); 678 } 679 } 680