diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2022-11-09 16:47:00 +0100 |
|---|---|---|
| committer | Jakub Konka <kubkon@jakubkonka.com> | 2022-11-09 18:35:06 +0100 |
| commit | 02852098eebda52ce6373d0440cec4965e6d4478 (patch) | |
| tree | e1e14e4715ba27d3fc7f416721b60325b4094e3f /src | |
| parent | 7007ecdc05c2b1dac8a737386c390a5d07533ea7 (diff) | |
| download | zig-02852098eebda52ce6373d0440cec4965e6d4478.tar.gz zig-02852098eebda52ce6373d0440cec4965e6d4478.zip | |
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`.
Diffstat (limited to 'src')
| -rw-r--r-- | src/arch/aarch64/CodeGen.zig | 281 | ||||
| -rw-r--r-- | src/arch/aarch64/bits.zig | 7 |
2 files changed, 265 insertions, 23 deletions
diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 613bdbd3fd..a117e4a083 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -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 }); } diff --git a/src/arch/aarch64/bits.zig b/src/arch/aarch64/bits.zig index aa13298afe..20b33c01c5 100644 --- a/src/arch/aarch64/bits.zig +++ b/src/arch/aarch64/bits.zig @@ -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" { |
