1const std = @import("std"); 2const Allocator = std.mem.Allocator; 3const fmtIntSizeBin = std.fmt.fmtIntSizeBin; 4 5const Module = @import("Module.zig"); 6const Value = @import("value.zig").Value; 7const Zir = @import("Zir.zig"); 8const Air = @import("Air.zig"); 9const Liveness = @import("Liveness.zig"); 10 11pub fn dump(gpa: Allocator, air: Air, zir: Zir, liveness: Liveness) void { 12 const instruction_bytes = air.instructions.len * 13 // Here we don't use @sizeOf(Air.Inst.Data) because it would include 14 // the debug safety tag but we want to measure release size. 15 (@sizeOf(Air.Inst.Tag) + 8); 16 const extra_bytes = air.extra.len * @sizeOf(u32); 17 const values_bytes = air.values.len * @sizeOf(Value); 18 const tomb_bytes = liveness.tomb_bits.len * @sizeOf(usize); 19 const liveness_extra_bytes = liveness.extra.len * @sizeOf(u32); 20 const liveness_special_bytes = liveness.special.count() * 8; 21 const total_bytes = @sizeOf(Air) + instruction_bytes + extra_bytes + 22 values_bytes + @sizeOf(Liveness) + liveness_extra_bytes + 23 liveness_special_bytes + tomb_bytes; 24 25 // zig fmt: off 26 std.debug.print( 27 \\# Total AIR+Liveness bytes: {} 28 \\# AIR Instructions: {d} ({}) 29 \\# AIR Extra Data: {d} ({}) 30 \\# AIR Values Bytes: {d} ({}) 31 \\# Liveness tomb_bits: {} 32 \\# Liveness Extra Data: {d} ({}) 33 \\# Liveness special table: {d} ({}) 34 \\ 35 , .{ 36 fmtIntSizeBin(total_bytes), 37 air.instructions.len, fmtIntSizeBin(instruction_bytes), 38 air.extra.len, fmtIntSizeBin(extra_bytes), 39 air.values.len, fmtIntSizeBin(values_bytes), 40 fmtIntSizeBin(tomb_bytes), 41 liveness.extra.len, fmtIntSizeBin(liveness_extra_bytes), 42 liveness.special.count(), fmtIntSizeBin(liveness_special_bytes), 43 }); 44 // zig fmt: on 45 var arena = std.heap.ArenaAllocator.init(gpa); 46 defer arena.deinit(); 47 48 var writer: Writer = .{ 49 .gpa = gpa, 50 .arena = arena.allocator(), 51 .air = air, 52 .zir = zir, 53 .liveness = liveness, 54 .indent = 2, 55 }; 56 const stream = std.io.getStdErr().writer(); 57 writer.writeAllConstants(stream) catch return; 58 stream.writeByte('\n') catch return; 59 writer.writeBody(stream, air.getMainBody()) catch return; 60} 61 62const Writer = struct { 63 gpa: Allocator, 64 arena: Allocator, 65 air: Air, 66 zir: Zir, 67 liveness: Liveness, 68 indent: usize, 69 70 fn writeAllConstants(w: *Writer, s: anytype) @TypeOf(s).Error!void { 71 for (w.air.instructions.items(.tag)) |tag, i| { 72 const inst = @intCast(u32, i); 73 switch (tag) { 74 .constant, .const_ty => { 75 try s.writeByteNTimes(' ', w.indent); 76 try s.print("%{d} ", .{inst}); 77 try w.writeInst(s, inst); 78 try s.writeAll(")\n"); 79 }, 80 else => continue, 81 } 82 } 83 } 84 85 fn writeBody(w: *Writer, s: anytype, body: []const Air.Inst.Index) @TypeOf(s).Error!void { 86 for (body) |inst| { 87 try s.writeByteNTimes(' ', w.indent); 88 if (w.liveness.isUnused(inst)) { 89 try s.print("%{d}!", .{inst}); 90 } else { 91 try s.print("%{d} ", .{inst}); 92 } 93 try w.writeInst(s, inst); 94 try s.writeAll(")\n"); 95 } 96 } 97 98 fn writeInst(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { 99 const tags = w.air.instructions.items(.tag); 100 const tag = tags[inst]; 101 try s.print("= {s}(", .{@tagName(tags[inst])}); 102 switch (tag) { 103 .arg => try w.writeTyStr(s, inst), 104 105 .add, 106 .addwrap, 107 .add_sat, 108 .sub, 109 .subwrap, 110 .sub_sat, 111 .mul, 112 .mulwrap, 113 .mul_sat, 114 .div_float, 115 .div_trunc, 116 .div_floor, 117 .div_exact, 118 .rem, 119 .mod, 120 .ptr_add, 121 .ptr_sub, 122 .bit_and, 123 .bit_or, 124 .xor, 125 .cmp_lt, 126 .cmp_lte, 127 .cmp_eq, 128 .cmp_gte, 129 .cmp_gt, 130 .cmp_neq, 131 .bool_and, 132 .bool_or, 133 .store, 134 .array_elem_val, 135 .slice_elem_val, 136 .ptr_elem_val, 137 .shl, 138 .shl_exact, 139 .shl_sat, 140 .shr, 141 .set_union_tag, 142 .min, 143 .max, 144 => try w.writeBinOp(s, inst), 145 146 .is_null, 147 .is_non_null, 148 .is_null_ptr, 149 .is_non_null_ptr, 150 .is_err, 151 .is_non_err, 152 .is_err_ptr, 153 .is_non_err_ptr, 154 .ptrtoint, 155 .bool_to_int, 156 .ret, 157 .ret_load, 158 => try w.writeUnOp(s, inst), 159 160 .breakpoint, 161 .unreach, 162 => try w.writeNoOp(s, inst), 163 164 .const_ty, 165 .alloc, 166 .ret_ptr, 167 => try w.writeTy(s, inst), 168 169 .not, 170 .bitcast, 171 .load, 172 .fptrunc, 173 .fpext, 174 .intcast, 175 .trunc, 176 .optional_payload, 177 .optional_payload_ptr, 178 .optional_payload_ptr_set, 179 .wrap_optional, 180 .unwrap_errunion_payload, 181 .unwrap_errunion_err, 182 .unwrap_errunion_payload_ptr, 183 .unwrap_errunion_err_ptr, 184 .wrap_errunion_payload, 185 .wrap_errunion_err, 186 .slice_ptr, 187 .slice_len, 188 .ptr_slice_len_ptr, 189 .ptr_slice_ptr_ptr, 190 .struct_field_ptr_index_0, 191 .struct_field_ptr_index_1, 192 .struct_field_ptr_index_2, 193 .struct_field_ptr_index_3, 194 .array_to_slice, 195 .int_to_float, 196 .float_to_int, 197 .get_union_tag, 198 .clz, 199 .ctz, 200 .popcount, 201 => try w.writeTyOp(s, inst), 202 203 .block, 204 .loop, 205 => try w.writeBlock(s, inst), 206 207 .slice, 208 .slice_elem_ptr, 209 .ptr_elem_ptr, 210 => try w.writeTyPlBin(s, inst), 211 212 .struct_field_ptr => try w.writeStructField(s, inst), 213 .struct_field_val => try w.writeStructField(s, inst), 214 .constant => try w.writeConstant(s, inst), 215 .assembly => try w.writeAssembly(s, inst), 216 .dbg_stmt => try w.writeDbgStmt(s, inst), 217 .call => try w.writeCall(s, inst), 218 .br => try w.writeBr(s, inst), 219 .cond_br => try w.writeCondBr(s, inst), 220 .switch_br => try w.writeSwitchBr(s, inst), 221 .cmpxchg_weak, .cmpxchg_strong => try w.writeCmpxchg(s, inst), 222 .fence => try w.writeFence(s, inst), 223 .atomic_load => try w.writeAtomicLoad(s, inst), 224 .atomic_store_unordered => try w.writeAtomicStore(s, inst, .Unordered), 225 .atomic_store_monotonic => try w.writeAtomicStore(s, inst, .Monotonic), 226 .atomic_store_release => try w.writeAtomicStore(s, inst, .Release), 227 .atomic_store_seq_cst => try w.writeAtomicStore(s, inst, .SeqCst), 228 .atomic_rmw => try w.writeAtomicRmw(s, inst), 229 .memcpy => try w.writeMemcpy(s, inst), 230 .memset => try w.writeMemset(s, inst), 231 } 232 } 233 234 fn writeTyStr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { 235 const ty_str = w.air.instructions.items(.data)[inst].ty_str; 236 const name = w.zir.nullTerminatedString(ty_str.str); 237 try s.print("\"{}\", {}", .{ std.zig.fmtEscapes(name), w.air.getRefType(ty_str.ty) }); 238 } 239 240 fn writeBinOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { 241 const bin_op = w.air.instructions.items(.data)[inst].bin_op; 242 try w.writeOperand(s, inst, 0, bin_op.lhs); 243 try s.writeAll(", "); 244 try w.writeOperand(s, inst, 1, bin_op.rhs); 245 } 246 247 fn writeUnOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { 248 const un_op = w.air.instructions.items(.data)[inst].un_op; 249 try w.writeOperand(s, inst, 0, un_op); 250 } 251 252 fn writeNoOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { 253 _ = w; 254 _ = inst; 255 _ = s; 256 // no-op, no argument to write 257 } 258 259 fn writeTy(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { 260 const ty = w.air.instructions.items(.data)[inst].ty; 261 try s.print("{}", .{ty}); 262 } 263 264 fn writeTyOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { 265 const ty_op = w.air.instructions.items(.data)[inst].ty_op; 266 try s.print("{}, ", .{w.air.getRefType(ty_op.ty)}); 267 try w.writeOperand(s, inst, 0, ty_op.operand); 268 } 269 270 fn writeBlock(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { 271 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; 272 const extra = w.air.extraData(Air.Block, ty_pl.payload); 273 const body = w.air.extra[extra.end..][0..extra.data.body_len]; 274 275 try s.print("{}, {{\n", .{w.air.getRefType(ty_pl.ty)}); 276 const old_indent = w.indent; 277 w.indent += 2; 278 try w.writeBody(s, body); 279 w.indent = old_indent; 280 try s.writeByteNTimes(' ', w.indent); 281 try s.writeAll("}"); 282 } 283 284 fn writeStructField(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { 285 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; 286 const extra = w.air.extraData(Air.StructField, ty_pl.payload).data; 287 288 try w.writeOperand(s, inst, 0, extra.struct_operand); 289 try s.print(", {d}", .{extra.field_index}); 290 } 291 292 fn writeTyPlBin(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { 293 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; 294 const extra = w.air.extraData(Air.Bin, ty_pl.payload).data; 295 296 try w.writeOperand(s, inst, 0, extra.lhs); 297 try s.writeAll(", "); 298 try w.writeOperand(s, inst, 1, extra.rhs); 299 } 300 301 fn writeCmpxchg(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { 302 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; 303 const extra = w.air.extraData(Air.Cmpxchg, ty_pl.payload).data; 304 305 try w.writeOperand(s, inst, 0, extra.ptr); 306 try s.writeAll(", "); 307 try w.writeOperand(s, inst, 1, extra.expected_value); 308 try s.writeAll(", "); 309 try w.writeOperand(s, inst, 2, extra.new_value); 310 try s.print(", {s}, {s}", .{ 311 @tagName(extra.successOrder()), @tagName(extra.failureOrder()), 312 }); 313 } 314 315 fn writeFence(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { 316 const atomic_order = w.air.instructions.items(.data)[inst].fence; 317 318 try s.print("{s}", .{@tagName(atomic_order)}); 319 } 320 321 fn writeAtomicLoad(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { 322 const atomic_load = w.air.instructions.items(.data)[inst].atomic_load; 323 324 try w.writeOperand(s, inst, 0, atomic_load.ptr); 325 try s.print(", {s}", .{@tagName(atomic_load.order)}); 326 } 327 328 fn writeAtomicStore( 329 w: *Writer, 330 s: anytype, 331 inst: Air.Inst.Index, 332 order: std.builtin.AtomicOrder, 333 ) @TypeOf(s).Error!void { 334 const bin_op = w.air.instructions.items(.data)[inst].bin_op; 335 try w.writeOperand(s, inst, 0, bin_op.lhs); 336 try s.writeAll(", "); 337 try w.writeOperand(s, inst, 1, bin_op.rhs); 338 try s.print(", {s}", .{@tagName(order)}); 339 } 340 341 fn writeAtomicRmw(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { 342 const pl_op = w.air.instructions.items(.data)[inst].pl_op; 343 const extra = w.air.extraData(Air.AtomicRmw, pl_op.payload).data; 344 345 try w.writeOperand(s, inst, 0, pl_op.operand); 346 try s.writeAll(", "); 347 try w.writeOperand(s, inst, 1, extra.operand); 348 try s.print(", {s}, {s}", .{ @tagName(extra.op()), @tagName(extra.ordering()) }); 349 } 350 351 fn writeMemset(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { 352 const pl_op = w.air.instructions.items(.data)[inst].pl_op; 353 const extra = w.air.extraData(Air.Bin, pl_op.payload).data; 354 355 try w.writeOperand(s, inst, 0, pl_op.operand); 356 try s.writeAll(", "); 357 try w.writeOperand(s, inst, 1, extra.lhs); 358 try s.writeAll(", "); 359 try w.writeOperand(s, inst, 2, extra.rhs); 360 } 361 362 fn writeMemcpy(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { 363 const pl_op = w.air.instructions.items(.data)[inst].pl_op; 364 const extra = w.air.extraData(Air.Bin, pl_op.payload).data; 365 366 try w.writeOperand(s, inst, 0, pl_op.operand); 367 try s.writeAll(", "); 368 try w.writeOperand(s, inst, 1, extra.lhs); 369 try s.writeAll(", "); 370 try w.writeOperand(s, inst, 2, extra.rhs); 371 } 372 373 fn writeConstant(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { 374 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; 375 const val = w.air.values[ty_pl.payload]; 376 try s.print("{}, {}", .{ w.air.getRefType(ty_pl.ty), val }); 377 } 378 379 fn writeAssembly(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { 380 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; 381 const air_asm = w.air.extraData(Air.Asm, ty_pl.payload); 382 const zir = w.zir; 383 const extended = zir.instructions.items(.data)[air_asm.data.zir_index].extended; 384 const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand); 385 const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source); 386 const outputs_len = @truncate(u5, extended.small); 387 const args_len = @truncate(u5, extended.small >> 5); 388 const clobbers_len = @truncate(u5, extended.small >> 10); 389 const args = @bitCast([]const Air.Inst.Ref, w.air.extra[air_asm.end..][0..args_len]); 390 391 var extra_i: usize = zir_extra.end; 392 const output_constraint: ?[]const u8 = out: { 393 var i: usize = 0; 394 while (i < outputs_len) : (i += 1) { 395 const output = zir.extraData(Zir.Inst.Asm.Output, extra_i); 396 extra_i = output.end; 397 break :out zir.nullTerminatedString(output.data.constraint); 398 } 399 break :out null; 400 }; 401 402 try s.print("\"{s}\"", .{asm_source}); 403 404 if (output_constraint) |constraint| { 405 const ret_ty = w.air.typeOfIndex(inst); 406 try s.print(", {s} -> {}", .{ constraint, ret_ty }); 407 } 408 409 for (args) |arg| { 410 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i); 411 extra_i = input.end; 412 const constraint = zir.nullTerminatedString(input.data.constraint); 413 414 try s.print(", {s} = (", .{constraint}); 415 try w.writeOperand(s, inst, 0, arg); 416 try s.writeByte(')'); 417 } 418 419 const clobbers = zir.extra[extra_i..][0..clobbers_len]; 420 for (clobbers) |clobber_index| { 421 const clobber = zir.nullTerminatedString(clobber_index); 422 try s.writeAll(", ~{"); 423 try s.writeAll(clobber); 424 try s.writeAll("}"); 425 } 426 } 427 428 fn writeDbgStmt(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { 429 const dbg_stmt = w.air.instructions.items(.data)[inst].dbg_stmt; 430 try s.print("{d}:{d}", .{ dbg_stmt.line + 1, dbg_stmt.column + 1 }); 431 } 432 433 fn writeCall(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { 434 const pl_op = w.air.instructions.items(.data)[inst].pl_op; 435 const extra = w.air.extraData(Air.Call, pl_op.payload); 436 const args = @bitCast([]const Air.Inst.Ref, w.air.extra[extra.end..][0..extra.data.args_len]); 437 try w.writeOperand(s, inst, 0, pl_op.operand); 438 try s.writeAll(", ["); 439 for (args) |arg, i| { 440 if (i != 0) try s.writeAll(", "); 441 try w.writeOperand(s, inst, 1 + i, arg); 442 } 443 try s.writeAll("]"); 444 } 445 446 fn writeBr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { 447 const br = w.air.instructions.items(.data)[inst].br; 448 try w.writeInstIndex(s, br.block_inst, false); 449 try s.writeAll(", "); 450 try w.writeOperand(s, inst, 0, br.operand); 451 } 452 453 fn writeCondBr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { 454 const pl_op = w.air.instructions.items(.data)[inst].pl_op; 455 const extra = w.air.extraData(Air.CondBr, pl_op.payload); 456 const then_body = w.air.extra[extra.end..][0..extra.data.then_body_len]; 457 const else_body = w.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; 458 const liveness_condbr = w.liveness.getCondBr(inst); 459 460 try w.writeOperand(s, inst, 0, pl_op.operand); 461 try s.writeAll(", {\n"); 462 const old_indent = w.indent; 463 w.indent += 2; 464 465 if (liveness_condbr.then_deaths.len != 0) { 466 try s.writeByteNTimes(' ', w.indent); 467 for (liveness_condbr.then_deaths) |operand, i| { 468 if (i != 0) try s.writeAll(" "); 469 try s.print("%{d}!", .{operand}); 470 } 471 try s.writeAll("\n"); 472 } 473 474 try w.writeBody(s, then_body); 475 try s.writeByteNTimes(' ', old_indent); 476 try s.writeAll("}, {\n"); 477 478 if (liveness_condbr.else_deaths.len != 0) { 479 try s.writeByteNTimes(' ', w.indent); 480 for (liveness_condbr.else_deaths) |operand, i| { 481 if (i != 0) try s.writeAll(" "); 482 try s.print("%{d}!", .{operand}); 483 } 484 try s.writeAll("\n"); 485 } 486 487 try w.writeBody(s, else_body); 488 w.indent = old_indent; 489 490 try s.writeByteNTimes(' ', old_indent); 491 try s.writeAll("}"); 492 } 493 494 fn writeSwitchBr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { 495 const pl_op = w.air.instructions.items(.data)[inst].pl_op; 496 const switch_br = w.air.extraData(Air.SwitchBr, pl_op.payload); 497 var extra_index: usize = switch_br.end; 498 var case_i: u32 = 0; 499 500 try w.writeOperand(s, inst, 0, pl_op.operand); 501 const old_indent = w.indent; 502 w.indent += 2; 503 504 while (case_i < switch_br.data.cases_len) : (case_i += 1) { 505 const case = w.air.extraData(Air.SwitchBr.Case, extra_index); 506 const items = @bitCast([]const Air.Inst.Ref, w.air.extra[case.end..][0..case.data.items_len]); 507 const case_body = w.air.extra[case.end + items.len ..][0..case.data.body_len]; 508 extra_index = case.end + case.data.items_len + case_body.len; 509 510 try s.writeAll(", ["); 511 for (items) |item, item_i| { 512 if (item_i != 0) try s.writeAll(", "); 513 try w.writeInstRef(s, item, false); 514 } 515 try s.writeAll("] => {\n"); 516 w.indent += 2; 517 try w.writeBody(s, case_body); 518 w.indent -= 2; 519 try s.writeByteNTimes(' ', w.indent); 520 try s.writeAll("}"); 521 } 522 523 const else_body = w.air.extra[extra_index..][0..switch_br.data.else_body_len]; 524 if (else_body.len != 0) { 525 try s.writeAll(", else => {\n"); 526 w.indent += 2; 527 try w.writeBody(s, else_body); 528 w.indent -= 2; 529 try s.writeByteNTimes(' ', w.indent); 530 try s.writeAll("}"); 531 } 532 533 try s.writeAll("\n"); 534 try s.writeByteNTimes(' ', old_indent); 535 try s.writeAll("}"); 536 } 537 538 fn writeOperand( 539 w: *Writer, 540 s: anytype, 541 inst: Air.Inst.Index, 542 op_index: usize, 543 operand: Air.Inst.Ref, 544 ) @TypeOf(s).Error!void { 545 const dies = if (op_index < Liveness.bpi - 1) 546 w.liveness.operandDies(inst, @intCast(Liveness.OperandInt, op_index)) 547 else blk: { 548 // TODO 549 break :blk false; 550 }; 551 return w.writeInstRef(s, operand, dies); 552 } 553 554 fn writeInstRef( 555 w: *Writer, 556 s: anytype, 557 operand: Air.Inst.Ref, 558 dies: bool, 559 ) @TypeOf(s).Error!void { 560 var i: usize = @enumToInt(operand); 561 562 if (i < Air.Inst.Ref.typed_value_map.len) { 563 return s.print("@{}", .{operand}); 564 } 565 i -= Air.Inst.Ref.typed_value_map.len; 566 567 return w.writeInstIndex(s, @intCast(Air.Inst.Index, i), dies); 568 } 569 570 fn writeInstIndex( 571 w: *Writer, 572 s: anytype, 573 inst: Air.Inst.Index, 574 dies: bool, 575 ) @TypeOf(s).Error!void { 576 _ = w; 577 if (dies) { 578 try s.print("%{d}!", .{inst}); 579 } else { 580 try s.print("%{d}", .{inst}); 581 } 582 } 583}; 584