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