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 // 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, None)?; 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 None, 82 )?; 83 } 84 } 85 86 for (&cref, cval) in func.dfg.constants.iter() { 87 any = true; 88 self.write_entity_definition(w, func, cref.into(), cval, None)?; 89 } 90 91 if let Some(limit) = func.stack_limit { 92 any = true; 93 self.write_entity_definition(w, func, AnyEntity::StackLimit, &limit, None)?; 94 } 95 96 Ok(any) 97 } 98 99 /// Write an entity definition defined in the preamble to `w`. 100 fn write_entity_definition( 101 &mut self, 102 w: &mut dyn Write, 103 func: &Function, 104 entity: AnyEntity, 105 value: &dyn fmt::Display, 106 maybe_fact: Option<&Fact>, 107 ) -> fmt::Result { 108 self.super_entity_definition(w, func, entity, value, maybe_fact) 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 maybe_fact: Option<&Fact>, 120 ) -> fmt::Result { 121 if let Some(fact) = maybe_fact { 122 writeln!(w, " {} ! {} = {}", entity, fact, value) 123 } else { 124 writeln!(w, " {} = {}", entity, value) 125 } 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 let ty = func.dfg.value_type(arg); 210 if let Some(f) = &func.dfg.facts[arg] { 211 write!(w, "{} ! {}: {}", arg, f, ty) 212 } else { 213 write!(w, "{}: {}", arg, ty) 214 } 215 } 216 217 /// Write out the basic block header, outdented: 218 /// 219 /// block1: 220 /// block1(v1: i32): 221 /// block10(v4: f64, v5: b1): 222 /// 223 pub fn write_block_header( 224 w: &mut dyn Write, 225 func: &Function, 226 block: Block, 227 indent: usize, 228 ) -> fmt::Result { 229 let cold = if func.layout.is_cold(block) { 230 " cold" 231 } else { 232 "" 233 }; 234 235 // The `indent` is the instruction indentation. block headers are 4 spaces out from that. 236 write!(w, "{1:0$}{2}", indent - 4, "", block)?; 237 238 let mut args = func.dfg.block_params(block).iter().cloned(); 239 match args.next() { 240 None => return writeln!(w, "{}:", cold), 241 Some(arg) => { 242 write!(w, "(")?; 243 write_arg(w, func, arg)?; 244 } 245 } 246 // Remaining arguments. 247 for arg in args { 248 write!(w, ", ")?; 249 write_arg(w, func, arg)?; 250 } 251 writeln!(w, "){}:", cold) 252 } 253 254 fn decorate_block<FW: FuncWriter>( 255 func_w: &mut FW, 256 w: &mut dyn Write, 257 func: &Function, 258 aliases: &SecondaryMap<Value, Vec<Value>>, 259 block: Block, 260 ) -> fmt::Result { 261 // Indent all instructions if any srclocs are present. 262 let indent = if func.rel_srclocs().is_empty() { 4 } else { 36 }; 263 264 func_w.write_block_header(w, func, block, indent)?; 265 for a in func.dfg.block_params(block).iter().cloned() { 266 write_value_aliases(w, aliases, a, indent)?; 267 } 268 269 for inst in func.layout.block_insts(block) { 270 func_w.write_instruction(w, func, aliases, inst, indent)?; 271 } 272 273 Ok(()) 274 } 275 276 //---------------------------------------------------------------------- 277 // 278 // Instructions 279 280 // Should `inst` be printed with a type suffix? 281 // 282 // Polymorphic instructions may need a suffix indicating the value of the controlling type variable 283 // if it can't be trivially inferred. 284 // 285 fn type_suffix(func: &Function, inst: Inst) -> Option<Type> { 286 let inst_data = &func.dfg.insts[inst]; 287 let constraints = inst_data.opcode().constraints(); 288 289 if !constraints.is_polymorphic() { 290 return None; 291 } 292 293 // If the controlling type variable can be inferred from the type of the designated value input 294 // operand, we don't need the type suffix. 295 if constraints.use_typevar_operand() { 296 let ctrl_var = inst_data.typevar_operand(&func.dfg.value_lists).unwrap(); 297 let def_block = match func.dfg.value_def(ctrl_var) { 298 ValueDef::Result(instr, _) => func.layout.inst_block(instr), 299 ValueDef::Param(block, _) => Some(block), 300 ValueDef::Union(..) => None, 301 }; 302 if def_block.is_some() && def_block == func.layout.inst_block(inst) { 303 return None; 304 } 305 } 306 307 let rtype = func.dfg.ctrl_typevar(inst); 308 assert!( 309 !rtype.is_invalid(), 310 "Polymorphic instruction must produce a result" 311 ); 312 Some(rtype) 313 } 314 315 /// Write out any aliases to the given target, including indirect aliases 316 fn write_value_aliases( 317 w: &mut dyn Write, 318 aliases: &SecondaryMap<Value, Vec<Value>>, 319 target: Value, 320 indent: usize, 321 ) -> fmt::Result { 322 let mut todo_stack = vec![target]; 323 while let Some(target) = todo_stack.pop() { 324 for &a in &aliases[target] { 325 writeln!(w, "{1:0$}{2} -> {3}", indent, "", a, target)?; 326 todo_stack.push(a); 327 } 328 } 329 330 Ok(()) 331 } 332 333 fn write_instruction( 334 w: &mut dyn Write, 335 func: &Function, 336 aliases: &SecondaryMap<Value, Vec<Value>>, 337 inst: Inst, 338 indent: usize, 339 ) -> fmt::Result { 340 // Prefix containing source location, encoding, and value locations. 341 let mut s = String::with_capacity(16); 342 343 // Source location goes first. 344 let srcloc = func.srcloc(inst); 345 if !srcloc.is_default() { 346 write!(s, "{} ", srcloc)?; 347 } 348 349 // Write out prefix and indent the instruction. 350 write!(w, "{1:0$}", indent, s)?; 351 352 // Write out the result values, if any. 353 let mut has_results = false; 354 for r in func.dfg.inst_results(inst) { 355 let r = func.dfg.resolve_aliases(*r); 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 393 // Resolve a value to its alias, if any. 394 let v = |v: Value| dfg.resolve_aliases(v); 395 396 // Resolve a list of values to their aliases and format them. 397 let vs = |vs: &[_]| { 398 let vs: Vec<_> = vs.iter().copied().map(v).collect(); 399 DisplayValues(&vs).to_string() 400 }; 401 402 use crate::ir::instructions::InstructionData::*; 403 match dfg.insts[inst] { 404 AtomicRmw { op, args, .. } => write!(w, " {} {}, {}", op, v(args[0]), v(args[1])), 405 AtomicCas { args, .. } => write!(w, " {}, {}, {}", v(args[0]), v(args[1]), v(args[2])), 406 LoadNoOffset { flags, arg, .. } => write!(w, "{} {}", flags, v(arg)), 407 StoreNoOffset { flags, args, .. } => write!(w, "{} {}, {}", flags, v(args[0]), v(args[1])), 408 Unary { arg, .. } => write!(w, " {}", v(arg)), 409 UnaryImm { imm, .. } => write!(w, " {}", imm), 410 UnaryIeee32 { imm, .. } => write!(w, " {}", imm), 411 UnaryIeee64 { imm, .. } => write!(w, " {}", imm), 412 UnaryGlobalValue { global_value, .. } => write!(w, " {}", global_value), 413 UnaryConst { 414 constant_handle, .. 415 } => write!(w, " {}", constant_handle), 416 Binary { args, .. } => write!(w, " {}, {}", v(args[0]), v(args[1])), 417 BinaryImm8 { arg, imm, .. } => write!(w, " {}, {}", v(arg), imm), 418 BinaryImm64 { arg, imm, .. } => write!(w, " {}, {}", v(arg), imm), 419 Ternary { args, .. } => write!(w, " {}, {}, {}", v(args[0]), v(args[1]), v(args[2])), 420 MultiAry { ref args, .. } => { 421 if args.is_empty() { 422 write!(w, "") 423 } else { 424 write!(w, " {}", vs(args.as_slice(pool))) 425 } 426 } 427 NullAry { .. } => write!(w, " "), 428 TernaryImm8 { imm, args, .. } => write!(w, " {}, {}, {}", v(args[0]), v(args[1]), imm), 429 Shuffle { imm, args, .. } => { 430 let data = dfg.immediates.get(imm).expect( 431 "Expected the shuffle mask to already be inserted into the immediates table", 432 ); 433 write!(w, " {}, {}, {}", v(args[0]), v(args[1]), data) 434 } 435 IntCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, v(args[0]), v(args[1])), 436 IntCompareImm { cond, arg, imm, .. } => write!(w, " {} {}, {}", cond, arg, imm), 437 IntAddTrap { args, code, .. } => write!(w, " {}, {}, {}", v(args[0]), v(args[1]), code), 438 FloatCompare { cond, args, .. } => write!(w, " {} {}, {}", cond, v(args[0]), v(args[1])), 439 Jump { destination, .. } => { 440 write!(w, " {}", destination.display(pool)) 441 } 442 Brif { 443 arg, 444 blocks: [block_then, block_else], 445 .. 446 } => { 447 write!(w, " {}, {}", v(arg), block_then.display(pool))?; 448 write!(w, ", {}", block_else.display(pool)) 449 } 450 BranchTable { arg, table, .. } => { 451 write!(w, " {}, {}", v(arg), jump_tables[table].display(pool)) 452 } 453 Call { 454 func_ref, ref args, .. 455 } => write!(w, " {}({})", func_ref, vs(args.as_slice(pool))), 456 CallIndirect { 457 sig_ref, ref args, .. 458 } => { 459 let args = args.as_slice(pool); 460 write!(w, " {}, {}({})", sig_ref, args[0], vs(&args[1..])) 461 } 462 FuncAddr { func_ref, .. } => write!(w, " {}", func_ref), 463 StackLoad { 464 stack_slot, offset, .. 465 } => write!(w, " {}{}", stack_slot, offset), 466 StackStore { 467 arg, 468 stack_slot, 469 offset, 470 .. 471 } => write!(w, " {}, {}{}", v(arg), stack_slot, offset), 472 DynamicStackLoad { 473 dynamic_stack_slot, .. 474 } => write!(w, " {}", dynamic_stack_slot), 475 DynamicStackStore { 476 arg, 477 dynamic_stack_slot, 478 .. 479 } => write!(w, " {}, {}", arg, dynamic_stack_slot), 480 Load { 481 flags, arg, offset, .. 482 } => write!(w, "{} {}{}", flags, v(arg), offset), 483 Store { 484 flags, 485 args, 486 offset, 487 .. 488 } => write!(w, "{} {}, {}{}", flags, v(args[0]), v(args[1]), offset), 489 Trap { code, .. } => write!(w, " {}", code), 490 CondTrap { arg, code, .. } => write!(w, " {}, {}", v(arg), code), 491 }?; 492 493 let mut sep = " ; "; 494 for arg in dfg.inst_values(inst) { 495 if let ValueDef::Result(src, _) = dfg.value_def(arg) { 496 let imm = match dfg.insts[src] { 497 UnaryImm { imm, .. } => imm.to_string(), 498 UnaryIeee32 { imm, .. } => imm.to_string(), 499 UnaryIeee64 { imm, .. } => imm.to_string(), 500 UnaryConst { 501 constant_handle, .. 502 } => constant_handle.to_string(), 503 _ => continue, 504 }; 505 write!(w, "{}{} = {}", sep, v(arg), imm)?; 506 sep = ", "; 507 } 508 } 509 Ok(()) 510 } 511 512 /// Displayable slice of values. 513 struct DisplayValues<'a>(&'a [Value]); 514 515 impl<'a> fmt::Display for DisplayValues<'a> { 516 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 517 for (i, val) in self.0.iter().enumerate() { 518 if i == 0 { 519 write!(f, "{}", val)?; 520 } else { 521 write!(f, ", {}", val)?; 522 } 523 } 524 Ok(()) 525 } 526 } 527 528 #[cfg(test)] 529 mod tests { 530 use crate::cursor::{Cursor, CursorPosition, FuncCursor}; 531 use crate::ir::types; 532 use crate::ir::{Function, InstBuilder, StackSlotData, StackSlotKind, UserFuncName}; 533 use alloc::string::ToString; 534 535 #[test] 536 fn basic() { 537 let mut f = Function::new(); 538 assert_eq!(f.to_string(), "function u0:0() fast {\n}\n"); 539 540 f.name = UserFuncName::testcase("foo"); 541 assert_eq!(f.to_string(), "function %foo() fast {\n}\n"); 542 543 f.create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 4)); 544 assert_eq!( 545 f.to_string(), 546 "function %foo() fast {\n ss0 = explicit_slot 4\n}\n" 547 ); 548 549 let block = f.dfg.make_block(); 550 f.layout.append_block(block); 551 assert_eq!( 552 f.to_string(), 553 "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0:\n}\n" 554 ); 555 556 f.dfg.append_block_param(block, types::I8); 557 assert_eq!( 558 f.to_string(), 559 "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8):\n}\n" 560 ); 561 562 f.dfg.append_block_param(block, types::F32.by(4).unwrap()); 563 assert_eq!( 564 f.to_string(), 565 "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8, v1: f32x4):\n}\n" 566 ); 567 568 { 569 let mut cursor = FuncCursor::new(&mut f); 570 cursor.set_position(CursorPosition::After(block)); 571 cursor.ins().return_(&[]) 572 }; 573 assert_eq!( 574 f.to_string(), 575 "function %foo() fast {\n ss0 = explicit_slot 4\n\nblock0(v0: i8, v1: f32x4):\n return\n}\n" 576 ); 577 } 578 579 #[test] 580 fn aliases() { 581 use crate::ir::InstBuilder; 582 583 let mut func = Function::new(); 584 { 585 let block0 = func.dfg.make_block(); 586 let mut pos = FuncCursor::new(&mut func); 587 pos.insert_block(block0); 588 589 // make some detached values for change_to_alias 590 let v0 = pos.func.dfg.append_block_param(block0, types::I32); 591 let v1 = pos.func.dfg.append_block_param(block0, types::I32); 592 let v2 = pos.func.dfg.append_block_param(block0, types::I32); 593 pos.func.dfg.detach_block_params(block0); 594 595 // alias to a param--will be printed at beginning of block defining param 596 let v3 = pos.func.dfg.append_block_param(block0, types::I32); 597 pos.func.dfg.change_to_alias(v0, v3); 598 599 // alias to an alias--should print attached to alias, not ultimate target 600 pos.func.dfg.make_value_alias_for_serialization(v0, v2); // v0 <- v2 601 602 // alias to a result--will be printed after instruction producing result 603 let _dummy0 = pos.ins().iconst(types::I32, 42); 604 let v4 = pos.ins().iadd(v0, v0); 605 pos.func.dfg.change_to_alias(v1, v4); 606 let _dummy1 = pos.ins().iconst(types::I32, 23); 607 let _v7 = pos.ins().iadd(v1, v1); 608 } 609 assert_eq!( 610 func.to_string(), 611 "function u0:0() fast {\nblock0(v3: i32):\n v0 -> v3\n v2 -> v0\n v4 = iconst.i32 42\n v5 = iadd v3, v3\n v1 -> v5\n v6 = iconst.i32 23\n v7 = iadd v5, v5\n}\n" 612 ); 613 } 614 615 #[test] 616 fn cold_blocks() { 617 let mut func = Function::new(); 618 { 619 let mut pos = FuncCursor::new(&mut func); 620 621 let block0 = pos.func.dfg.make_block(); 622 pos.insert_block(block0); 623 pos.func.layout.set_cold(block0); 624 625 let block1 = pos.func.dfg.make_block(); 626 pos.insert_block(block1); 627 pos.func.dfg.append_block_param(block1, types::I32); 628 pos.func.layout.set_cold(block1); 629 } 630 631 assert_eq!( 632 func.to_string(), 633 "function u0:0() fast {\nblock0 cold:\n\nblock1(v0: i32) cold:\n}\n" 634 ); 635 } 636 } 637