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