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