authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-11-09 16:47:00+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-11-09 18:35:06+01:00
log02852098eebda52ce6373d0440cec4965e6d4478
treee1e14e4715ba27d3fc7f416721b60325b4094e3f
parent7007ecdc05c2b1dac8a737386c390a5d07533ea7

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`.

2 files changed, 265 insertions(+), 23 deletions(-)

src/arch/aarch64/CodeGen.zig+258-23
...@@ -51,13 +51,14 @@ gpa: Allocator,...@@ -51,13 +51,14 @@ gpa: Allocator,
51air: Air,51air: Air,
52liveness: Liveness,52liveness: Liveness,
53bin_file: *link.File,53bin_file: *link.File,
54debug_output: DebugInfoOutput,
54target: *const std.Target,55target: *const std.Target,
55mod_fn: *const Module.Fn,56mod_fn: *const Module.Fn,
56err_msg: ?*ErrorMsg,57err_msg: ?*ErrorMsg,
57args: []MCValue,58args: []MCValue,
58ret_mcv: MCValue,59ret_mcv: MCValue,
59fn_type: Type,60fn_type: Type,
60arg_index: usize,61arg_index: u32,
61src_loc: Module.SrcLoc,62src_loc: Module.SrcLoc,
62stack_align: u32,63stack_align: u32,
6364
...@@ -75,6 +76,12 @@ end_di_column: u32,...@@ -75,6 +76,12 @@ end_di_column: u32,
75/// which is a relative jump, based on the address following the reloc.76/// which is a relative jump, based on the address following the reloc.
76exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .{},77exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .{},
7778
79/// We postpone the creation of debug info for function args and locals
80/// until after all Mir instructions have been generated. Only then we
81/// will know saved_regs_stack_space which is necessary in order to
82/// calculate the right stack offsest with respect to the `.fp` register.
83dbg_info_relocs: std.ArrayListUnmanaged(DbgInfoReloc) = .{},
84
78/// Whenever there is a runtime branch, we push a Branch onto this stack,85/// Whenever there is a runtime branch, we push a Branch onto this stack,
79/// and pop it off when the runtime branch joins. This provides an "overlay"86/// and pop it off when the runtime branch joins. This provides an "overlay"
80/// of the table of mappings from instructions to `MCValue` from within the branch.87/// of the table of mappings from instructions to `MCValue` from within the branch.
...@@ -160,6 +167,213 @@ const MCValue = union(enum) {...@@ -160,6 +167,213 @@ const MCValue = union(enum) {
160 stack_argument_offset: u32,167 stack_argument_offset: u32,
161};168};
162169
170const DbgInfoReloc = struct {
171 tag: Air.Inst.Tag,
172 ty: Type,
173 name: [:0]const u8,
174 mcv: MCValue,
175
176 fn genDbgInfo(reloc: DbgInfoReloc, function: Self) !void {
177 switch (reloc.tag) {
178 .arg => try reloc.genArgDbgInfo(function),
179
180 .dbg_var_ptr,
181 .dbg_var_val,
182 => try reloc.genVarDbgInfo(function),
183
184 else => unreachable,
185 }
186 }
187
188 fn genArgDbgInfo(reloc: DbgInfoReloc, function: Self) error{OutOfMemory}!void {
189 const name_with_null = reloc.name.ptr[0 .. reloc.name.len + 1];
190
191 switch (function.debug_output) {
192 .dwarf => |dw| {
193 const dbg_info = &dw.dbg_info;
194 switch (reloc.mcv) {
195 .register => |reg| {
196 try dbg_info.ensureUnusedCapacity(3);
197 dbg_info.appendAssumeCapacity(@enumToInt(link.File.Dwarf.AbbrevKind.parameter));
198 dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
199 1, // ULEB128 dwarf expression length
200 reg.dwarfLocOp(),
201 });
202 try dbg_info.ensureUnusedCapacity(5 + name_with_null.len);
203 try function.addDbgInfoTypeReloc(reloc.ty); // DW.AT.type, DW.FORM.ref4
204 dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string
205 },
206
207 .stack_offset,
208 .stack_argument_offset,
209 => |offset| {
210 const adjusted_offset = switch (reloc.mcv) {
211 .stack_offset => -@intCast(i32, offset),
212 .stack_argument_offset => @intCast(i32, function.saved_regs_stack_space + offset),
213 else => unreachable,
214 };
215
216 try dbg_info.ensureUnusedCapacity(8);
217 dbg_info.appendAssumeCapacity(@enumToInt(link.File.Dwarf.AbbrevKind.parameter));
218 const fixup = dbg_info.items.len;
219 dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
220 1, // we will backpatch it after we encode the displacement in LEB128
221 Register.x29.dwarfLocOpDeref(), // frame pointer
222 });
223 leb128.writeILEB128(dbg_info.writer(), adjusted_offset) catch unreachable;
224 dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2);
225 try dbg_info.ensureUnusedCapacity(5 + name_with_null.len);
226 try function.addDbgInfoTypeReloc(reloc.ty); // DW.AT.type, DW.FORM.ref4
227 dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string
228
229 },
230
231 else => unreachable, // not a possible argument
232 }
233 },
234 .plan9 => {},
235 .none => {},
236 }
237 }
238
239 fn genVarDbgInfo(reloc: DbgInfoReloc, function: Self) !void {
240 const name_with_null = reloc.name.ptr[0 .. reloc.name.len + 1];
241 const ty = switch (reloc.tag) {
242 .dbg_var_ptr => reloc.ty.childType(),
243 .dbg_var_val => reloc.ty,
244 else => unreachable,
245 };
246
247 switch (function.debug_output) {
248 .dwarf => |dw| {
249 const dbg_info = &dw.dbg_info;
250 try dbg_info.append(@enumToInt(link.File.Dwarf.AbbrevKind.variable));
251 const endian = function.target.cpu.arch.endian();
252
253 switch (reloc.mcv) {
254 .register => |reg| {
255 try dbg_info.ensureUnusedCapacity(2);
256 dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
257 1, // ULEB128 dwarf expression length
258 reg.dwarfLocOp(),
259 });
260 },
261
262 .ptr_stack_offset,
263 .stack_offset,
264 => |off| {
265 try dbg_info.ensureUnusedCapacity(7);
266 const fixup = dbg_info.items.len;
267 dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
268 1, // we will backpatch it after we encode the displacement in LEB128
269 Register.x29.dwarfLocOpDeref(), // frame pointer
270 });
271 leb128.writeILEB128(dbg_info.writer(), -@intCast(i32, off)) catch unreachable;
272 dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2);
273 },
274
275 .memory,
276 .linker_load,
277 => {
278 const ptr_width = @intCast(u8, @divExact(function.target.cpu.arch.ptrBitWidth(), 8));
279 const is_ptr = switch (reloc.tag) {
280 .dbg_var_ptr => true,
281 .dbg_var_val => false,
282 else => unreachable,
283 };
284 try dbg_info.ensureUnusedCapacity(2 + ptr_width);
285 dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
286 1 + ptr_width + @boolToInt(is_ptr),
287 DW.OP.addr, // literal address
288 });
289 const offset = @intCast(u32, dbg_info.items.len);
290 const addr = switch (reloc.mcv) {
291 .memory => |addr| addr,
292 else => 0,
293 };
294 switch (ptr_width) {
295 0...4 => {
296 try dbg_info.writer().writeInt(u32, @intCast(u32, addr), endian);
297 },
298 5...8 => {
299 try dbg_info.writer().writeInt(u64, addr, endian);
300 },
301 else => unreachable,
302 }
303 if (is_ptr) {
304 // We need deref the address as we point to the value via GOT entry.
305 try dbg_info.append(DW.OP.deref);
306 }
307 switch (reloc.mcv) {
308 .linker_load => |load_struct| try dw.addExprlocReloc(
309 load_struct.sym_index,
310 offset,
311 is_ptr,
312 ),
313 else => {},
314 }
315 },
316
317 .immediate => |x| {
318 try dbg_info.ensureUnusedCapacity(2);
319 const fixup = dbg_info.items.len;
320 dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
321 1,
322 if (ty.isSignedInt()) DW.OP.consts else DW.OP.constu,
323 });
324 if (ty.isSignedInt()) {
325 try leb128.writeILEB128(dbg_info.writer(), @bitCast(i64, x));
326 } else {
327 try leb128.writeULEB128(dbg_info.writer(), x);
328 }
329 try dbg_info.append(DW.OP.stack_value);
330 dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2);
331 },
332
333 .undef => {
334 // DW.AT.location, DW.FORM.exprloc
335 // uleb128(exprloc_len)
336 // DW.OP.implicit_value uleb128(len_of_bytes) bytes
337 const abi_size = @intCast(u32, ty.abiSize(function.target.*));
338 var implicit_value_len = std.ArrayList(u8).init(function.gpa);
339 defer implicit_value_len.deinit();
340 try leb128.writeULEB128(implicit_value_len.writer(), abi_size);
341 const total_exprloc_len = 1 + implicit_value_len.items.len + abi_size;
342 try leb128.writeULEB128(dbg_info.writer(), total_exprloc_len);
343 try dbg_info.ensureUnusedCapacity(total_exprloc_len);
344 dbg_info.appendAssumeCapacity(DW.OP.implicit_value);
345 dbg_info.appendSliceAssumeCapacity(implicit_value_len.items);
346 dbg_info.appendNTimesAssumeCapacity(0xaa, abi_size);
347 },
348
349 .none => {
350 try dbg_info.ensureUnusedCapacity(3);
351 dbg_info.appendSliceAssumeCapacity(&[3]u8{ // DW.AT.location, DW.FORM.exprloc
352 2, DW.OP.lit0, DW.OP.stack_value,
353 });
354 },
355
356 .stack_argument_offset => unreachable,
357
358 else => {
359 try dbg_info.ensureUnusedCapacity(2);
360 dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
361 1, DW.OP.nop,
362 });
363 log.debug("TODO generate debug info for {}", .{reloc.mcv});
364 },
365 }
366
367 try dbg_info.ensureUnusedCapacity(5 + name_with_null.len);
368 try function.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4
369 dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string
370 },
371 .plan9 => {},
372 .none => {},
373 }
374 }
375};
376
163const Branch = struct {377const Branch = struct {
164 inst_table: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, MCValue) = .{},378 inst_table: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, MCValue) = .{},
165379
...@@ -262,6 +476,7 @@ pub fn generate(...@@ -262,6 +476,7 @@ pub fn generate(
262 .gpa = bin_file.allocator,476 .gpa = bin_file.allocator,
263 .air = air,477 .air = air,
264 .liveness = liveness,478 .liveness = liveness,
479 .debug_output = debug_output,
265 .target = &bin_file.options.target,480 .target = &bin_file.options.target,
266 .bin_file = bin_file,481 .bin_file = bin_file,
267 .mod_fn = module_fn,482 .mod_fn = module_fn,
...@@ -279,6 +494,7 @@ pub fn generate(...@@ -279,6 +494,7 @@ pub fn generate(
279 defer function.stack.deinit(bin_file.allocator);494 defer function.stack.deinit(bin_file.allocator);
280 defer function.blocks.deinit(bin_file.allocator);495 defer function.blocks.deinit(bin_file.allocator);
281 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);496 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
497 defer function.dbg_info_relocs.deinit(bin_file.allocator);
282498
283 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {499 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {
284 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },500 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
...@@ -302,6 +518,10 @@ pub fn generate(...@@ -302,6 +518,10 @@ pub fn generate(
302 else => |e| return e,518 else => |e| return e,
303 };519 };
304520
521 for (function.dbg_info_relocs.items) |reloc| {
522 try reloc.genDbgInfo(function);
523 }
524
305 var mir = Mir{525 var mir = Mir{
306 .instructions = function.mir_instructions.toOwnedSlice(),526 .instructions = function.mir_instructions.toOwnedSlice(),
307 .extra = function.mir_extra.toOwnedSlice(bin_file.allocator),527 .extra = function.mir_extra.toOwnedSlice(bin_file.allocator),
...@@ -854,23 +1074,20 @@ fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {...@@ -854,23 +1074,20 @@ fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {
8541074
855/// Adds a Type to the .debug_info at the current position. The bytes will be populated later,1075/// Adds a Type to the .debug_info at the current position. The bytes will be populated later,
856/// after codegen for this symbol is done.1076/// after codegen for this symbol is done.
857fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void {1077fn addDbgInfoTypeReloc(self: Self, ty: Type) !void {
858 switch (self.debug_output) {1078 switch (self.debug_output) {
859 .dwarf => |dbg_out| {1079 .dwarf => |dw| {
860 assert(ty.hasRuntimeBits());1080 const dbg_info = &dw.dbg_info;
861 const index = dbg_out.dbg_info.items.len;1081 const index = dbg_info.items.len;
862 try dbg_out.dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref41082 try dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4
8631083 const mod = self.bin_file.options.module.?;
864 const gop = try dbg_out.dbg_info_type_relocs.getOrPutContext(self.gpa, ty, .{1084 const fn_owner_decl = mod.declPtr(self.mod_fn.owner_decl);
865 .target = self.target.*,1085 const atom = switch (self.bin_file.tag) {
866 });1086 .elf => &fn_owner_decl.link.elf.dbg_info_atom,
867 if (!gop.found_existing) {1087 .macho => &fn_owner_decl.link.macho.dbg_info_atom,
868 gop.value_ptr.* = .{1088 else => unreachable,
869 .off = undefined,1089 };
870 .relocs = .{},1090 try dw.addTypeRelocGlobal(atom, ty, @intCast(u32, index));
871 };
872 }
873 try gop.value_ptr.relocs.append(self.gpa, @intCast(u32, index));
874 },1091 },
875 .plan9 => {},1092 .plan9 => {},
876 .none => {},1093 .none => {},
...@@ -3872,8 +4089,9 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {...@@ -3872,8 +4089,9 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
3872 self.arg_index += 1;4089 self.arg_index += 1;
38734090
3874 const ty = self.air.typeOfIndex(inst);4091 const ty = self.air.typeOfIndex(inst);
3875
3876 const result = self.args[arg_index];4092 const result = self.args[arg_index];
4093 const name = self.mod_fn.getParamName(self.bin_file.options.module.?, arg_index);
4094
3877 const mcv = switch (result) {4095 const mcv = switch (result) {
3878 // Copy registers to the stack4096 // Copy registers to the stack
3879 .register => |reg| blk: {4097 .register => |reg| blk: {
...@@ -3889,8 +4107,14 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {...@@ -3889,8 +4107,14 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
3889 },4107 },
3890 else => result,4108 else => result,
3891 };4109 };
3892 // TODO generate debug info4110
3893 // try self.genArgDbgInfo(inst, mcv);4111 const tag = self.air.instructions.items(.tag)[inst];
4112 try self.dbg_info_relocs.append(self.gpa, .{
4113 .tag = tag,
4114 .ty = ty,
4115 .name = name,
4116 .mcv = result,
4117 });
38944118
3895 if (self.liveness.isUnused(inst))4119 if (self.liveness.isUnused(inst))
3896 return self.finishAirBookkeeping();4120 return self.finishAirBookkeeping();
...@@ -4378,10 +4602,21 @@ fn airDbgBlock(self: *Self, inst: Air.Inst.Index) !void {...@@ -4378,10 +4602,21 @@ fn airDbgBlock(self: *Self, inst: Air.Inst.Index) !void {
43784602
4379fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {4603fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
4380 const pl_op = self.air.instructions.items(.data)[inst].pl_op;4604 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
4381 const name = self.air.nullTerminatedString(pl_op.payload);
4382 const operand = pl_op.operand;4605 const operand = pl_op.operand;
4383 // TODO emit debug info for this variable4606 const tag = self.air.instructions.items(.tag)[inst];
4384 _ = name;4607 const ty = self.air.typeOf(operand);
4608 const mcv = try self.resolveInst(operand);
4609 const name = self.air.nullTerminatedString(pl_op.payload);
4610
4611 log.debug("airDbgVar: %{d}: {}, {}", .{ inst, ty.fmtDebug(), mcv });
4612
4613 try self.dbg_info_relocs.append(self.gpa, .{
4614 .tag = tag,
4615 .ty = ty,
4616 .name = name,
4617 .mcv = mcv,
4618 });
4619
4385 return self.finishAir(inst, .dead, .{ operand, .none, .none });4620 return self.finishAir(inst, .dead, .{ operand, .none, .none });
4386}4621}
43874622
src/arch/aarch64/bits.zig+7
...@@ -296,6 +296,13 @@ pub const Register = enum(u8) {...@@ -296,6 +296,13 @@ pub const Register = enum(u8) {
296 pub fn dwarfLocOp(self: Register) u8 {296 pub fn dwarfLocOp(self: Register) u8 {
297 return @as(u8, self.enc()) + DW.OP.reg0;297 return @as(u8, self.enc()) + DW.OP.reg0;
298 }298 }
299
300 /// DWARF encodings that push a value onto the DWARF stack that is either
301 /// the contents of a register or the result of adding the contents a given
302 /// register to a given signed offset.
303 pub fn dwarfLocOpDeref(self: Register) u8 {
304 return @as(u8, self.enc()) + DW.OP.breg0;
305 }
299};306};
300307
301test "Register.enc" {308test "Register.enc" {