Dwarf: improve x86_64 backend debug info

Closes #17811
This commit is contained in:
Jacob Young
2023-11-11 16:06:01 -05:00
committed by Andrew Kelley
parent 3fc6a2f113
commit 2eeb735822
6 changed files with 432 additions and 287 deletions

View File

@@ -148,10 +148,10 @@ pub fn writeUnsignedFixed(comptime l: usize, ptr: *[l]u8, int: std.meta.Int(.uns
value >>= 7;
ptr[i] = byte;
}
ptr[i] = @as(u8, @truncate(value));
ptr[i] = @truncate(value);
}
test "writeUnsignedFixed" {
test writeUnsignedFixed {
{
var buf: [4]u8 = undefined;
writeUnsignedFixed(4, &buf, 0);
@@ -174,6 +174,65 @@ test "writeUnsignedFixed" {
}
}
/// This is an "advanced" function. It allows one to use a fixed amount of memory to store an
/// ILEB128. This defeats the entire purpose of using this data encoding; it will no longer use
/// fewer bytes to store smaller numbers. The advantage of using a fixed width is that it makes
/// fields have a predictable size and so depending on the use case this tradeoff can be worthwhile.
/// An example use case of this is in emitting DWARF info where one wants to make a ILEB128 field
/// "relocatable", meaning that it becomes possible to later go back and patch the number to be a
/// different value without shifting all the following code.
pub fn writeSignedFixed(comptime l: usize, ptr: *[l]u8, int: std.meta.Int(.signed, l * 7)) void {
const T = @TypeOf(int);
const U = if (@typeInfo(T).Int.bits < 8) u8 else T;
var value: U = @intCast(int);
comptime var i = 0;
inline while (i < (l - 1)) : (i += 1) {
const byte: u8 = @bitCast(@as(i8, @truncate(value)) | -0b1000_0000);
value >>= 7;
ptr[i] = byte;
}
ptr[i] = @as(u7, @bitCast(@as(i7, @truncate(value))));
}
test writeSignedFixed {
{
var buf: [4]u8 = undefined;
writeSignedFixed(4, &buf, 0);
try testing.expect((try test_read_ileb128(i64, &buf)) == 0);
}
{
var buf: [4]u8 = undefined;
writeSignedFixed(4, &buf, 1);
try testing.expect((try test_read_ileb128(i64, &buf)) == 1);
}
{
var buf: [4]u8 = undefined;
writeSignedFixed(4, &buf, -1);
try testing.expect((try test_read_ileb128(i64, &buf)) == -1);
}
{
var buf: [4]u8 = undefined;
writeSignedFixed(4, &buf, 1000);
try testing.expect((try test_read_ileb128(i64, &buf)) == 1000);
}
{
var buf: [4]u8 = undefined;
writeSignedFixed(4, &buf, -1000);
try testing.expect((try test_read_ileb128(i64, &buf)) == -1000);
}
{
var buf: [4]u8 = undefined;
writeSignedFixed(4, &buf, -10000000);
try testing.expect((try test_read_ileb128(i64, &buf)) == -10000000);
}
{
var buf: [4]u8 = undefined;
writeSignedFixed(4, &buf, 10000000);
try testing.expect((try test_read_ileb128(i64, &buf)) == 10000000);
}
}
// tests
fn test_read_stream_ileb128(comptime T: type, encoded: []const u8) !T {
var reader = std.io.fixedBufferStream(encoded);

View File

@@ -10576,12 +10576,12 @@ fn genVarDbgInfo(
fn airTrap(self: *Self) !void {
try self.asmOpOnly(.{ ._, .ud2 });
return self.finishAirBookkeeping();
self.finishAirBookkeeping();
}
fn airBreakpoint(self: *Self) !void {
try self.asmOpOnly(.{ ._, .int3 });
return self.finishAirBookkeeping();
self.finishAirBookkeeping();
}
fn airRetAddr(self: *Self, inst: Air.Inst.Index) !void {
@@ -10603,7 +10603,7 @@ fn airFence(self: *Self, inst: Air.Inst.Index) !void {
.Acquire, .Release, .AcqRel => {},
.SeqCst => try self.asmOpOnly(.{ ._, .mfence }),
}
return self.finishAirBookkeeping();
self.finishAirBookkeeping();
}
fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void {
@@ -11419,21 +11419,23 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
.column = dbg_stmt.column,
} },
});
return self.finishAirBookkeeping();
self.finishAirBookkeeping();
}
fn airDbgInline(self: *Self, inst: Air.Inst.Index) !void {
const mod = self.bin_file.options.module.?;
const ty_fn = self.air.instructions.items(.data)[inst].ty_fn;
const func = mod.funcInfo(ty_fn.func);
// TODO emit debug info for function change
_ = func;
return self.finishAir(inst, .unreach, .{ .none, .none, .none });
_ = try self.addInst(.{
.tag = .pseudo,
.ops = .pseudo_dbg_inline_func,
.data = .{ .func = ty_fn.func },
});
self.finishAirBookkeeping();
}
fn airDbgBlock(self: *Self, inst: Air.Inst.Index) !void {
_ = inst;
// TODO emit debug info lexical block
return self.finishAir(inst, .unreach, .{ .none, .none, .none });
self.finishAirBookkeeping();
}
fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
@@ -11518,9 +11520,8 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
.close_scope = true,
});
// We already took care of pl_op.operand earlier, so we're going
// to pass .none here
return self.finishAir(inst, .unreach, .{ .none, .none, .none });
// We already took care of pl_op.operand earlier, so there's nothing left to do.
self.finishAirBookkeeping();
}
fn isNull(self: *Self, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) !MCValue {
@@ -11865,7 +11866,7 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void {
});
_ = try self.asmJmpReloc(jmp_target);
return self.finishAirBookkeeping();
self.finishAirBookkeeping();
}
fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
@@ -11977,8 +11978,8 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) !void {
});
}
// We already took care of pl_op.operand earlier, so we're going to pass .none here
return self.finishAir(inst, .unreach, .{ .none, .none, .none });
// We already took care of pl_op.operand earlier, so there's nothing left to do
self.finishAirBookkeeping();
}
fn performReloc(self: *Self, reloc: Mir.Inst.Index) !void {

View File

@@ -151,7 +151,7 @@ pub fn emitMir(emit: *Emit) Error!void {
else => unreachable,
},
.target = target,
.offset = @as(u32, @intCast(end_offset - 4)),
.offset = @intCast(end_offset - 4),
.addend = 0,
.pcrel = true,
.length = 2,
@@ -173,7 +173,7 @@ pub fn emitMir(emit: *Emit) Error!void {
else => unreachable,
},
.target = target,
.offset = @as(u32, @intCast(end_offset - 4)),
.offset = @intCast(end_offset - 4),
.addend = 0,
.pcrel = true,
.length = 2,
@@ -182,7 +182,7 @@ pub fn emitMir(emit: *Emit) Error!void {
const atom_index = symbol.atom_index;
try p9_file.addReloc(atom_index, .{ // TODO we may need to add a .type field to the relocs if they are .linker_got instead of just .linker_direct
.target = symbol.sym_index, // we set sym_index to just be the atom index
.offset = @as(u32, @intCast(end_offset - 4)),
.offset = @intCast(end_offset - 4),
.addend = 0,
.type = .pcrel,
});
@@ -229,6 +229,18 @@ pub fn emitMir(emit: *Emit) Error!void {
.none => {},
}
},
.pseudo_dbg_inline_func => {
switch (emit.debug_output) {
.dwarf => |dw| {
log.debug("mirDbgInline (line={d}, col={d})", .{
emit.prev_di_line, emit.prev_di_column,
});
try dw.setInlineFunc(mir_inst.data.func);
},
.plan9 => {},
.none => {},
}
},
.pseudo_dead_none => {},
},
}
@@ -269,17 +281,18 @@ fn fixupRelocs(emit: *Emit) Error!void {
for (emit.relocs.items) |reloc| {
const target = emit.code_offset_mapping.get(reloc.target) orelse
return emit.fail("JMP/CALL relocation target not found!", .{});
const disp = @as(i32, @intCast(@as(i64, @intCast(target)) - @as(i64, @intCast(reloc.source + reloc.length))));
mem.writeInt(i32, emit.code.items[reloc.offset..][0..4], disp, .little);
const disp = @as(i64, @intCast(target)) - @as(i64, @intCast(reloc.source + reloc.length));
mem.writeInt(i32, emit.code.items[reloc.offset..][0..4], @intCast(disp), .little);
}
}
fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) Error!void {
const delta_line = @as(i32, @intCast(line)) - @as(i32, @intCast(emit.prev_di_line));
const delta_line = @as(i33, line) - @as(i33, emit.prev_di_line);
const delta_pc: usize = emit.code.items.len - emit.prev_di_pc;
log.debug(" (advance pc={d} and line={d})", .{ delta_line, delta_pc });
switch (emit.debug_output) {
.dwarf => |dw| {
if (column != emit.prev_di_column) try dw.setColumn(column);
try dw.advancePCAndLine(delta_line, delta_pc);
emit.prev_di_line = line;
emit.prev_di_column = column;
@@ -289,7 +302,7 @@ fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) Error!void {
if (delta_pc <= 0) return; // only do this when the pc changes
// increasing the line number
try link.File.Plan9.changeLine(&dbg_out.dbg_line, delta_line);
try link.File.Plan9.changeLine(&dbg_out.dbg_line, @intCast(delta_line));
// increasing the pc
const d_pc_p9 = @as(i64, @intCast(delta_pc)) - dbg_out.pc_quanta;
if (d_pc_p9 > 0) {
@@ -297,16 +310,16 @@ fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) Error!void {
var diff = @divExact(d_pc_p9, dbg_out.pc_quanta) - dbg_out.pc_quanta;
while (diff > 0) {
if (diff < 64) {
try dbg_out.dbg_line.append(@as(u8, @intCast(diff + 128)));
try dbg_out.dbg_line.append(@intCast(diff + 128));
diff = 0;
} else {
try dbg_out.dbg_line.append(@as(u8, @intCast(64 + 128)));
try dbg_out.dbg_line.append(@intCast(64 + 128));
diff -= 64;
}
}
if (dbg_out.pcop_change_index) |pci|
dbg_out.dbg_line.items[pci] += 1;
dbg_out.pcop_change_index = @as(u32, @intCast(dbg_out.dbg_line.items.len - 1));
dbg_out.pcop_change_index = @intCast(dbg_out.dbg_line.items.len - 1);
} else if (d_pc_p9 == 0) {
// we don't need to do anything, because adding the pc quanta does it for us
} else unreachable;

View File

@@ -259,6 +259,7 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
.pseudo_dbg_prologue_end_none,
.pseudo_dbg_line_line_column,
.pseudo_dbg_epilogue_begin_none,
.pseudo_dbg_inline_func,
.pseudo_dead_none,
=> {},
else => unreachable,

View File

@@ -6,19 +6,6 @@
//! The main purpose of MIR is to postpone the assignment of offsets until Isel,
//! so that, for example, the smaller encodings of jump instructions can be used.
const Mir = @This();
const std = @import("std");
const builtin = @import("builtin");
const assert = std.debug.assert;
const bits = @import("bits.zig");
const encoder = @import("encoder.zig");
const Air = @import("../../Air.zig");
const CodeGen = @import("CodeGen.zig");
const IntegerBitSet = std.bit_set.IntegerBitSet;
const Register = bits.Register;
instructions: std.MultiArrayList(Inst).Slice,
/// The meaning of this data is determined by `Inst.Tag` value.
extra: []const u32,
@@ -884,6 +871,8 @@ pub const Inst = struct {
pseudo_dbg_line_line_column,
/// Start of epilogue
pseudo_dbg_epilogue_begin_none,
/// Start or end of inline function
pseudo_dbg_inline_func,
/// Tombstone
/// Emitter should skip this instruction.
@@ -987,6 +976,7 @@ pub const Inst = struct {
line: u32,
column: u32,
},
func: InternPool.Index,
/// Register list
reg_list: RegisterList,
};
@@ -1198,3 +1188,14 @@ pub fn resolveFrameLoc(mir: Mir, mem: Memory) Memory {
} else mem,
};
}
const assert = std.debug.assert;
const bits = @import("bits.zig");
const builtin = @import("builtin");
const encoder = @import("encoder.zig");
const std = @import("std");
const IntegerBitSet = std.bit_set.IntegerBitSet;
const InternPool = @import("../../InternPool.zig");
const Mir = @This();
const Register = bits.Register;

File diff suppressed because it is too large Load Diff