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