From 364e53f3bf6b5aa4e5e7eba5d790c5b957007067 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 30 Mar 2022 18:29:44 +0200 Subject: [PATCH] dwarf: emit debug info for local variables on x86_64 Add support for emitting debug info for local variables within a subprogram. This required moving bits responsible for populating the debug info back to `CodeGen` from `Emit` as we require the operand to be resolved at callsite plus we need to know its type. Without enforcing this, we could end up with a `dead` mcv. --- src/arch/arm/Emit.zig | 4 +- src/arch/riscv64/CodeGen.zig | 2 +- src/arch/x86_64/CodeGen.zig | 148 +++++++++++++++++++++++++++++++---- src/arch/x86_64/Emit.zig | 88 --------------------- src/arch/x86_64/Mir.zig | 22 ++---- src/arch/x86_64/PrintMir.zig | 2 +- src/link/Dwarf.zig | 138 +++++++++++++++++--------------- 7 files changed, 216 insertions(+), 188 deletions(-) diff --git a/src/arch/arm/Emit.zig b/src/arch/arm/Emit.zig index 77fa82d1d2..7c9e508e5f 100644 --- a/src/arch/arm/Emit.zig +++ b/src/arch/arm/Emit.zig @@ -417,7 +417,7 @@ fn genArgDbgInfo(self: *Emit, inst: Air.Inst.Index, arg_index: u32) !void { .dwarf => |dw| { const dbg_info = &dw.dbg_info; try dbg_info.ensureUnusedCapacity(3); - dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter); + dbg_info.appendAssumeCapacity(@enumToInt(link.File.Dwarf.AbbrevKind.parameter)); dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc 1, // ULEB128 dwarf expression length reg.dwarfLocOp(), @@ -449,7 +449,7 @@ fn genArgDbgInfo(self: *Emit, inst: Air.Inst.Index, arg_index: u32) !void { }; const dbg_info = &dw.dbg_info; - try dbg_info.append(link.File.Dwarf.abbrev_parameter); + try dbg_info.append(@enumToInt(link.File.Dwarf.AbbrevKind.parameter)); // Get length of the LEB128 stack offset var counting_writer = std.io.countingWriter(std.io.null_writer); diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index ac12bbceaf..a7d4c872a7 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -1574,7 +1574,7 @@ fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue, arg_index: u32 .dwarf => |dw| { const dbg_info = &dw.dbg_info; try dbg_info.ensureUnusedCapacity(3); - dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter); + dbg_info.appendAssumeCapacity(@enumToInt(link.File.Dwarf.AbbrevKind.parameter)); dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc 1, // ULEB128 dwarf expression length reg.dwarfLocOp(), diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 0a11c1480f..7f5144a6dc 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -48,6 +48,7 @@ gpa: Allocator, air: Air, liveness: Liveness, bin_file: *link.File, +debug_output: DebugInfoOutput, target: *const std.Target, mod_fn: *const Module.Fn, err_msg: ?*ErrorMsg, @@ -337,6 +338,7 @@ pub fn generate( .liveness = liveness, .target = &bin_file.options.target, .bin_file = bin_file, + .debug_output = debug_output, .mod_fn = module_fn, .err_msg = null, .args = undefined, // populated after `resolveCallingConventionValues` @@ -382,7 +384,6 @@ pub fn generate( }; var mir = Mir{ - .function = &function, .instructions = function.mir_instructions.toOwnedSlice(), .extra = function.mir_extra.toOwnedSlice(bin_file.allocator), }; @@ -391,7 +392,6 @@ pub fn generate( var emit = Emit{ .mir = mir, .bin_file = bin_file, - .function = &function, .debug_output = debug_output, .target = &bin_file.options.target, .src_loc = src_loc, @@ -3425,17 +3425,11 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { const arg_index = self.arg_index; self.arg_index += 1; + const ty = self.air.typeOfIndex(inst); const mcv = self.args[arg_index]; - const payload = try self.addExtra(Mir.ArgDbgInfo{ - .air_inst = inst, - .arg_index = arg_index, - .max_stack = self.max_end_stack, - }); - _ = try self.addInst(.{ - .tag = .arg_dbg_info, - .ops = undefined, - .data = .{ .payload = payload }, - }); + const name = self.mod_fn.getParamName(arg_index); + const name_with_null = name.ptr[0 .. name.len + 1]; + if (self.liveness.isUnused(inst)) return self.finishAirBookkeeping(); @@ -3443,10 +3437,46 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { switch (mcv) { .register => |reg| { self.register_manager.getRegAssumeFree(reg.to64(), inst); + switch (self.debug_output) { + .dwarf => |dw| { + const dbg_info = &dw.dbg_info; + try dbg_info.ensureUnusedCapacity(3); + dbg_info.appendAssumeCapacity(@enumToInt(link.File.Dwarf.AbbrevKind.parameter)); + dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc + 1, // ULEB128 dwarf expression length + reg.dwarfLocOp(), + }); + try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); + try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 + dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string + }, + .plan9 => {}, + .none => {}, + } break :blk mcv; }, .stack_offset => |off| { const offset = @intCast(i32, self.max_end_stack) - off + 16; + switch (self.debug_output) { + .dwarf => |dw| { + const dbg_info = &dw.dbg_info; + try dbg_info.ensureUnusedCapacity(8); + dbg_info.appendAssumeCapacity(@enumToInt(link.File.Dwarf.AbbrevKind.parameter)); + const fixup = dbg_info.items.len; + dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc + 1, // we will backpatch it after we encode the displacement in LEB128 + DW.OP.breg6, // .rbp TODO handle -fomit-frame-pointer + }); + leb128.writeILEB128(dbg_info.writer(), offset) catch unreachable; + dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2); + try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); + try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 + dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string + + }, + .plan9 => {}, + .none => {}, + } break :blk MCValue{ .stack_offset = -offset }; }, else => return self.fail("TODO implement arg for {}", .{mcv}), @@ -3885,13 +3915,99 @@ fn airDbgBlock(self: *Self, inst: Air.Inst.Index) !void { fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { const pl_op = self.air.instructions.items(.data)[inst].pl_op; - const name = self.air.nullTerminatedString(pl_op.payload); const operand = pl_op.operand; - // TODO emit debug info for this variable - _ = name; + const ty = self.air.typeOf(operand); + + if (!self.liveness.operandDies(inst, 0)) { + const mcv = try self.resolveInst(operand); + const name = self.air.nullTerminatedString(pl_op.payload); + + const tag = self.air.instructions.items(.tag)[inst]; + switch (tag) { + .dbg_var_ptr => try self.genVarDbgInfo(ty.childType(), mcv, name), + .dbg_var_val => try self.genVarDbgInfo(ty, mcv, name), + else => unreachable, + } + } + return self.finishAir(inst, .dead, .{ operand, .none, .none }); } +fn genVarDbgInfo( + self: *Self, + ty: Type, + mcv: MCValue, + name: [:0]const u8, +) !void { + const name_with_null = name.ptr[0 .. name.len + 1]; + switch (mcv) { + .register => |reg| { + switch (self.debug_output) { + .dwarf => |dw| { + const dbg_info = &dw.dbg_info; + try dbg_info.ensureUnusedCapacity(3); + dbg_info.appendAssumeCapacity(@enumToInt(link.File.Dwarf.AbbrevKind.variable)); + dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc + 1, // ULEB128 dwarf expression length + reg.dwarfLocOp(), + }); + try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); + try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 + dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string + }, + .plan9 => {}, + .none => {}, + } + }, + .ptr_stack_offset, .stack_offset => |off| { + switch (self.debug_output) { + .dwarf => |dw| { + const dbg_info = &dw.dbg_info; + try dbg_info.ensureUnusedCapacity(8); + dbg_info.appendAssumeCapacity(@enumToInt(link.File.Dwarf.AbbrevKind.variable)); + const fixup = dbg_info.items.len; + dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc + 1, // we will backpatch it after we encode the displacement in LEB128 + DW.OP.breg6, // .rbp TODO handle -fomit-frame-pointer + }); + leb128.writeILEB128(dbg_info.writer(), -off) catch unreachable; + dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2); + try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); + try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 + dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string + + }, + .plan9 => {}, + .none => {}, + } + }, + else => { + log.debug("TODO generate debug info for {}", .{mcv}); + }, + } +} + +/// Adds a Type to the .debug_info at the current position. The bytes will be populated later, +/// after codegen for this symbol is done. +fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void { + switch (self.debug_output) { + .dwarf => |dw| { + assert(ty.hasRuntimeBits()); + const dbg_info = &dw.dbg_info; + const index = dbg_info.items.len; + try dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4 + const atom = switch (self.bin_file.tag) { + .elf => &self.mod_fn.owner_decl.link.elf.dbg_info_atom, + .macho => &self.mod_fn.owner_decl.link.macho.dbg_info_atom, + else => unreachable, + }; + try dw.addTypeReloc(atom, ty, @intCast(u32, index), null); + }, + .plan9 => {}, + .none => {}, + } +} + fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !u32 { const abi_size = ty.abiSize(self.target.*); switch (mcv) { @@ -5919,7 +6035,7 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand }); } -fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { +pub fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { // First section of indexes correspond to a set number of constant values. const ref_int = @enumToInt(inst); if (ref_int < Air.Inst.Ref.typed_value_map.len) { diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index 3296339419..6af2c07974 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -30,7 +30,6 @@ const Type = @import("../../type.zig").Type; mir: Mir, bin_file: *link.File, -function: *const CodeGen, debug_output: DebugInfoOutput, target: *const std.Target, err_msg: ?*ErrorMsg = null, @@ -187,7 +186,6 @@ pub fn lowerMir(emit: *Emit) InnerError!void { .dbg_line => try emit.mirDbgLine(inst), .dbg_prologue_end => try emit.mirDbgPrologueEnd(inst), .dbg_epilogue_begin => try emit.mirDbgEpilogueBegin(inst), - .arg_dbg_info => try emit.mirArgDbgInfo(inst), .push_regs_from_callee_preserved_regs => try emit.mirPushPopRegsFromCalleePreservedRegs(.push, inst), .pop_regs_from_callee_preserved_regs => try emit.mirPushPopRegsFromCalleePreservedRegs(.pop, inst), @@ -1057,92 +1055,6 @@ fn mirDbgEpilogueBegin(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { } } -fn mirArgDbgInfo(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const tag = emit.mir.instructions.items(.tag)[inst]; - assert(tag == .arg_dbg_info); - const payload = emit.mir.instructions.items(.data)[inst].payload; - const arg_dbg_info = emit.mir.extraData(Mir.ArgDbgInfo, payload).data; - const mcv = emit.mir.function.args[arg_dbg_info.arg_index]; - try emit.genArgDbgInfo(arg_dbg_info.air_inst, mcv, arg_dbg_info.max_stack, arg_dbg_info.arg_index); -} - -fn genArgDbgInfo(emit: *Emit, inst: Air.Inst.Index, mcv: MCValue, max_stack: u32, arg_index: u32) !void { - const ty = emit.mir.function.air.instructions.items(.data)[inst].ty; - const name = emit.mir.function.mod_fn.getParamName(arg_index); - const name_with_null = name.ptr[0 .. name.len + 1]; - - switch (mcv) { - .register => |reg| { - switch (emit.debug_output) { - .dwarf => |dw| { - const dbg_info = &dw.dbg_info; - try dbg_info.ensureUnusedCapacity(3); - dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter); - dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc - 1, // ULEB128 dwarf expression length - reg.dwarfLocOp(), - }); - try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); - try emit.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 - dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string - }, - .plan9 => {}, - .none => {}, - } - }, - .stack_offset => |off| { - switch (emit.debug_output) { - .dwarf => |dw| { - // we add here +16 like we do in airArg in CodeGen since we refer directly to - // rbp as the start of function frame minus 8 bytes for caller's rbp preserved in the - // prologue, and 8 bytes for return address. - // TODO we need to make this more generic if we don't use rbp as the frame pointer - // for example when -fomit-frame-pointer is set. - const disp = @intCast(i32, max_stack) - off + 16; - const dbg_info = &dw.dbg_info; - try dbg_info.ensureUnusedCapacity(8); - dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter); - const fixup = dbg_info.items.len; - dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc - 1, // we will backpatch it after we encode the displacement in LEB128 - DW.OP.breg6, // .rbp TODO handle -fomit-frame-pointer - }); - leb128.writeILEB128(dbg_info.writer(), disp) catch unreachable; - dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2); - try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); - try emit.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 - dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string - - }, - .plan9 => {}, - .none => {}, - } - }, - else => {}, - } -} - -/// Adds a Type to the .debug_info at the current position. The bytes will be populated later, -/// after codegen for this symbol is done. -fn addDbgInfoTypeReloc(emit: *Emit, ty: Type) !void { - switch (emit.debug_output) { - .dwarf => |dw| { - assert(ty.hasRuntimeBits()); - const dbg_info = &dw.dbg_info; - const index = dbg_info.items.len; - try dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4 - const atom = switch (emit.bin_file.tag) { - .elf => &emit.function.mod_fn.owner_decl.link.elf.dbg_info_atom, - .macho => &emit.function.mod_fn.owner_decl.link.macho.dbg_info_atom, - else => unreachable, - }; - try dw.addTypeReloc(atom, ty, @intCast(u32, index), null); - }, - .plan9 => {}, - .none => {}, - } -} - const Tag = enum { adc, add, diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index 183a76e4b7..a99cd58f06 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -16,7 +16,6 @@ const Air = @import("../../Air.zig"); const CodeGen = @import("CodeGen.zig"); const Register = bits.Register; -function: *const CodeGen, instructions: std.MultiArrayList(Inst).Slice, /// The meaning of this data is determined by `Inst.Tag` value. extra: []const u32, @@ -364,9 +363,6 @@ pub const Inst = struct { /// update debug line dbg_line, - /// arg debug info - arg_dbg_info, - /// push registers from the callee_preserved_regs /// data is the bitfield of which regs to push /// for example on x86_64, the callee_preserved_regs are [_]Register{ .rcx, .rsi, .rdi, .r8, .r9, .r10, .r11 }; }; @@ -453,18 +449,6 @@ pub const DbgLineColumn = struct { column: u32, }; -pub const ArgDbgInfo = struct { - air_inst: Air.Inst.Index, - arg_index: u32, - max_stack: u32, -}; - -pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void { - mir.instructions.deinit(gpa); - gpa.free(mir.extra); - mir.* = undefined; -} - pub const Ops = struct { reg1: Register = .none, reg2: Register = .none, @@ -490,6 +474,12 @@ pub const Ops = struct { } }; +pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void { + mir.instructions.deinit(gpa); + gpa.free(mir.extra); + mir.* = undefined; +} + pub fn extraData(mir: Mir, comptime T: type, index: usize) struct { data: T, end: usize } { const fields = std.meta.fields(T); var i: usize = index; diff --git a/src/arch/x86_64/PrintMir.zig b/src/arch/x86_64/PrintMir.zig index 8c07350c2d..e457d859ea 100644 --- a/src/arch/x86_64/PrintMir.zig +++ b/src/arch/x86_64/PrintMir.zig @@ -147,7 +147,7 @@ pub fn printMir(print: *const Print, w: anytype, mir_to_air_map: std.AutoHashMap .call_extern => try print.mirCallExtern(inst, w), - .dbg_line, .dbg_prologue_end, .dbg_epilogue_begin, .arg_dbg_info => try w.print("{s}\n", .{@tagName(tag)}), + .dbg_line, .dbg_prologue_end, .dbg_epilogue_begin => try w.print("{s}\n", .{@tagName(tag)}), .push_regs_from_callee_preserved_regs => try print.mirPushPopRegsFromCalleePreservedRegs(.push, inst, w), .pop_regs_from_callee_preserved_regs => try print.mirPushPopRegsFromCalleePreservedRegs(.pop, inst, w), diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 907a21b774..4c6a80fe98 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -148,11 +148,11 @@ pub const DeclState = struct { switch (ty.zigTypeTag()) { .NoReturn => unreachable, .Void => { - try dbg_info_buffer.append(abbrev_pad1); + try dbg_info_buffer.append(@enumToInt(AbbrevKind.pad1)); }, .Bool => { try dbg_info_buffer.appendSlice(&[_]u8{ - abbrev_base_type, + @enumToInt(AbbrevKind.base_type), DW.ATE.boolean, // DW.AT.encoding , DW.FORM.data1 1, // DW.AT.byte_size, DW.FORM.data1 'b', 'o', 'o', 'l', 0, // DW.AT.name, DW.FORM.string @@ -161,7 +161,7 @@ pub const DeclState = struct { .Int => { const info = ty.intInfo(target); try dbg_info_buffer.ensureUnusedCapacity(12); - dbg_info_buffer.appendAssumeCapacity(abbrev_base_type); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.base_type)); // DW.AT.encoding, DW.FORM.data1 dbg_info_buffer.appendAssumeCapacity(switch (info.signedness) { .signed => DW.ATE.signed, @@ -175,7 +175,7 @@ pub const DeclState = struct { .Optional => { if (ty.isPtrLikeOptional()) { try dbg_info_buffer.ensureUnusedCapacity(12); - dbg_info_buffer.appendAssumeCapacity(abbrev_base_type); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.base_type)); // DW.AT.encoding, DW.FORM.data1 dbg_info_buffer.appendAssumeCapacity(DW.ATE.address); // DW.AT.byte_size, DW.FORM.data1 @@ -187,7 +187,7 @@ pub const DeclState = struct { var buf = try arena.create(Type.Payload.ElemType); const payload_ty = ty.optionalChild(buf); // DW.AT.structure_type - try dbg_info_buffer.append(abbrev_struct_type); + try dbg_info_buffer.append(@enumToInt(AbbrevKind.struct_type)); // DW.AT.byte_size, DW.FORM.sdata const abi_size = ty.abiSize(target); try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); @@ -195,7 +195,7 @@ pub const DeclState = struct { try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(target)}); // DW.AT.member try dbg_info_buffer.ensureUnusedCapacity(7); - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.struct_member)); // DW.AT.name, DW.FORM.string dbg_info_buffer.appendSliceAssumeCapacity("maybe"); dbg_info_buffer.appendAssumeCapacity(0); @@ -207,7 +207,7 @@ pub const DeclState = struct { try dbg_info_buffer.ensureUnusedCapacity(6); dbg_info_buffer.appendAssumeCapacity(0); // DW.AT.member - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.struct_member)); // DW.AT.name, DW.FORM.string dbg_info_buffer.appendSliceAssumeCapacity("val"); dbg_info_buffer.appendAssumeCapacity(0); @@ -227,14 +227,14 @@ pub const DeclState = struct { // Slices are structs: struct { .ptr = *, .len = N } // DW.AT.structure_type try dbg_info_buffer.ensureUnusedCapacity(2); - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_type); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.struct_type)); // DW.AT.byte_size, DW.FORM.sdata dbg_info_buffer.appendAssumeCapacity(@sizeOf(usize) * 2); // DW.AT.name, DW.FORM.string try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(target)}); // DW.AT.member try dbg_info_buffer.ensureUnusedCapacity(5); - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.struct_member)); // DW.AT.name, DW.FORM.string dbg_info_buffer.appendSliceAssumeCapacity("ptr"); dbg_info_buffer.appendAssumeCapacity(0); @@ -248,7 +248,7 @@ pub const DeclState = struct { try dbg_info_buffer.ensureUnusedCapacity(6); dbg_info_buffer.appendAssumeCapacity(0); // DW.AT.member - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.struct_member)); // DW.AT.name, DW.FORM.string dbg_info_buffer.appendSliceAssumeCapacity("len"); dbg_info_buffer.appendAssumeCapacity(0); @@ -263,7 +263,7 @@ pub const DeclState = struct { dbg_info_buffer.appendAssumeCapacity(0); } else { try dbg_info_buffer.ensureUnusedCapacity(5); - dbg_info_buffer.appendAssumeCapacity(abbrev_ptr_type); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.ptr_type)); // DW.AT.type, DW.FORM.ref4 const index = dbg_info_buffer.items.len; try dbg_info_buffer.resize(index + 4); @@ -272,7 +272,7 @@ pub const DeclState = struct { }, .Struct => blk: { // DW.AT.structure_type - try dbg_info_buffer.append(abbrev_struct_type); + try dbg_info_buffer.append(@enumToInt(AbbrevKind.struct_type)); // DW.AT.byte_size, DW.FORM.sdata const abi_size = ty.abiSize(target); try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); @@ -285,7 +285,7 @@ pub const DeclState = struct { const fields = ty.tupleFields(); for (fields.types) |field, field_index| { // DW.AT.member - try dbg_info_buffer.append(abbrev_struct_member); + try dbg_info_buffer.append(@enumToInt(AbbrevKind.struct_member)); // DW.AT.name, DW.FORM.string try dbg_info_buffer.writer().print("{d}\x00", .{field_index}); // DW.AT.type, DW.FORM.ref4 @@ -315,7 +315,7 @@ pub const DeclState = struct { const field = fields.get(field_name).?; // DW.AT.member try dbg_info_buffer.ensureUnusedCapacity(field_name.len + 2); - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.struct_member)); // DW.AT.name, DW.FORM.string dbg_info_buffer.appendSliceAssumeCapacity(field_name); dbg_info_buffer.appendAssumeCapacity(0); @@ -335,7 +335,7 @@ pub const DeclState = struct { }, .Enum => { // DW.AT.enumeration_type - try dbg_info_buffer.append(abbrev_enum_type); + try dbg_info_buffer.append(@enumToInt(AbbrevKind.enum_type)); // DW.AT.byte_size, DW.FORM.sdata const abi_size = ty.abiSize(target); try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); @@ -355,7 +355,7 @@ pub const DeclState = struct { for (fields.keys()) |field_name, field_i| { // DW.AT.enumerator try dbg_info_buffer.ensureUnusedCapacity(field_name.len + 2 + @sizeOf(u64)); - dbg_info_buffer.appendAssumeCapacity(abbrev_enum_variant); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.enum_variant)); // DW.AT.name, DW.FORM.string dbg_info_buffer.appendSliceAssumeCapacity(field_name); dbg_info_buffer.appendAssumeCapacity(0); @@ -385,7 +385,7 @@ pub const DeclState = struct { // for untagged unions. if (is_tagged) { // DW.AT.structure_type - try dbg_info_buffer.append(abbrev_struct_type); + try dbg_info_buffer.append(@enumToInt(AbbrevKind.struct_type)); // DW.AT.byte_size, DW.FORM.sdata try leb128.writeULEB128(dbg_info_buffer.writer(), layout.abi_size); // DW.AT.name, DW.FORM.string @@ -395,7 +395,7 @@ pub const DeclState = struct { // DW.AT.member try dbg_info_buffer.ensureUnusedCapacity(9); - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.struct_member)); // DW.AT.name, DW.FORM.string dbg_info_buffer.appendSliceAssumeCapacity("payload"); dbg_info_buffer.appendAssumeCapacity(0); @@ -408,7 +408,7 @@ pub const DeclState = struct { } // DW.AT.union_type - try dbg_info_buffer.append(abbrev_union_type); + try dbg_info_buffer.append(@enumToInt(AbbrevKind.union_type)); // DW.AT.byte_size, DW.FORM.sdata, try leb128.writeULEB128(dbg_info_buffer.writer(), layout.payload_size); // DW.AT.name, DW.FORM.string @@ -423,7 +423,7 @@ pub const DeclState = struct { const field = fields.get(field_name).?; if (!field.ty.hasRuntimeBits()) continue; // DW.AT.member - try dbg_info_buffer.append(abbrev_struct_member); + try dbg_info_buffer.append(@enumToInt(AbbrevKind.struct_member)); // DW.AT.name, DW.FORM.string try dbg_info_buffer.writer().print("{s}\x00", .{field_name}); // DW.AT.type, DW.FORM.ref4 @@ -439,7 +439,7 @@ pub const DeclState = struct { if (is_tagged) { // DW.AT.member try dbg_info_buffer.ensureUnusedCapacity(5); - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.struct_member)); // DW.AT.name, DW.FORM.string dbg_info_buffer.appendSliceAssumeCapacity("tag"); dbg_info_buffer.appendAssumeCapacity(0); @@ -471,7 +471,7 @@ pub const DeclState = struct { const payload_off = mem.alignForwardGeneric(u64, error_ty.abiSize(target), abi_align); // DW.AT.structure_type - try dbg_info_buffer.append(abbrev_struct_type); + try dbg_info_buffer.append(@enumToInt(AbbrevKind.struct_type)); // DW.AT.byte_size, DW.FORM.sdata try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); // DW.AT.name, DW.FORM.string @@ -480,7 +480,7 @@ pub const DeclState = struct { // DW.AT.member try dbg_info_buffer.ensureUnusedCapacity(7); - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.struct_member)); // DW.AT.name, DW.FORM.string dbg_info_buffer.appendSliceAssumeCapacity("value"); dbg_info_buffer.appendAssumeCapacity(0); @@ -493,7 +493,7 @@ pub const DeclState = struct { // DW.AT.member try dbg_info_buffer.ensureUnusedCapacity(5); - dbg_info_buffer.appendAssumeCapacity(abbrev_struct_member); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.struct_member)); // DW.AT.name, DW.FORM.string dbg_info_buffer.appendSliceAssumeCapacity("err"); dbg_info_buffer.appendAssumeCapacity(0); @@ -509,7 +509,7 @@ pub const DeclState = struct { }, else => { log.debug("TODO implement .debug_info for type '{}'", .{ty.fmtDebug()}); - try dbg_info_buffer.append(abbrev_pad1); + try dbg_info_buffer.append(@enumToInt(AbbrevKind.pad1)); }, } } @@ -550,18 +550,21 @@ pub const SrcFn = struct { pub const PtrWidth = enum { p32, p64 }; -pub const abbrev_compile_unit = 1; -pub const abbrev_subprogram = 2; -pub const abbrev_subprogram_retvoid = 3; -pub const abbrev_base_type = 4; -pub const abbrev_ptr_type = 5; -pub const abbrev_struct_type = 6; -pub const abbrev_struct_member = 7; -pub const abbrev_enum_type = 8; -pub const abbrev_enum_variant = 9; -pub const abbrev_union_type = 10; -pub const abbrev_pad1 = 11; -pub const abbrev_parameter = 12; +pub const AbbrevKind = enum(u8) { + compile_unit = 1, + subprogram, + subprogram_retvoid, + base_type, + ptr_type, + struct_type, + struct_member, + enum_type, + enum_variant, + union_type, + pad1, + parameter, + variable, +}; /// The reloc offset for the virtual address of a function in its Line Number Program. /// Size is a virtual address integer. @@ -670,9 +673,9 @@ pub fn initDeclState(self: *Dwarf, decl: *Module.Decl) !DeclState { const fn_ret_type = decl.ty.fnReturnType(); const fn_ret_has_bits = fn_ret_type.hasRuntimeBits(); if (fn_ret_has_bits) { - dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.subprogram)); } else { - dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram_retvoid); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.subprogram_retvoid)); } // These get overwritten after generating the machine code. These values are // "relocations" and have to be in this fixed place so that functions can be @@ -926,7 +929,7 @@ pub fn commitDeclState( else => unreachable, }; - { + if (decl_state.abbrev_table.items.len > 0) { // Now we emit the .debug_info types of the Decl. These will count towards the size of // the buffer, so we have to do it before computing the offset, and we can't perform the actual // relocations yet. @@ -1244,14 +1247,14 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void { // These are LEB encoded but since the values are all less than 127 // we can simply append these bytes. const abbrev_buf = [_]u8{ - abbrev_compile_unit, DW.TAG.compile_unit, DW.CHILDREN.yes, // header - DW.AT.stmt_list, DW.FORM.sec_offset, DW.AT.low_pc, - DW.FORM.addr, DW.AT.high_pc, DW.FORM.addr, - DW.AT.name, DW.FORM.strp, DW.AT.comp_dir, - DW.FORM.strp, DW.AT.producer, DW.FORM.strp, - DW.AT.language, DW.FORM.data2, 0, + @enumToInt(AbbrevKind.compile_unit), DW.TAG.compile_unit, DW.CHILDREN.yes, // header + DW.AT.stmt_list, DW.FORM.sec_offset, DW.AT.low_pc, + DW.FORM.addr, DW.AT.high_pc, DW.FORM.addr, + DW.AT.name, DW.FORM.strp, DW.AT.comp_dir, + DW.FORM.strp, DW.AT.producer, DW.FORM.strp, + DW.AT.language, DW.FORM.data2, 0, 0, // table sentinel - abbrev_subprogram, + @enumToInt(AbbrevKind.subprogram), DW.TAG.subprogram, DW.CHILDREN.yes, // header DW.AT.low_pc, @@ -1262,15 +1265,15 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void { DW.FORM.ref4, DW.AT.name, DW.FORM.string, - 0, 0, // table sentinel - abbrev_subprogram_retvoid, + 0, 0, // table sentinel + @enumToInt(AbbrevKind.subprogram_retvoid), DW.TAG.subprogram, DW.CHILDREN.yes, // header DW.AT.low_pc, DW.FORM.addr, DW.AT.high_pc, DW.FORM.data4, DW.AT.name, DW.FORM.string, 0, 0, // table sentinel - abbrev_base_type, + @enumToInt(AbbrevKind.base_type), DW.TAG.base_type, DW.CHILDREN.no, // header DW.AT.encoding, @@ -1281,14 +1284,14 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void { DW.FORM.string, 0, 0, // table sentinel - abbrev_ptr_type, + @enumToInt(AbbrevKind.ptr_type), DW.TAG.pointer_type, DW.CHILDREN.no, // header DW.AT.type, DW.FORM.ref4, 0, 0, // table sentinel - abbrev_struct_type, + @enumToInt(AbbrevKind.struct_type), DW.TAG.structure_type, DW.CHILDREN.yes, // header DW.AT.byte_size, @@ -1297,7 +1300,7 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void { DW.FORM.string, 0, 0, // table sentinel - abbrev_struct_member, + @enumToInt(AbbrevKind.struct_member), DW.TAG.member, DW.CHILDREN.no, // header DW.AT.name, @@ -1308,7 +1311,7 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void { DW.FORM.sdata, 0, 0, // table sentinel - abbrev_enum_type, + @enumToInt(AbbrevKind.enum_type), DW.TAG.enumeration_type, DW.CHILDREN.yes, // header DW.AT.byte_size, @@ -1317,7 +1320,7 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void { DW.FORM.string, 0, 0, // table sentinel - abbrev_enum_variant, + @enumToInt(AbbrevKind.enum_variant), DW.TAG.enumerator, DW.CHILDREN.no, // header DW.AT.name, @@ -1326,7 +1329,7 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void { DW.FORM.data8, 0, 0, // table sentinel - abbrev_union_type, + @enumToInt(AbbrevKind.union_type), DW.TAG.union_type, DW.CHILDREN.yes, // header DW.AT.byte_size, @@ -1335,18 +1338,25 @@ pub fn writeDbgAbbrev(self: *Dwarf, file: *File) !void { DW.FORM.string, 0, 0, // table sentinel - abbrev_pad1, + @enumToInt(AbbrevKind.pad1), DW.TAG.unspecified_type, DW.CHILDREN.no, // header 0, 0, // table sentinel - abbrev_parameter, + @enumToInt(AbbrevKind.parameter), DW.TAG.formal_parameter, DW.CHILDREN.no, // header DW.AT.location, DW.FORM.exprloc, DW.AT.type, DW.FORM.ref4, DW.AT.name, DW.FORM.string, 0, 0, // table sentinel + @enumToInt(AbbrevKind.variable), + DW.TAG.variable, DW.CHILDREN.no, // header + DW.AT.location, DW.FORM.exprloc, + DW.AT.type, DW.FORM.ref4, + DW.AT.name, DW.FORM.string, + 0, + 0, // table sentinel 0, 0, 0, // section sentinel @@ -1459,7 +1469,7 @@ pub fn writeDbgInfoHeader(self: *Dwarf, file: *File, module: *Module, low_pc: u6 const comp_dir_strp = try self.makeString(module.root_pkg.root_src_directory.path orelse "."); const producer_strp = try self.makeString(link.producer_string); - di_buf.appendAssumeCapacity(abbrev_compile_unit); + di_buf.appendAssumeCapacity(@enumToInt(AbbrevKind.compile_unit)); if (self.tag == .macho) { mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), 0); // DW.AT.stmt_list, DW.FORM.sec_offset mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), low_pc); @@ -1606,7 +1616,7 @@ fn pwriteDbgInfoNops( const tracy = trace(@src()); defer tracy.end(); - const page_of_nops = [1]u8{abbrev_pad1} ** 4096; + const page_of_nops = [1]u8{@enumToInt(AbbrevKind.pad1)} ** 4096; var vecs: [32]std.os.iovec_const = undefined; var vec_index: usize = 0; { @@ -1673,7 +1683,7 @@ pub fn writeDbgAranges(self: *Dwarf, file: *File, addr: u64, size: u64) !void { .p32 => @as(usize, 4), .p64 => 12, }; - const ptr_width_bytes: u8 = self.ptrWidthBytes(); + const ptr_width_bytes = self.ptrWidthBytes(); // Enough for all the data without resizing. When support for more compilation units // is added, the size of this section will become more variable. @@ -2040,7 +2050,7 @@ fn addDbgInfoErrorSet( const target_endian = target.cpu.arch.endian(); // DW.AT.enumeration_type - try dbg_info_buffer.append(abbrev_enum_type); + try dbg_info_buffer.append(@enumToInt(AbbrevKind.enum_type)); // DW.AT.byte_size, DW.FORM.sdata const abi_size = ty.abiSize(target); try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size); @@ -2051,7 +2061,7 @@ fn addDbgInfoErrorSet( // DW.AT.enumerator const no_error = "(no error)"; try dbg_info_buffer.ensureUnusedCapacity(no_error.len + 2 + @sizeOf(u64)); - dbg_info_buffer.appendAssumeCapacity(abbrev_enum_variant); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.enum_variant)); // DW.AT.name, DW.FORM.string dbg_info_buffer.appendSliceAssumeCapacity(no_error); dbg_info_buffer.appendAssumeCapacity(0); @@ -2063,7 +2073,7 @@ fn addDbgInfoErrorSet( const kv = module.getErrorValue(error_name) catch unreachable; // DW.AT.enumerator try dbg_info_buffer.ensureUnusedCapacity(error_name.len + 2 + @sizeOf(u64)); - dbg_info_buffer.appendAssumeCapacity(abbrev_enum_variant); + dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.enum_variant)); // DW.AT.name, DW.FORM.string dbg_info_buffer.appendSliceAssumeCapacity(error_name); dbg_info_buffer.appendAssumeCapacity(0);