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.
This commit is contained in:
Jakub Konka 2022-03-30 18:29:44 +02:00
parent 795f075790
commit 364e53f3bf
7 changed files with 216 additions and 188 deletions

View File

@ -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);

View File

@ -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(),

View File

@ -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) {

View File

@ -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,

View File

@ -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;

View File

@ -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),

View File

@ -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);