mirror of
https://github.com/ziglang/zig.git
synced 2026-02-09 19:10:48 +00:00
aarch64: emit DWARF debug info for fn params and locals
We postpone emitting debug info until *after* we generate the function so that we have an idea of the consumed stack space. The stack offsets encoded within DWARF are with respect to the frame pointer `.fp`.
This commit is contained in:
parent
7007ecdc05
commit
02852098ee
@ -51,13 +51,14 @@ 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,
|
||||
args: []MCValue,
|
||||
ret_mcv: MCValue,
|
||||
fn_type: Type,
|
||||
arg_index: usize,
|
||||
arg_index: u32,
|
||||
src_loc: Module.SrcLoc,
|
||||
stack_align: u32,
|
||||
|
||||
@ -75,6 +76,12 @@ end_di_column: u32,
|
||||
/// which is a relative jump, based on the address following the reloc.
|
||||
exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .{},
|
||||
|
||||
/// We postpone the creation of debug info for function args and locals
|
||||
/// until after all Mir instructions have been generated. Only then we
|
||||
/// will know saved_regs_stack_space which is necessary in order to
|
||||
/// calculate the right stack offsest with respect to the `.fp` register.
|
||||
dbg_info_relocs: std.ArrayListUnmanaged(DbgInfoReloc) = .{},
|
||||
|
||||
/// Whenever there is a runtime branch, we push a Branch onto this stack,
|
||||
/// and pop it off when the runtime branch joins. This provides an "overlay"
|
||||
/// of the table of mappings from instructions to `MCValue` from within the branch.
|
||||
@ -160,6 +167,213 @@ const MCValue = union(enum) {
|
||||
stack_argument_offset: u32,
|
||||
};
|
||||
|
||||
const DbgInfoReloc = struct {
|
||||
tag: Air.Inst.Tag,
|
||||
ty: Type,
|
||||
name: [:0]const u8,
|
||||
mcv: MCValue,
|
||||
|
||||
fn genDbgInfo(reloc: DbgInfoReloc, function: Self) !void {
|
||||
switch (reloc.tag) {
|
||||
.arg => try reloc.genArgDbgInfo(function),
|
||||
|
||||
.dbg_var_ptr,
|
||||
.dbg_var_val,
|
||||
=> try reloc.genVarDbgInfo(function),
|
||||
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
fn genArgDbgInfo(reloc: DbgInfoReloc, function: Self) error{OutOfMemory}!void {
|
||||
const name_with_null = reloc.name.ptr[0 .. reloc.name.len + 1];
|
||||
|
||||
switch (function.debug_output) {
|
||||
.dwarf => |dw| {
|
||||
const dbg_info = &dw.dbg_info;
|
||||
switch (reloc.mcv) {
|
||||
.register => |reg| {
|
||||
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 function.addDbgInfoTypeReloc(reloc.ty); // DW.AT.type, DW.FORM.ref4
|
||||
dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string
|
||||
},
|
||||
|
||||
.stack_offset,
|
||||
.stack_argument_offset,
|
||||
=> |offset| {
|
||||
const adjusted_offset = switch (reloc.mcv) {
|
||||
.stack_offset => -@intCast(i32, offset),
|
||||
.stack_argument_offset => @intCast(i32, function.saved_regs_stack_space + offset),
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
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
|
||||
Register.x29.dwarfLocOpDeref(), // frame pointer
|
||||
});
|
||||
leb128.writeILEB128(dbg_info.writer(), adjusted_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 function.addDbgInfoTypeReloc(reloc.ty); // DW.AT.type, DW.FORM.ref4
|
||||
dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string
|
||||
|
||||
},
|
||||
|
||||
else => unreachable, // not a possible argument
|
||||
}
|
||||
},
|
||||
.plan9 => {},
|
||||
.none => {},
|
||||
}
|
||||
}
|
||||
|
||||
fn genVarDbgInfo(reloc: DbgInfoReloc, function: Self) !void {
|
||||
const name_with_null = reloc.name.ptr[0 .. reloc.name.len + 1];
|
||||
const ty = switch (reloc.tag) {
|
||||
.dbg_var_ptr => reloc.ty.childType(),
|
||||
.dbg_var_val => reloc.ty,
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
switch (function.debug_output) {
|
||||
.dwarf => |dw| {
|
||||
const dbg_info = &dw.dbg_info;
|
||||
try dbg_info.append(@enumToInt(link.File.Dwarf.AbbrevKind.variable));
|
||||
const endian = function.target.cpu.arch.endian();
|
||||
|
||||
switch (reloc.mcv) {
|
||||
.register => |reg| {
|
||||
try dbg_info.ensureUnusedCapacity(2);
|
||||
dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
|
||||
1, // ULEB128 dwarf expression length
|
||||
reg.dwarfLocOp(),
|
||||
});
|
||||
},
|
||||
|
||||
.ptr_stack_offset,
|
||||
.stack_offset,
|
||||
=> |off| {
|
||||
try dbg_info.ensureUnusedCapacity(7);
|
||||
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
|
||||
Register.x29.dwarfLocOpDeref(), // frame pointer
|
||||
});
|
||||
leb128.writeILEB128(dbg_info.writer(), -@intCast(i32, off)) catch unreachable;
|
||||
dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2);
|
||||
},
|
||||
|
||||
.memory,
|
||||
.linker_load,
|
||||
=> {
|
||||
const ptr_width = @intCast(u8, @divExact(function.target.cpu.arch.ptrBitWidth(), 8));
|
||||
const is_ptr = switch (reloc.tag) {
|
||||
.dbg_var_ptr => true,
|
||||
.dbg_var_val => false,
|
||||
else => unreachable,
|
||||
};
|
||||
try dbg_info.ensureUnusedCapacity(2 + ptr_width);
|
||||
dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
|
||||
1 + ptr_width + @boolToInt(is_ptr),
|
||||
DW.OP.addr, // literal address
|
||||
});
|
||||
const offset = @intCast(u32, dbg_info.items.len);
|
||||
const addr = switch (reloc.mcv) {
|
||||
.memory => |addr| addr,
|
||||
else => 0,
|
||||
};
|
||||
switch (ptr_width) {
|
||||
0...4 => {
|
||||
try dbg_info.writer().writeInt(u32, @intCast(u32, addr), endian);
|
||||
},
|
||||
5...8 => {
|
||||
try dbg_info.writer().writeInt(u64, addr, endian);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
if (is_ptr) {
|
||||
// We need deref the address as we point to the value via GOT entry.
|
||||
try dbg_info.append(DW.OP.deref);
|
||||
}
|
||||
switch (reloc.mcv) {
|
||||
.linker_load => |load_struct| try dw.addExprlocReloc(
|
||||
load_struct.sym_index,
|
||||
offset,
|
||||
is_ptr,
|
||||
),
|
||||
else => {},
|
||||
}
|
||||
},
|
||||
|
||||
.immediate => |x| {
|
||||
try dbg_info.ensureUnusedCapacity(2);
|
||||
const fixup = dbg_info.items.len;
|
||||
dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
|
||||
1,
|
||||
if (ty.isSignedInt()) DW.OP.consts else DW.OP.constu,
|
||||
});
|
||||
if (ty.isSignedInt()) {
|
||||
try leb128.writeILEB128(dbg_info.writer(), @bitCast(i64, x));
|
||||
} else {
|
||||
try leb128.writeULEB128(dbg_info.writer(), x);
|
||||
}
|
||||
try dbg_info.append(DW.OP.stack_value);
|
||||
dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2);
|
||||
},
|
||||
|
||||
.undef => {
|
||||
// DW.AT.location, DW.FORM.exprloc
|
||||
// uleb128(exprloc_len)
|
||||
// DW.OP.implicit_value uleb128(len_of_bytes) bytes
|
||||
const abi_size = @intCast(u32, ty.abiSize(function.target.*));
|
||||
var implicit_value_len = std.ArrayList(u8).init(function.gpa);
|
||||
defer implicit_value_len.deinit();
|
||||
try leb128.writeULEB128(implicit_value_len.writer(), abi_size);
|
||||
const total_exprloc_len = 1 + implicit_value_len.items.len + abi_size;
|
||||
try leb128.writeULEB128(dbg_info.writer(), total_exprloc_len);
|
||||
try dbg_info.ensureUnusedCapacity(total_exprloc_len);
|
||||
dbg_info.appendAssumeCapacity(DW.OP.implicit_value);
|
||||
dbg_info.appendSliceAssumeCapacity(implicit_value_len.items);
|
||||
dbg_info.appendNTimesAssumeCapacity(0xaa, abi_size);
|
||||
},
|
||||
|
||||
.none => {
|
||||
try dbg_info.ensureUnusedCapacity(3);
|
||||
dbg_info.appendSliceAssumeCapacity(&[3]u8{ // DW.AT.location, DW.FORM.exprloc
|
||||
2, DW.OP.lit0, DW.OP.stack_value,
|
||||
});
|
||||
},
|
||||
|
||||
.stack_argument_offset => unreachable,
|
||||
|
||||
else => {
|
||||
try dbg_info.ensureUnusedCapacity(2);
|
||||
dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
|
||||
1, DW.OP.nop,
|
||||
});
|
||||
log.debug("TODO generate debug info for {}", .{reloc.mcv});
|
||||
},
|
||||
}
|
||||
|
||||
try dbg_info.ensureUnusedCapacity(5 + name_with_null.len);
|
||||
try function.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4
|
||||
dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string
|
||||
},
|
||||
.plan9 => {},
|
||||
.none => {},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const Branch = struct {
|
||||
inst_table: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, MCValue) = .{},
|
||||
|
||||
@ -262,6 +476,7 @@ pub fn generate(
|
||||
.gpa = bin_file.allocator,
|
||||
.air = air,
|
||||
.liveness = liveness,
|
||||
.debug_output = debug_output,
|
||||
.target = &bin_file.options.target,
|
||||
.bin_file = bin_file,
|
||||
.mod_fn = module_fn,
|
||||
@ -279,6 +494,7 @@ pub fn generate(
|
||||
defer function.stack.deinit(bin_file.allocator);
|
||||
defer function.blocks.deinit(bin_file.allocator);
|
||||
defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
|
||||
defer function.dbg_info_relocs.deinit(bin_file.allocator);
|
||||
|
||||
var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {
|
||||
error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
|
||||
@ -302,6 +518,10 @@ pub fn generate(
|
||||
else => |e| return e,
|
||||
};
|
||||
|
||||
for (function.dbg_info_relocs.items) |reloc| {
|
||||
try reloc.genDbgInfo(function);
|
||||
}
|
||||
|
||||
var mir = Mir{
|
||||
.instructions = function.mir_instructions.toOwnedSlice(),
|
||||
.extra = function.mir_extra.toOwnedSlice(bin_file.allocator),
|
||||
@ -854,23 +1074,20 @@ fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {
|
||||
|
||||
/// 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 {
|
||||
fn addDbgInfoTypeReloc(self: Self, ty: Type) !void {
|
||||
switch (self.debug_output) {
|
||||
.dwarf => |dbg_out| {
|
||||
assert(ty.hasRuntimeBits());
|
||||
const index = dbg_out.dbg_info.items.len;
|
||||
try dbg_out.dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4
|
||||
|
||||
const gop = try dbg_out.dbg_info_type_relocs.getOrPutContext(self.gpa, ty, .{
|
||||
.target = self.target.*,
|
||||
});
|
||||
if (!gop.found_existing) {
|
||||
gop.value_ptr.* = .{
|
||||
.off = undefined,
|
||||
.relocs = .{},
|
||||
};
|
||||
}
|
||||
try gop.value_ptr.relocs.append(self.gpa, @intCast(u32, index));
|
||||
.dwarf => |dw| {
|
||||
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 mod = self.bin_file.options.module.?;
|
||||
const fn_owner_decl = mod.declPtr(self.mod_fn.owner_decl);
|
||||
const atom = switch (self.bin_file.tag) {
|
||||
.elf => &fn_owner_decl.link.elf.dbg_info_atom,
|
||||
.macho => &fn_owner_decl.link.macho.dbg_info_atom,
|
||||
else => unreachable,
|
||||
};
|
||||
try dw.addTypeRelocGlobal(atom, ty, @intCast(u32, index));
|
||||
},
|
||||
.plan9 => {},
|
||||
.none => {},
|
||||
@ -3872,8 +4089,9 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
|
||||
self.arg_index += 1;
|
||||
|
||||
const ty = self.air.typeOfIndex(inst);
|
||||
|
||||
const result = self.args[arg_index];
|
||||
const name = self.mod_fn.getParamName(self.bin_file.options.module.?, arg_index);
|
||||
|
||||
const mcv = switch (result) {
|
||||
// Copy registers to the stack
|
||||
.register => |reg| blk: {
|
||||
@ -3889,8 +4107,14 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
|
||||
},
|
||||
else => result,
|
||||
};
|
||||
// TODO generate debug info
|
||||
// try self.genArgDbgInfo(inst, mcv);
|
||||
|
||||
const tag = self.air.instructions.items(.tag)[inst];
|
||||
try self.dbg_info_relocs.append(self.gpa, .{
|
||||
.tag = tag,
|
||||
.ty = ty,
|
||||
.name = name,
|
||||
.mcv = result,
|
||||
});
|
||||
|
||||
if (self.liveness.isUnused(inst))
|
||||
return self.finishAirBookkeeping();
|
||||
@ -4378,10 +4602,21 @@ 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 tag = self.air.instructions.items(.tag)[inst];
|
||||
const ty = self.air.typeOf(operand);
|
||||
const mcv = try self.resolveInst(operand);
|
||||
const name = self.air.nullTerminatedString(pl_op.payload);
|
||||
|
||||
log.debug("airDbgVar: %{d}: {}, {}", .{ inst, ty.fmtDebug(), mcv });
|
||||
|
||||
try self.dbg_info_relocs.append(self.gpa, .{
|
||||
.tag = tag,
|
||||
.ty = ty,
|
||||
.name = name,
|
||||
.mcv = mcv,
|
||||
});
|
||||
|
||||
return self.finishAir(inst, .dead, .{ operand, .none, .none });
|
||||
}
|
||||
|
||||
|
||||
@ -296,6 +296,13 @@ pub const Register = enum(u8) {
|
||||
pub fn dwarfLocOp(self: Register) u8 {
|
||||
return @as(u8, self.enc()) + DW.OP.reg0;
|
||||
}
|
||||
|
||||
/// DWARF encodings that push a value onto the DWARF stack that is either
|
||||
/// the contents of a register or the result of adding the contents a given
|
||||
/// register to a given signed offset.
|
||||
pub fn dwarfLocOpDeref(self: Register) u8 {
|
||||
return @as(u8, self.enc()) + DW.OP.breg0;
|
||||
}
|
||||
};
|
||||
|
||||
test "Register.enc" {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user