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 { destination, .. } => { 418 write!(w, " {}", destination.block(pool))?; 419 write_block_args(w, destination.args_slice(pool)) 420 } 421 Branch { 422 arg, destination, .. 423 } => { 424 write!(w, " {}, {}", arg, destination.block(pool))?; 425 write_block_args(w, destination.args_slice(pool)) 426 } 427 BranchTable { 428 arg, 429 destination, 430 table, 431 .. 432 } => write!(w, " {}, {}, {}", arg, destination, table), 433 Call { 434 func_ref, ref args, .. 435 } => write!(w, " {}({})", func_ref, DisplayValues(args.as_slice(pool))), 436 CallIndirect { 437 sig_ref, ref args, .. 438 } => { 439 let args = args.as_slice(pool); 440 write!( 441 w, 442 " {}, {}({})", 443 sig_ref, 444 args[0], 445 DisplayValues(&args[1..]) 446 ) 447 } 448 FuncAddr { func_ref, .. } => write!(w, " {}", func_ref), 449 StackLoad { 450 stack_slot, offset, .. 451 } => write!(w, " {}{}", stack_slot, offset), 452 StackStore { 453 arg, 454 stack_slot, 455 offset, 456 .. 457 } => write!(w, " {}, {}{}", arg, stack_slot, offset), 458 DynamicStackLoad { 459 dynamic_stack_slot, .. 460 } => write!(w, " {}", dynamic_stack_slot), 461 DynamicStackStore { 462 arg, 463 dynamic_stack_slot, 464 .. 465 } => write!(w, " {}, {}", arg, dynamic_stack_slot), 466 TableAddr { table, arg, .. } => write!(w, " {}, {}", table, arg), 467 Load { 468 flags, arg, offset, .. 469 } => write!(w, "{} {}{}", flags, arg, offset), 470 Store { 471 flags, 472 args, 473 offset, 474 .. 475 } => write!(w, "{} {}, {}{}", flags, args[0], args[1], offset), 476 Trap { code, .. } => write!(w, " {}", code), 477 CondTrap { arg, code, .. } => write!(w, " {}, {}", arg, code), 478 }?; 479 480 let mut sep = " ; "; 481 for arg in dfg.inst_values(inst) { 482 if let ValueDef::Result(src, _) = dfg.value_def(arg) { 483 let imm = match dfg.insts[src] { 484 UnaryImm { imm, .. } => imm.to_string(), 485 UnaryIeee32 { imm, .. } => imm.to_string(), 486 UnaryIeee64 { imm, .. } => imm.to_string(), 487 UnaryConst { 488 constant_handle, .. 489 } => constant_handle.to_string(), 490 _ => continue, 491 }; 492 write!(w, "{}{} = {}", sep, arg, imm)?; 493 sep = ", "; 494 } 495 } 496 Ok(()) 497 } 498 499 /// Write block args using optional parantheses. 500 fn write_block_args(w: &mut dyn Write, args: &[Value]) -> fmt::Result { 501 if args.is_empty() { 502 Ok(()) 503 } else { 504 write!(w, "({})", DisplayValues(args)) 505 } 506 } 507 508 /// Displayable slice of values. 509 struct DisplayValues<'a>(&'a [Value]); 510 511 impl<'a> fmt::Display for DisplayValues<'a> { 512 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 513 for (i, val) in self.0.iter().enumerate() { 514 if i == 0 { 515 write!(f, "{}", val)?; 516 } else { 517 write!(f, ", {}", val)?; 518 } 519 } 520 Ok(()) 521 } 522 } 523 524 struct DisplayValuesWithDelimiter<'a>(&'a [Value], char); 525 526 impl<'a> fmt::Display for DisplayValuesWithDelimiter<'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, "{}{}", self.1, 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