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::Ieee128; 9 use crate::ir::pcc::Fact; 10 use crate::ir::{Block, DataFlowGraph, Function, Inst, Opcode, SigRef, Type, Value, ValueDef}; 11 use crate::packed_option::ReservedValue; 12 use alloc::string::{String, ToString}; 13 use alloc::vec::Vec; 14 use core::fmt::{self, Write}; 15 16 /// A `FuncWriter` used to decorate functions during printing. 17 pub trait FuncWriter { 18 /// Write the basic block header for the current function. 19 fn write_block_header( 20 &mut self, 21 w: &mut dyn Write, 22 func: &Function, 23 block: Block, 24 indent: usize, 25 ) -> fmt::Result; 26 27 /// Write the given `inst` to `w`. 28 fn write_instruction( 29 &mut self, 30 w: &mut dyn Write, 31 func: &Function, 32 aliases: &SecondaryMap<Value, Vec<Value>>, 33 inst: Inst, 34 indent: usize, 35 ) -> fmt::Result; 36 37 /// Write the preamble to `w`. By default, this uses `write_entity_definition`. 38 fn write_preamble(&mut self, w: &mut dyn Write, func: &Function) -> Result<bool, fmt::Error> { 39 self.super_preamble(w, func) 40 } 41 42 /// Default impl of `write_preamble` 43 fn super_preamble(&mut self, w: &mut dyn Write, func: &Function) -> Result<bool, fmt::Error> { 44 let mut any = false; 45 46 for (ss, slot) in func.dynamic_stack_slots.iter() { 47 any = true; 48 self.write_entity_definition(w, func, ss.into(), slot, None)?; 49 } 50 51 for (ss, slot) in func.sized_stack_slots.iter() { 52 any = true; 53 self.write_entity_definition(w, func, ss.into(), slot, None)?; 54 } 55 56 for (gv, gv_data) in &func.global_values { 57 any = true; 58 let maybe_fact = func.global_value_facts[gv].as_ref(); 59 self.write_entity_definition(w, func, gv.into(), gv_data, maybe_fact)?; 60 } 61 62 for (mt, mt_data) in &func.memory_types { 63 any = true; 64 self.write_entity_definition(w, func, mt.into(), mt_data, None)?; 65 } 66 67 // Write out all signatures before functions since function declarations can refer to 68 // signatures. 69 for (sig, sig_data) in &func.dfg.signatures { 70 any = true; 71 self.write_entity_definition(w, func, sig.into(), &sig_data, None)?; 72 } 73 74 for (fnref, ext_func) in &func.dfg.ext_funcs { 75 if ext_func.signature != SigRef::reserved_value() { 76 any = true; 77 self.write_entity_definition( 78 w, 79 func, 80 fnref.into(), 81 &ext_func.display(Some(&func.params)), 82 None, 83 )?; 84 } 85 } 86 87 for (&cref, cval) in func.dfg.constants.iter() { 88 any = true; 89 self.write_entity_definition(w, func, cref.into(), cval, None)?; 90 } 91 92 if let Some(limit) = func.stack_limit { 93 any = true; 94 self.write_entity_definition(w, func, AnyEntity::StackLimit, &limit, None)?; 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 maybe_fact: Option<&Fact>, 108 ) -> fmt::Result { 109 self.super_entity_definition(w, func, entity, value, maybe_fact) 110 } 111 112 /// Default impl of `write_entity_definition` 113 #[allow(unused_variables)] 114 fn super_entity_definition( 115 &mut self, 116 w: &mut dyn Write, 117 func: &Function, 118 entity: AnyEntity, 119 value: &dyn fmt::Display, 120 maybe_fact: Option<&Fact>, 121 ) -> fmt::Result { 122 if let Some(fact) = maybe_fact { 123 writeln!(w, " {} ! {} = {}", entity, fact, value) 124 } else { 125 writeln!(w, " {} = {}", entity, value) 126 } 127 } 128 } 129 130 /// A `PlainWriter` that doesn't decorate the function. 131 pub struct PlainWriter; 132 133 impl FuncWriter for PlainWriter { 134 fn write_instruction( 135 &mut self, 136 w: &mut dyn Write, 137 func: &Function, 138 aliases: &SecondaryMap<Value, Vec<Value>>, 139 inst: Inst, 140 indent: usize, 141 ) -> fmt::Result { 142 write_instruction(w, func, aliases, inst, indent) 143 } 144 145 fn write_block_header( 146 &mut self, 147 w: &mut dyn Write, 148 func: &Function, 149 block: Block, 150 indent: usize, 151 ) -> fmt::Result { 152 write_block_header(w, func, block, indent) 153 } 154 } 155 156 /// Write `func` to `w` as equivalent text. 157 /// Use `isa` to emit ISA-dependent annotations. 158 pub fn write_function(w: &mut dyn Write, func: &Function) -> fmt::Result { 159 decorate_function(&mut PlainWriter, w, func) 160 } 161 162 /// Create a reverse-alias map from a value to all aliases having that value as a direct target 163 fn alias_map(func: &Function) -> SecondaryMap<Value, Vec<Value>> { 164 let mut aliases = SecondaryMap::<_, Vec<_>>::new(); 165 for v in func.dfg.values() { 166 // VADFS returns the immediate target of an alias 167 if let Some(k) = func.dfg.value_alias_dest_for_serialization(v) { 168 aliases[k].push(v); 169 } 170 } 171 aliases 172 } 173 174 /// Writes `func` to `w` as text. 175 /// write_function_plain is passed as 'closure' to print instructions as text. 176 /// pretty_function_error is passed as 'closure' to add error decoration. 177 pub fn decorate_function<FW: FuncWriter>( 178 func_w: &mut FW, 179 w: &mut dyn Write, 180 func: &Function, 181 ) -> fmt::Result { 182 write!(w, "function ")?; 183 write_spec(w, func)?; 184 writeln!(w, " {{")?; 185 let aliases = alias_map(func); 186 let mut any = func_w.write_preamble(w, func)?; 187 for block in &func.layout { 188 if any { 189 writeln!(w)?; 190 } 191 decorate_block(func_w, w, func, &aliases, block)?; 192 any = true; 193 } 194 writeln!(w, "}}") 195 } 196 197 //---------------------------------------------------------------------- 198 // 199 // Function spec. 200 201 fn write_spec(w: &mut dyn Write, func: &Function) -> fmt::Result { 202 write!(w, "{}{}", func.name, func.signature) 203 } 204 205 //---------------------------------------------------------------------- 206 // 207 // Basic blocks 208 209 fn write_arg(w: &mut dyn Write, func: &Function, arg: Value) -> fmt::Result { 210 let ty = func.dfg.value_type(arg); 211 if let Some(f) = &func.dfg.facts[arg] { 212 write!(w, "{} ! {}: {}", arg, f, ty) 213 } else { 214 write!(w, "{}: {}", arg, ty) 215 } 216 } 217 218 /// Write out the basic block header, outdented: 219 /// 220 /// block1: 221 /// block1(v1: i32): 222 /// block10(v4: f64, v5: i8): 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.insts[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 ValueDef::Union(..) => None, 302 }; 303 if def_block.is_some() && def_block == func.layout.inst_block(inst) { 304 return None; 305 } 306 } 307 308 let rtype = func.dfg.ctrl_typevar(inst); 309 assert!( 310 !rtype.is_invalid(), 311 "Polymorphic instruction must produce a result" 312 ); 313 Some(rtype) 314 } 315 316 /// Write out any aliases to the given target, including indirect aliases 317 fn write_value_aliases( 318 w: &mut dyn Write, 319 aliases: &SecondaryMap<Value, Vec<Value>>, 320 target: Value, 321 indent: usize, 322 ) -> fmt::Result { 323 let mut todo_stack = vec![target]; 324 while let Some(target) = todo_stack.pop() { 325 for &a in &aliases[target] { 326 writeln!(w, "{1:0$}{2} -> {3}", indent, "", a, target)?; 327 todo_stack.push(a); 328 } 329 } 330 331 Ok(()) 332 } 333 334 fn write_instruction( 335 w: &mut dyn Write, 336 func: &Function, 337 aliases: &SecondaryMap<Value, Vec<Value>>, 338 inst: Inst, 339 indent: usize, 340 ) -> fmt::Result { 341 // Prefix containing source location, encoding, and value locations. 342 let mut s = String::with_capacity(16); 343 344 // Source location goes first. 345 let srcloc = func.srcloc(inst); 346 if !srcloc.is_default() { 347 write!(s, "{} ", srcloc)?; 348 } 349 350 // Write out prefix and indent the instruction. 351 write!(w, "{1:0$}", indent, s)?; 352 353 // Write out the result values, if any. 354 let mut has_results = false; 355 for r in func.dfg.inst_results(inst) { 356 if !has_results { 357 has_results = true; 358 write!(w, "{}", r)?; 359 } else { 360 write!(w, ", {}", r)?; 361 } 362 if let Some(f) = &func.dfg.facts[*r] { 363 write!(w, " ! {}", f)?; 364 } 365 } 366 if has_results { 367 write!(w, " = ")?; 368 } 369 370 // Then the opcode, possibly with a '.type' suffix. 371 let opcode = func.dfg.insts[inst].opcode(); 372 373 match type_suffix(func, inst) { 374 Some(suf) => write!(w, "{}.{}", opcode, suf)?, 375 None => write!(w, "{}", opcode)?, 376 } 377 378 write_operands(w, &func.dfg, inst)?; 379 writeln!(w)?; 380 381 // Value aliases come out on lines after the instruction defining the referent. 382 for r in func.dfg.inst_results(inst) { 383 write_value_aliases(w, aliases, *r, indent)?; 384 } 385 Ok(()) 386 } 387 388 /// Write the operands of `inst` to `w` with a prepended space. 389 pub fn write_operands(w: &mut dyn Write, dfg: &DataFlowGraph, inst: Inst) -> fmt::Result { 390 let pool = &dfg.value_lists; 391 let jump_tables = &dfg.jump_tables; 392 use crate::ir::instructions::InstructionData::*; 393 match dfg.insts[inst] { 394 AtomicRmw { op, args, .. } => write!(w, " {} {}, {}", op, args[0], args[1]), 395 AtomicCas { args, .. } => write!(w, " {}, {}, {}", args[0], args[1], args[2]), 396 LoadNoOffset { flags, arg, .. } => write!(w, "{} {}", flags, arg), 397 StoreNoOffset { flags, args, .. } => write!(w, "{} {}, {}", flags, args[0], args[1]), 398 Unary { arg, .. } => write!(w, " {}", arg), 399 UnaryImm { imm, .. } => write!(w, " {}", imm), 400 UnaryIeee16 { imm, .. } => write!(w, " {}", imm), 401 UnaryIeee32 { imm, .. } => write!(w, " {}", imm), 402 UnaryIeee64 { imm, .. } => write!(w, " {}", imm), 403 UnaryGlobalValue { global_value, .. } => write!(w, " {}", global_value), 404 UnaryConst { 405 constant_handle, .. 406 } => write!(w, " {}", constant_handle), 407 Binary { args, .. } => write!(w, " {}, {}", args[0], args[1]), 408 BinaryImm8 { arg, imm, .. } => write!(w, " {}, {}", arg, imm), 409 BinaryImm64 { arg, imm, .. } => write!(w, " {}, {}", arg, imm), 410 Ternary { args, .. } => write!(w, " {}, {}, {}", args[0], args[1], args[2]), 411 MultiAry { ref args, .. } => { 412 if args.is_empty() { 413 write!(w, "") 414 } else { 415 write!(w, " {}", DisplayValues(args.as_slice(pool))) 416 } 417 } 418 NullAry { .. } => write!(w, " "), 419 TernaryImm8 { imm, args, .. } => write!(w, " {}, {}, {}", args[0], args[1], imm), 420 Shuffle { imm, args, .. } => { 421 let data = dfg.immediates.get(imm).expect( 422 "Expected the shuffle mask to already be inserted into the immediates table", 423 ); 424 write!(w, " {}, {}, {}", args[0], args[1], data) 425 } 426 IntCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]), 427 IntCompareImm { cond, arg, imm, .. } => write!(w, " {} {}, {}", cond, arg, imm), 428 IntAddTrap { args, code, .. } => write!(w, " {}, {}, {}", args[0], args[1], code), 429 FloatCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, args[0], args[1]), 430 Jump { destination, .. } => { 431 write!(w, " {}", destination.display(pool)) 432 } 433 Brif { 434 arg, 435 blocks: [block_then, block_else], 436 .. 437 } => { 438 write!(w, " {}, {}", arg, block_then.display(pool))?; 439 write!(w, ", {}", block_else.display(pool)) 440 } 441 BranchTable { arg, table, .. } => { 442 write!(w, " {}, {}", arg, jump_tables[table].display(pool)) 443 } 444 Call { 445 func_ref, ref args, .. 446 } => { 447 write!(w, " {}({})", func_ref, DisplayValues(args.as_slice(pool)))?; 448 write_user_stack_map_entries(w, dfg, inst) 449 } 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 write_user_stack_map_entries(w, dfg, inst) 462 } 463 FuncAddr { func_ref, .. } => write!(w, " {}", func_ref), 464 StackLoad { 465 stack_slot, offset, .. 466 } => write!(w, " {}{}", stack_slot, offset), 467 StackStore { 468 arg, 469 stack_slot, 470 offset, 471 .. 472 } => write!(w, " {}, {}{}", arg, stack_slot, offset), 473 DynamicStackLoad { 474 dynamic_stack_slot, .. 475 } => write!(w, " {}", dynamic_stack_slot), 476 DynamicStackStore { 477 arg, 478 dynamic_stack_slot, 479 .. 480 } => write!(w, " {}, {}", arg, dynamic_stack_slot), 481 Load { 482 flags, arg, offset, .. 483 } => write!(w, "{} {}{}", flags, arg, offset), 484 Store { 485 flags, 486 args, 487 offset, 488 .. 489 } => write!(w, "{} {}, {}{}", flags, args[0], args[1], offset), 490 Trap { code, .. } => write!(w, " {}", code), 491 CondTrap { arg, code, .. } => write!(w, " {}, {}", arg, code), 492 }?; 493 494 let mut sep = " ; "; 495 for arg in dfg.inst_values(inst) { 496 if let ValueDef::Result(src, _) = dfg.value_def(arg) { 497 let imm = match dfg.insts[src] { 498 UnaryImm { imm, .. } => imm.to_string(), 499 UnaryIeee16 { imm, .. } => imm.to_string(), 500 UnaryIeee32 { imm, .. } => imm.to_string(), 501 UnaryIeee64 { imm, .. } => imm.to_string(), 502 UnaryConst { 503 constant_handle, 504 opcode: Opcode::F128const, 505 } => Ieee128::try_from(dfg.constants.get(constant_handle)) 506 .expect("16-byte f128 constant") 507 .to_string(), 508 UnaryConst { 509 constant_handle, .. 510 } => constant_handle.to_string(), 511 _ => continue, 512 }; 513 write!(w, "{}{} = {}", sep, arg, imm)?; 514 sep = ", "; 515 } 516 } 517 Ok(()) 518 } 519 520 fn write_user_stack_map_entries(w: &mut dyn Write, dfg: &DataFlowGraph, inst: Inst) -> fmt::Result { 521 let entries = match dfg.user_stack_map_entries(inst) { 522 None => return Ok(()), 523 Some(es) => es, 524 }; 525 write!(w, ", stack_map=[")?; 526 let mut need_comma = false; 527 for entry in entries { 528 if need_comma { 529 write!(w, ", ")?; 530 } 531 write!(w, "{} @ {}+{}", entry.ty, entry.slot, entry.offset)?; 532 need_comma = true; 533 } 534 write!(w, "]")?; 535 Ok(()) 536 } 537 538 /// Displayable slice of values. 539 struct DisplayValues<'a>(&'a [Value]); 540 541 impl<'a> fmt::Display for DisplayValues<'a> { 542 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 543 for (i, val) in self.0.iter().enumerate() { 544 if i == 0 { 545 write!(f, "{}", val)?; 546 } else { 547 write!(f, ", {}", val)?; 548 } 549 } 550 Ok(()) 551 } 552 } 553 554 #[cfg(test)] 555 mod tests { 556 use crate::cursor::{Cursor, CursorPosition, FuncCursor}; 557 use crate::ir::types; 558 use crate::ir::{Function, InstBuilder, StackSlotData, StackSlotKind, UserFuncName}; 559 use alloc::string::ToString; 560 561 #[test] 562 fn basic() { 563 let mut f = Function::new(); 564 assert_eq!(f.to_string(), "function u0:0() fast {\n}\n"); 565 566 f.name = UserFuncName::testcase("foo"); 567 assert_eq!(f.to_string(), "function %foo() fast {\n}\n"); 568 569 f.create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 4, 0)); 570 assert_eq!( 571 f.to_string(), 572 "function %foo() fast {\n ss0 = explicit_slot 4\n}\n" 573 ); 574 575 let block = f.dfg.make_block(); 576 f.layout.append_block(block); 577 assert_eq!( 578 f.to_string(), 579 "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0:\n}\n" 580 ); 581 582 f.dfg.append_block_param(block, types::I8); 583 assert_eq!( 584 f.to_string(), 585 "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8):\n}\n" 586 ); 587 588 f.dfg.append_block_param(block, types::F32.by(4).unwrap()); 589 assert_eq!( 590 f.to_string(), 591 "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8, v1: f32x4):\n}\n" 592 ); 593 594 { 595 let mut cursor = FuncCursor::new(&mut f); 596 cursor.set_position(CursorPosition::After(block)); 597 cursor.ins().return_(&[]) 598 }; 599 assert_eq!( 600 f.to_string(), 601 "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8, v1: f32x4):\n return\n}\n" 602 ); 603 604 let mut f = Function::new(); 605 f.create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 4, 2)); 606 assert_eq!( 607 f.to_string(), 608 "function u0:0() fast {\n ss0 = explicit_slot 4, align = 4\n}\n" 609 ); 610 } 611 612 #[test] 613 fn aliases() { 614 use crate::ir::InstBuilder; 615 616 let mut func = Function::new(); 617 { 618 let block0 = func.dfg.make_block(); 619 let mut pos = FuncCursor::new(&mut func); 620 pos.insert_block(block0); 621 622 // make some detached values for change_to_alias 623 let v0 = pos.func.dfg.append_block_param(block0, types::I32); 624 let v1 = pos.func.dfg.append_block_param(block0, types::I32); 625 let v2 = pos.func.dfg.append_block_param(block0, types::I32); 626 pos.func.dfg.detach_block_params(block0); 627 628 // alias to a param--will be printed at beginning of block defining param 629 let v3 = pos.func.dfg.append_block_param(block0, types::I32); 630 pos.func.dfg.change_to_alias(v0, v3); 631 632 // alias to an alias--should print attached to alias, not ultimate target 633 pos.func.dfg.make_value_alias_for_serialization(v0, v2); // v0 <- v2 634 635 // alias to a result--will be printed after instruction producing result 636 let _dummy0 = pos.ins().iconst(types::I32, 42); 637 let v4 = pos.ins().iadd(v0, v0); 638 pos.func.dfg.change_to_alias(v1, v4); 639 let _dummy1 = pos.ins().iconst(types::I32, 23); 640 let _v7 = pos.ins().iadd(v1, v1); 641 } 642 assert_eq!( 643 func.to_string(), 644 "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" 645 ); 646 } 647 648 #[test] 649 fn cold_blocks() { 650 let mut func = Function::new(); 651 { 652 let mut pos = FuncCursor::new(&mut func); 653 654 let block0 = pos.func.dfg.make_block(); 655 pos.insert_block(block0); 656 pos.func.layout.set_cold(block0); 657 658 let block1 = pos.func.dfg.make_block(); 659 pos.insert_block(block1); 660 pos.func.dfg.append_block_param(block1, types::I32); 661 pos.func.layout.set_cold(block1); 662 } 663 664 assert_eq!( 665 func.to_string(), 666 "function u0:0() fast {\nblock0 cold:\n\nblock1(v0: i32) cold:\n}\n" 667 ); 668 } 669 } 670