From 3a516433b0cf94e8aa67819acc49c03e0b72f296 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Mon, 27 Mar 2023 03:10:03 -0400 Subject: [PATCH] x86_64: add live codegen debug --- src/arch/x86_64/CodeGen.zig | 69 ++++++++++++++++++++++++++++++- src/arch/x86_64/encoder.zig | 45 ++++++++++----------- src/print_air.zig | 81 ++++++++++++++++++++++--------------- 3 files changed, 138 insertions(+), 57 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 5ab0e64615..f93cc31b92 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -8,6 +8,7 @@ const link = @import("../../link.zig"); const log = std.log.scoped(.codegen); const math = std.math; const mem = std.mem; +const print_air = @import("../../print_air.zig"); const trace = @import("../../tracy.zig").trace; const Air = @import("../../Air.zig"); @@ -20,6 +21,7 @@ const ErrorMsg = Module.ErrorMsg; const Result = codegen.Result; const Emit = @import("Emit.zig"); const Liveness = @import("../../Liveness.zig"); +const Lower = @import("Lower.zig"); const Mir = @import("Mir.zig"); const Module = @import("../../Module.zig"); const Target = std.Target; @@ -44,6 +46,8 @@ const sse = abi.RegisterClass.sse; const InnerError = CodeGenError || error{OutOfRegisters}; +const debug_wip_mir = false; + gpa: Allocator, air: Air, liveness: Liveness, @@ -103,8 +107,12 @@ air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init, /// For mir debug info, maps a mir index to a air index mir_to_air_map: if (builtin.mode == .Debug) std.AutoHashMap(Mir.Inst.Index, Air.Inst.Index) else void, +debug_wip_mir_inst: @TypeOf(debug_wip_mir_inst_init) = debug_wip_mir_inst_init, + const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {}; +const debug_wip_mir_inst_init = if (debug_wip_mir) @as(Mir.Inst.Index, 0) else {}; + pub const MCValue = union(enum) { /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc. /// TODO Look into deleting this tag and using `dead` instead, since every use @@ -267,6 +275,12 @@ pub fn generate( assert(fn_owner_decl.has_tv); const fn_type = fn_owner_decl.ty; + if (debug_wip_mir) { + const stderr = std.io.getStdErr().writer(); + fn_owner_decl.renderFullyQualifiedName(mod, stderr) catch {}; + stderr.writeAll(":\n") catch {}; + } + var branch_stack = std.ArrayList(Branch).init(bin_file.allocator); try branch_stack.ensureUnusedCapacity(2); // The outermost branch is used for constants only. @@ -683,6 +697,56 @@ fn asmMemoryRegisterImmediate( }); } +fn printWipMir(self: *Self, stream: anytype, air_inst: ?Air.Inst.Index) !void { + const mod = self.bin_file.options.module.?; + if (!debug_wip_mir) return; + + var lower = Lower{ + .allocator = self.gpa, + .mir = .{ + .instructions = self.mir_instructions.slice(), + .extra = self.mir_extra.items, + }, + .target = self.target, + .src_loc = self.src_loc, + }; + var mir_inst = self.debug_wip_mir_inst; + var mir_end = @intCast(Mir.Inst.Index, self.mir_instructions.len); + defer self.debug_wip_mir_inst = mir_end; + while (mir_inst < mir_end) : (mir_inst += 1) { + for (lower.lowerMir(lower.mir.instructions.get(mir_inst)) catch |err| switch (err) { + error.LowerFail => { + defer { + lower.err_msg.?.deinit(self.gpa); + lower.err_msg = null; + } + try stream.print("{s}\n", .{lower.err_msg.?.msg}); + continue; + }, + error.InvalidInstruction, error.CannotEncode => |e| { + try stream.writeAll(switch (e) { + error.InvalidInstruction => "CodeGen failed to find a viable instruction.\n", + error.CannotEncode => "CodeGen failed to encode the instruction.\n", + }); + continue; + }, + else => |e| return e, + }) |lower_inst| { + try stream.writeAll(" | "); + try lower_inst.fmtPrint(stream); + try stream.writeByte('\n'); + } + } + + if (air_inst) |inst| { + print_air.writeInst(stream, inst, mod, self.air, self.liveness); + } +} + +fn dumpWipMir(self: *Self, air_inst: ?Air.Inst.Index) void { + self.printWipMir(std.io.getStdErr().writer(), air_inst) catch return; +} + fn gen(self: *Self) InnerError!void { const cc = self.fn_type.fnCallingConvention(); if (cc != .Naked) { @@ -836,6 +900,8 @@ fn gen(self: *Self) InnerError!void { .ops = undefined, .data = .{ .payload = payload }, }); + + self.dumpWipMir(null); } fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { @@ -845,8 +911,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { const old_air_bookkeeping = self.air_bookkeeping; try self.ensureProcessDeathCapacity(Liveness.bpi); if (builtin.mode == .Debug) { - try self.mir_to_air_map.put(@intCast(u32, self.mir_instructions.len), inst); + try self.mir_to_air_map.put(@intCast(Mir.Inst.Index, self.mir_instructions.len), inst); } + self.dumpWipMir(inst); switch (air_tags[inst]) { // zig fmt: off diff --git a/src/arch/x86_64/encoder.zig b/src/arch/x86_64/encoder.zig index e7e231c063..663dad8727 100644 --- a/src/arch/x86_64/encoder.zig +++ b/src/arch/x86_64/encoder.zig @@ -54,18 +54,17 @@ pub const Instruction = struct { }; } - pub fn fmtPrint(op: Operand, enc_op: Encoding.Op, writer: anytype) !void { + pub fn fmtPrint(op: Operand, enc_op: Encoding.Op, writer: anytype) @TypeOf(writer).Error!void { switch (op) { .none => {}, .reg => |reg| try writer.writeAll(@tagName(reg)), .mem => |mem| switch (mem) { .rip => |rip| { try writer.print("{s} ptr [rip", .{@tagName(rip.ptr_size)}); - if (rip.disp != 0) { - const sign_bit = if (sign(rip.disp) < 0) "-" else "+"; - const disp_abs = try std.math.absInt(rip.disp); - try writer.print(" {s} 0x{x}", .{ sign_bit, disp_abs }); - } + if (rip.disp != 0) try writer.print(" {c} 0x{x}", .{ + @as(u8, if (rip.disp < 0) '-' else '+'), + std.math.absCast(rip.disp), + }); try writer.writeByte(']'); }, .sib => |sib| { @@ -77,27 +76,31 @@ pub const Instruction = struct { try writer.writeByte('['); + var any = false; if (sib.base) |base| { try writer.print("{s}", .{@tagName(base)}); + any = true; } if (sib.scale_index) |si| { - if (sib.base != null) { - try writer.writeAll(" + "); - } + if (any) try writer.writeAll(" + "); try writer.print("{s} * {d}", .{ @tagName(si.index), si.scale }); + any = true; } - if (sib.disp != 0) { - if (sib.base != null or sib.scale_index != null) { - try writer.writeByte(' '); - } - try writer.writeByte(if (sign(sib.disp) < 0) '-' else '+'); - const disp_abs = try std.math.absInt(sib.disp); - try writer.print(" 0x{x}", .{disp_abs}); + if (sib.disp != 0 or !any) { + if (any) + try writer.print(" {c} ", .{@as(u8, if (sib.disp < 0) '-' else '+')}) + else if (sib.disp < 0) + try writer.writeByte('-'); + try writer.print("0x{x}", .{std.math.absCast(sib.disp)}); + any = true; } try writer.writeByte(']'); }, - .moffs => |moffs| try writer.print("{s}:0x{x}", .{ @tagName(moffs.seg), moffs.offset }), + .moffs => |moffs| try writer.print("{s}:0x{x}", .{ + @tagName(moffs.seg), + moffs.offset, + }), }, .imm => |imm| try writer.print("0x{x}", .{imm.asUnsigned(enc_op.bitSize())}), } @@ -127,10 +130,10 @@ pub const Instruction = struct { return inst; } - pub fn fmtPrint(inst: Instruction, writer: anytype) !void { + pub fn fmtPrint(inst: Instruction, writer: anytype) @TypeOf(writer).Error!void { if (inst.prefix != .none) try writer.print("{s} ", .{@tagName(inst.prefix)}); try writer.print("{s}", .{@tagName(inst.encoding.mnemonic)}); - for (inst.ops, inst.encodings.ops, 0..) |op, enc, i| { + for (inst.ops, inst.encoding.data.ops, 0..) |op, enc, i| { if (op == .none) break; if (i > 0) try writer.writeByte(','); try writer.writeByte(' '); @@ -390,10 +393,6 @@ pub const Instruction = struct { } }; -inline fn sign(i: anytype) @TypeOf(i) { - return @as(@TypeOf(i), @boolToInt(i > 0)) - @boolToInt(i < 0); -} - pub const LegacyPrefixes = packed struct { /// LOCK prefix_f0: bool = false, diff --git a/src/print_air.zig b/src/print_air.zig index f5c06daae2..8d29a272ca 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -8,7 +8,7 @@ const Type = @import("type.zig").Type; const Air = @import("Air.zig"); const Liveness = @import("Liveness.zig"); -pub fn dump(module: *Module, air: Air, liveness: Liveness) void { +pub fn write(stream: anytype, module: *Module, air: Air, liveness: Liveness) void { const instruction_bytes = air.instructions.len * // Here we don't use @sizeOf(Air.Inst.Data) because it would include // the debug safety tag but we want to measure release size. @@ -23,7 +23,7 @@ pub fn dump(module: *Module, air: Air, liveness: Liveness) void { liveness_special_bytes + tomb_bytes; // zig fmt: off - std.debug.print( + stream.print( \\# Total AIR+Liveness bytes: {} \\# AIR Instructions: {d} ({}) \\# AIR Extra Data: {d} ({}) @@ -40,65 +40,78 @@ pub fn dump(module: *Module, air: Air, liveness: Liveness) void { fmtIntSizeBin(tomb_bytes), liveness.extra.len, fmtIntSizeBin(liveness_extra_bytes), liveness.special.count(), fmtIntSizeBin(liveness_special_bytes), - }); + }) catch return; // zig fmt: on - var arena = std.heap.ArenaAllocator.init(module.gpa); - defer arena.deinit(); var writer: Writer = .{ .module = module, .gpa = module.gpa, - .arena = arena.allocator(), .air = air, .liveness = liveness, .indent = 2, + .skip_body = false, }; - const stream = std.io.getStdErr().writer(); writer.writeAllConstants(stream) catch return; stream.writeByte('\n') catch return; writer.writeBody(stream, air.getMainBody()) catch return; } +pub fn writeInst( + stream: anytype, + inst: Air.Inst.Index, + module: *Module, + air: Air, + liveness: Liveness, +) void { + var writer: Writer = .{ + .module = module, + .gpa = module.gpa, + .air = air, + .liveness = liveness, + .indent = 2, + .skip_body = true, + }; + writer.writeInst(stream, inst) catch return; +} + +pub fn dump(module: *Module, air: Air, liveness: Liveness) void { + write(std.io.getStdErr().writer(), module, air, liveness); +} + +pub fn dumpInst(inst: Air.Inst.Index, module: *Module, air: Air, liveness: Liveness) void { + writeInst(std.io.getStdErr().writer(), inst, module, air, liveness); +} + const Writer = struct { module: *Module, gpa: Allocator, - arena: Allocator, air: Air, liveness: Liveness, indent: usize, + skip_body: bool, fn writeAllConstants(w: *Writer, s: anytype) @TypeOf(s).Error!void { for (w.air.instructions.items(.tag), 0..) |tag, i| { - const inst = @intCast(u32, i); + const inst = @intCast(Air.Inst.Index, i); switch (tag) { - .constant, .const_ty => { - try s.writeByteNTimes(' ', w.indent); - try s.print("%{d} ", .{inst}); - try w.writeInst(s, inst); - try s.writeAll(")\n"); - }, + .constant, .const_ty => try w.writeInst(s, inst), else => continue, } } } fn writeBody(w: *Writer, s: anytype, body: []const Air.Inst.Index) @TypeOf(s).Error!void { - for (body) |inst| { - try s.writeByteNTimes(' ', w.indent); - if (w.liveness.isUnused(inst)) { - try s.print("%{d}!", .{inst}); - } else { - try s.print("%{d} ", .{inst}); - } - try w.writeInst(s, inst); - try s.writeAll(")\n"); - } + for (body) |inst| try w.writeInst(s, inst); } fn writeInst(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const tags = w.air.instructions.items(.tag); - const tag = tags[inst]; - try s.print("= {s}(", .{@tagName(tags[inst])}); + const tag = w.air.instructions.items(.tag)[inst]; + try s.writeByteNTimes(' ', w.indent); + try s.print("%{d}{c}= {s}(", .{ + inst, + @as(u8, if (w.liveness.isUnused(inst)) '!' else ' '), + @tagName(tag), + }); switch (tag) { .add, .addwrap, @@ -316,6 +329,7 @@ const Writer = struct { .dbg_block_begin, .dbg_block_end => {}, } + try s.writeAll(")\n"); } fn writeBinOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { @@ -372,6 +386,7 @@ const Writer = struct { const body = w.air.extra[extra.end..][0..extra.data.body_len]; try w.writeType(s, w.air.getRefType(ty_pl.ty)); + if (w.skip_body) return s.writeAll(", ..."); try s.writeAll(", {\n"); const old_indent = w.indent; w.indent += 2; @@ -703,6 +718,7 @@ const Writer = struct { const body = w.air.extra[extra.end..][0..extra.data.body_len]; try w.writeOperand(s, inst, 0, pl_op.operand); + if (w.skip_body) return s.writeAll(", ..."); try s.writeAll(", {\n"); const old_indent = w.indent; w.indent += 2; @@ -721,6 +737,7 @@ const Writer = struct { try s.writeAll(", "); try w.writeType(s, w.air.getRefType(ty_pl.ty)); + if (w.skip_body) return s.writeAll(", ..."); try s.writeAll(", {\n"); const old_indent = w.indent; w.indent += 2; @@ -738,6 +755,7 @@ const Writer = struct { const liveness_condbr = w.liveness.getCondBr(inst); try w.writeOperand(s, inst, 0, pl_op.operand); + if (w.skip_body) return s.writeAll(", ..."); try s.writeAll(", {\n"); const old_indent = w.indent; w.indent += 2; @@ -900,10 +918,7 @@ const Writer = struct { dies: bool, ) @TypeOf(s).Error!void { _ = w; - if (dies) { - try s.print("%{d}!", .{inst}); - } else { - try s.print("%{d}", .{inst}); - } + try s.print("%{d}", .{inst}); + if (dies) try s.writeByte('!'); } };