authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-11-10 16:50:57+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-01-09 13:57:34-07:00
log84f584317897bfc1d529361b79721d22d130d4d3
tree6bee6902f5290641d98ffc37eb2c68a96c5a9067
parent95ecd521b8f169a80e543f8a07aef4807a1d46d8

Merge pull request #13495 from ziglang/macho-dsym

stage2: misc DWARF debug info fixes and additions for x86_64 and aarch64

7 files changed, 412 insertions(+), 116 deletions(-)

src/arch/aarch64/CodeGen.zig+265-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.
...@@ -159,6 +166,220 @@ const MCValue = union(enum) {...@@ -159,6 +166,220 @@ const MCValue = union(enum) {
159 stack_argument_offset: u32,166 stack_argument_offset: u32,
160};167};
161168
169const DbgInfoReloc = struct {
170 tag: Air.Inst.Tag,
171 ty: Type,
172 name: [:0]const u8,
173 mcv: MCValue,
174
175 fn genDbgInfo(reloc: DbgInfoReloc, function: Self) !void {
176 switch (reloc.tag) {
177 .arg => try reloc.genArgDbgInfo(function),
178
179 .dbg_var_ptr,
180 .dbg_var_val,
181 => try reloc.genVarDbgInfo(function),
182
183 else => unreachable,
184 }
185 }
186
187 fn genArgDbgInfo(reloc: DbgInfoReloc, function: Self) error{OutOfMemory}!void {
188 const name_with_null = reloc.name.ptr[0 .. reloc.name.len + 1];
189
190 switch (function.debug_output) {
191 .dwarf => |dw| {
192 const dbg_info = &dw.dbg_info;
193 switch (reloc.mcv) {
194 .register => |reg| {
195 try dbg_info.ensureUnusedCapacity(3);
196 dbg_info.appendAssumeCapacity(@enumToInt(link.File.Dwarf.AbbrevKind.parameter));
197 dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
198 1, // ULEB128 dwarf expression length
199 reg.dwarfLocOp(),
200 });
201 try dbg_info.ensureUnusedCapacity(5 + name_with_null.len);
202 try function.addDbgInfoTypeReloc(reloc.ty); // DW.AT.type, DW.FORM.ref4
203 dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string
204 },
205
206 .stack_offset,
207 .stack_argument_offset,
208 => |offset| {
209 const adjusted_offset = switch (reloc.mcv) {
210 .stack_offset => -@intCast(i32, offset),
211 .stack_argument_offset => @intCast(i32, function.saved_regs_stack_space + offset),
212 else => unreachable,
213 };
214
215 try dbg_info.ensureUnusedCapacity(8);
216 dbg_info.appendAssumeCapacity(@enumToInt(link.File.Dwarf.AbbrevKind.parameter));
217 const fixup = dbg_info.items.len;
218 dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
219 1, // we will backpatch it after we encode the displacement in LEB128
220 Register.x29.dwarfLocOpDeref(), // frame pointer
221 });
222 leb128.writeILEB128(dbg_info.writer(), adjusted_offset) catch unreachable;
223 dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2);
224 try dbg_info.ensureUnusedCapacity(5 + name_with_null.len);
225 try function.addDbgInfoTypeReloc(reloc.ty); // DW.AT.type, DW.FORM.ref4
226 dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string
227
228 },
229
230 else => unreachable, // not a possible argument
231 }
232 },
233 .plan9 => {},
234 .none => {},
235 }
236 }
237
238 fn genVarDbgInfo(reloc: DbgInfoReloc, function: Self) !void {
239 const name_with_null = reloc.name.ptr[0 .. reloc.name.len + 1];
240 const ty = switch (reloc.tag) {
241 .dbg_var_ptr => reloc.ty.childType(),
242 .dbg_var_val => reloc.ty,
243 else => unreachable,
244 };
245
246 switch (function.debug_output) {
247 .dwarf => |dw| {
248 const dbg_info = &dw.dbg_info;
249 try dbg_info.append(@enumToInt(link.File.Dwarf.AbbrevKind.variable));
250 const endian = function.target.cpu.arch.endian();
251
252 switch (reloc.mcv) {
253 .register => |reg| {
254 try dbg_info.ensureUnusedCapacity(2);
255 dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
256 1, // ULEB128 dwarf expression length
257 reg.dwarfLocOp(),
258 });
259 },
260
261 .ptr_stack_offset,
262 .stack_offset,
263 .stack_argument_offset,
264 => |offset| {
265 const adjusted_offset = switch (reloc.mcv) {
266 .ptr_stack_offset,
267 .stack_offset,
268 => -@intCast(i32, offset),
269 .stack_argument_offset => @intCast(i32, function.saved_regs_stack_space + offset),
270 else => unreachable,
271 };
272
273 try dbg_info.ensureUnusedCapacity(7);
274 const fixup = dbg_info.items.len;
275 dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
276 1, // we will backpatch it after we encode the displacement in LEB128
277 Register.x29.dwarfLocOpDeref(), // frame pointer
278 });
279 leb128.writeILEB128(dbg_info.writer(), adjusted_offset) catch unreachable;
280 dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2);
281 },
282
283 .memory,
284 .linker_load,
285 => {
286 const ptr_width = @intCast(u8, @divExact(function.target.cpu.arch.ptrBitWidth(), 8));
287 const is_ptr = switch (reloc.tag) {
288 .dbg_var_ptr => true,
289 .dbg_var_val => false,
290 else => unreachable,
291 };
292 try dbg_info.ensureUnusedCapacity(2 + ptr_width);
293 dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
294 1 + ptr_width + @boolToInt(is_ptr),
295 DW.OP.addr, // literal address
296 });
297 const offset = @intCast(u32, dbg_info.items.len);
298 const addr = switch (reloc.mcv) {
299 .memory => |addr| addr,
300 else => 0,
301 };
302 switch (ptr_width) {
303 0...4 => {
304 try dbg_info.writer().writeInt(u32, @intCast(u32, addr), endian);
305 },
306 5...8 => {
307 try dbg_info.writer().writeInt(u64, addr, endian);
308 },
309 else => unreachable,
310 }
311 if (is_ptr) {
312 // We need deref the address as we point to the value via GOT entry.
313 try dbg_info.append(DW.OP.deref);
314 }
315 switch (reloc.mcv) {
316 .linker_load => |load_struct| try dw.addExprlocReloc(
317 load_struct.sym_index,
318 offset,
319 is_ptr,
320 ),
321 else => {},
322 }
323 },
324
325 .immediate => |x| {
326 try dbg_info.ensureUnusedCapacity(2);
327 const fixup = dbg_info.items.len;
328 dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
329 1,
330 if (ty.isSignedInt()) DW.OP.consts else DW.OP.constu,
331 });
332 if (ty.isSignedInt()) {
333 try leb128.writeILEB128(dbg_info.writer(), @bitCast(i64, x));
334 } else {
335 try leb128.writeULEB128(dbg_info.writer(), x);
336 }
337 try dbg_info.append(DW.OP.stack_value);
338 dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2);
339 },
340
341 .undef => {
342 // DW.AT.location, DW.FORM.exprloc
343 // uleb128(exprloc_len)
344 // DW.OP.implicit_value uleb128(len_of_bytes) bytes
345 const abi_size = @intCast(u32, ty.abiSize(function.target.*));
346 var implicit_value_len = std.ArrayList(u8).init(function.gpa);
347 defer implicit_value_len.deinit();
348 try leb128.writeULEB128(implicit_value_len.writer(), abi_size);
349 const total_exprloc_len = 1 + implicit_value_len.items.len + abi_size;
350 try leb128.writeULEB128(dbg_info.writer(), total_exprloc_len);
351 try dbg_info.ensureUnusedCapacity(total_exprloc_len);
352 dbg_info.appendAssumeCapacity(DW.OP.implicit_value);
353 dbg_info.appendSliceAssumeCapacity(implicit_value_len.items);
354 dbg_info.appendNTimesAssumeCapacity(0xaa, abi_size);
355 },
356
357 .none => {
358 try dbg_info.ensureUnusedCapacity(3);
359 dbg_info.appendSliceAssumeCapacity(&[3]u8{ // DW.AT.location, DW.FORM.exprloc
360 2, DW.OP.lit0, DW.OP.stack_value,
361 });
362 },
363
364 else => {
365 try dbg_info.ensureUnusedCapacity(2);
366 dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
367 1, DW.OP.nop,
368 });
369 log.debug("TODO generate debug info for {}", .{reloc.mcv});
370 },
371 }
372
373 try dbg_info.ensureUnusedCapacity(5 + name_with_null.len);
374 try function.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4
375 dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string
376 },
377 .plan9 => {},
378 .none => {},
379 }
380 }
381};
382
162const Branch = struct {383const Branch = struct {
163 inst_table: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, MCValue) = .{},384 inst_table: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, MCValue) = .{},
164385
...@@ -261,6 +482,7 @@ pub fn generate(...@@ -261,6 +482,7 @@ pub fn generate(
261 .gpa = bin_file.allocator,482 .gpa = bin_file.allocator,
262 .air = air,483 .air = air,
263 .liveness = liveness,484 .liveness = liveness,
485 .debug_output = debug_output,
264 .target = &bin_file.options.target,486 .target = &bin_file.options.target,
265 .bin_file = bin_file,487 .bin_file = bin_file,
266 .mod_fn = module_fn,488 .mod_fn = module_fn,
...@@ -278,6 +500,7 @@ pub fn generate(...@@ -278,6 +500,7 @@ pub fn generate(
278 defer function.stack.deinit(bin_file.allocator);500 defer function.stack.deinit(bin_file.allocator);
279 defer function.blocks.deinit(bin_file.allocator);501 defer function.blocks.deinit(bin_file.allocator);
280 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);502 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
503 defer function.dbg_info_relocs.deinit(bin_file.allocator);
281504
282 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {505 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {
283 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },506 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
...@@ -301,6 +524,10 @@ pub fn generate(...@@ -301,6 +524,10 @@ pub fn generate(
301 else => |e| return e,524 else => |e| return e,
302 };525 };
303526
527 for (function.dbg_info_relocs.items) |reloc| {
528 try reloc.genDbgInfo(function);
529 }
530
304 var mir = Mir{531 var mir = Mir{
305 .instructions = function.mir_instructions.toOwnedSlice(),532 .instructions = function.mir_instructions.toOwnedSlice(),
306 .extra = function.mir_extra.toOwnedSlice(bin_file.allocator),533 .extra = function.mir_extra.toOwnedSlice(bin_file.allocator),
...@@ -853,23 +1080,20 @@ fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {...@@ -853,23 +1080,20 @@ fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {
8531080
854/// Adds a Type to the .debug_info at the current position. The bytes will be populated later,1081/// Adds a Type to the .debug_info at the current position. The bytes will be populated later,
855/// after codegen for this symbol is done.1082/// after codegen for this symbol is done.
856fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void {1083fn addDbgInfoTypeReloc(self: Self, ty: Type) !void {
857 switch (self.debug_output) {1084 switch (self.debug_output) {
858 .dwarf => |dbg_out| {1085 .dwarf => |dw| {
859 assert(ty.hasRuntimeBits());1086 const dbg_info = &dw.dbg_info;
860 const index = dbg_out.dbg_info.items.len;1087 const index = dbg_info.items.len;
861 try dbg_out.dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref41088 try dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4
8621089 const mod = self.bin_file.options.module.?;
863 const gop = try dbg_out.dbg_info_type_relocs.getOrPutContext(self.gpa, ty, .{1090 const fn_owner_decl = mod.declPtr(self.mod_fn.owner_decl);
864 .target = self.target.*,1091 const atom = switch (self.bin_file.tag) {
865 });1092 .elf => &fn_owner_decl.link.elf.dbg_info_atom,
866 if (!gop.found_existing) {1093 .macho => &fn_owner_decl.link.macho.dbg_info_atom,
867 gop.value_ptr.* = .{1094 else => unreachable,
868 .off = undefined,1095 };
869 .relocs = .{},1096 try dw.addTypeRelocGlobal(atom, ty, @intCast(u32, index));
870 };
871 }
872 try gop.value_ptr.relocs.append(self.gpa, @intCast(u32, index));
873 },1097 },
874 .plan9 => {},1098 .plan9 => {},
875 .none => {},1099 .none => {},
...@@ -3866,8 +4090,9 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {...@@ -3866,8 +4090,9 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
3866 self.arg_index += 1;4090 self.arg_index += 1;
38674091
3868 const ty = self.air.typeOfIndex(inst);4092 const ty = self.air.typeOfIndex(inst);
3869
3870 const result = self.args[arg_index];4093 const result = self.args[arg_index];
4094 const name = self.mod_fn.getParamName(self.bin_file.options.module.?, arg_index);
4095
3871 const mcv = switch (result) {4096 const mcv = switch (result) {
3872 // Copy registers to the stack4097 // Copy registers to the stack
3873 .register => |reg| blk: {4098 .register => |reg| blk: {
...@@ -3883,8 +4108,14 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {...@@ -3883,8 +4108,14 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
3883 },4108 },
3884 else => result,4109 else => result,
3885 };4110 };
3886 // TODO generate debug info4111
3887 // try self.genArgDbgInfo(inst, mcv);4112 const tag = self.air.instructions.items(.tag)[inst];
4113 try self.dbg_info_relocs.append(self.gpa, .{
4114 .tag = tag,
4115 .ty = ty,
4116 .name = name,
4117 .mcv = result,
4118 });
38884119
3889 if (self.liveness.isUnused(inst))4120 if (self.liveness.isUnused(inst))
3890 return self.finishAirBookkeeping();4121 return self.finishAirBookkeeping();
...@@ -4335,10 +4566,21 @@ fn airDbgBlock(self: *Self, inst: Air.Inst.Index) !void {...@@ -4335,10 +4566,21 @@ fn airDbgBlock(self: *Self, inst: Air.Inst.Index) !void {
43354566
4336fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {4567fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
4337 const pl_op = self.air.instructions.items(.data)[inst].pl_op;4568 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
4338 const name = self.air.nullTerminatedString(pl_op.payload);
4339 const operand = pl_op.operand;4569 const operand = pl_op.operand;
4340 // TODO emit debug info for this variable4570 const tag = self.air.instructions.items(.tag)[inst];
4341 _ = name;4571 const ty = self.air.typeOf(operand);
4572 const mcv = try self.resolveInst(operand);
4573 const name = self.air.nullTerminatedString(pl_op.payload);
4574
4575 log.debug("airDbgVar: %{d}: {}, {}", .{ inst, ty.fmtDebug(), mcv });
4576
4577 try self.dbg_info_relocs.append(self.gpa, .{
4578 .tag = tag,
4579 .ty = ty,
4580 .name = name,
4581 .mcv = mcv,
4582 });
4583
4342 return self.finishAir(inst, .dead, .{ operand, .none, .none });4584 return self.finishAir(inst, .dead, .{ operand, .none, .none });
4343}4585}
43444586
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" {
src/arch/x86_64/CodeGen.zig+73-65
...@@ -3797,64 +3797,68 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {...@@ -3797,64 +3797,68 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
3797 const ty = self.air.typeOfIndex(inst);3797 const ty = self.air.typeOfIndex(inst);
3798 const mcv = self.args[arg_index];3798 const mcv = self.args[arg_index];
3799 const name = self.mod_fn.getParamName(self.bin_file.options.module.?, arg_index);3799 const name = self.mod_fn.getParamName(self.bin_file.options.module.?, arg_index);
3800 const name_with_null = name.ptr[0 .. name.len + 1];
38013800
3802 if (self.liveness.isUnused(inst))3801 if (self.liveness.isUnused(inst))
3803 return self.finishAirBookkeeping();3802 return self.finishAirBookkeeping();
38043803
3805 const dst_mcv: MCValue = blk: {3804 const dst_mcv: MCValue = switch (mcv) {
3806 switch (mcv) {3805 .register => |reg| blk: {
3807 .register => |reg| {3806 self.register_manager.getRegAssumeFree(reg.to64(), inst);
3808 self.register_manager.getRegAssumeFree(reg.to64(), inst);3807 break :blk MCValue{ .register = reg };
3809 switch (self.debug_output) {3808 },
3810 .dwarf => |dw| {3809 .stack_offset => |off| blk: {
3811 const dbg_info = &dw.dbg_info;3810 const offset = @intCast(i32, self.max_end_stack) - off + 16;
3812 try dbg_info.ensureUnusedCapacity(3);3811 break :blk MCValue{ .stack_offset = -offset };
3813 dbg_info.appendAssumeCapacity(@enumToInt(link.File.Dwarf.AbbrevKind.parameter));3812 },
3814 dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc3813 else => return self.fail("TODO implement arg for {}", .{mcv}),
3815 1, // ULEB128 dwarf expression length
3816 reg.dwarfLocOp(),
3817 });
3818 try dbg_info.ensureUnusedCapacity(5 + name_with_null.len);
3819 try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4
3820 dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string
3821 },
3822 .plan9 => {},
3823 .none => {},
3824 }
3825 break :blk mcv;
3826 },
3827 .stack_offset => |off| {
3828 const offset = @intCast(i32, self.max_end_stack) - off + 16;
3829 switch (self.debug_output) {
3830 .dwarf => |dw| {
3831 const dbg_info = &dw.dbg_info;
3832 try dbg_info.ensureUnusedCapacity(8);
3833 dbg_info.appendAssumeCapacity(@enumToInt(link.File.Dwarf.AbbrevKind.parameter));
3834 const fixup = dbg_info.items.len;
3835 dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
3836 1, // we will backpatch it after we encode the displacement in LEB128
3837 DW.OP.breg6, // .rbp TODO handle -fomit-frame-pointer
3838 });
3839 leb128.writeILEB128(dbg_info.writer(), offset) catch unreachable;
3840 dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2);
3841 try dbg_info.ensureUnusedCapacity(5 + name_with_null.len);
3842 try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4
3843 dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string
3844
3845 },
3846 .plan9 => {},
3847 .none => {},
3848 }
3849 break :blk MCValue{ .stack_offset = -offset };
3850 },
3851 else => return self.fail("TODO implement arg for {}", .{mcv}),
3852 }
3853 };3814 };
3815 try self.genArgDbgInfo(ty, name, dst_mcv);
38543816
3855 return self.finishAir(inst, dst_mcv, .{ .none, .none, .none });3817 return self.finishAir(inst, dst_mcv, .{ .none, .none, .none });
3856}3818}
38573819
3820fn genArgDbgInfo(self: Self, ty: Type, name: [:0]const u8, mcv: MCValue) !void {
3821 const name_with_null = name.ptr[0 .. name.len + 1];
3822 switch (self.debug_output) {
3823 .dwarf => |dw| {
3824 const dbg_info = &dw.dbg_info;
3825 switch (mcv) {
3826 .register => |reg| {
3827 try dbg_info.ensureUnusedCapacity(3);
3828 dbg_info.appendAssumeCapacity(@enumToInt(link.File.Dwarf.AbbrevKind.parameter));
3829 dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
3830 1, // ULEB128 dwarf expression length
3831 reg.dwarfLocOp(),
3832 });
3833 try dbg_info.ensureUnusedCapacity(5 + name_with_null.len);
3834 try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4
3835 dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string
3836 },
3837
3838 .stack_offset => |off| {
3839 try dbg_info.ensureUnusedCapacity(8);
3840 dbg_info.appendAssumeCapacity(@enumToInt(link.File.Dwarf.AbbrevKind.parameter));
3841 const fixup = dbg_info.items.len;
3842 dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
3843 1, // we will backpatch it after we encode the displacement in LEB128
3844 Register.rbp.dwarfLocOpDeref(), // TODO handle -fomit-frame-pointer
3845 });
3846 leb128.writeILEB128(dbg_info.writer(), -off) catch unreachable;
3847 dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2);
3848 try dbg_info.ensureUnusedCapacity(5 + name_with_null.len);
3849 try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4
3850 dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string
3851
3852 },
3853
3854 else => unreachable, // not a valid function parameter
3855 }
3856 },
3857 .plan9 => {},
3858 .none => {},
3859 }
3860}
3861
3858fn airBreakpoint(self: *Self) !void {3862fn airBreakpoint(self: *Self) !void {
3859 _ = try self.addInst(.{3863 _ = try self.addInst(.{
3860 .tag = .interrupt,3864 .tag = .interrupt,
...@@ -4424,7 +4428,7 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {...@@ -4424,7 +4428,7 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
4424}4428}
44254429
4426fn genVarDbgInfo(4430fn genVarDbgInfo(
4427 self: *Self,4431 self: Self,
4428 tag: Air.Inst.Tag,4432 tag: Air.Inst.Tag,
4429 ty: Type,4433 ty: Type,
4430 mcv: MCValue,4434 mcv: MCValue,
...@@ -4445,17 +4449,23 @@ fn genVarDbgInfo(...@@ -4445,17 +4449,23 @@ fn genVarDbgInfo(
4445 reg.dwarfLocOp(),4449 reg.dwarfLocOp(),
4446 });4450 });
4447 },4451 },
4448 .ptr_stack_offset, .stack_offset => |off| {4452
4453 .ptr_stack_offset,
4454 .stack_offset,
4455 => |off| {
4449 try dbg_info.ensureUnusedCapacity(7);4456 try dbg_info.ensureUnusedCapacity(7);
4450 const fixup = dbg_info.items.len;4457 const fixup = dbg_info.items.len;
4451 dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc4458 dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
4452 1, // we will backpatch it after we encode the displacement in LEB1284459 1, // we will backpatch it after we encode the displacement in LEB128
4453 DW.OP.breg6, // .rbp TODO handle -fomit-frame-pointer4460 Register.rbp.dwarfLocOpDeref(), // TODO handle -fomit-frame-pointer
4454 });4461 });
4455 leb128.writeILEB128(dbg_info.writer(), -off) catch unreachable;4462 leb128.writeILEB128(dbg_info.writer(), -off) catch unreachable;
4456 dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2);4463 dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2);
4457 },4464 },
4458 .memory, .linker_load => {4465
4466 .memory,
4467 .linker_load,
4468 => {
4459 const ptr_width = @intCast(u8, @divExact(self.target.cpu.arch.ptrBitWidth(), 8));4469 const ptr_width = @intCast(u8, @divExact(self.target.cpu.arch.ptrBitWidth(), 8));
4460 const is_ptr = switch (tag) {4470 const is_ptr = switch (tag) {
4461 .dbg_var_ptr => true,4471 .dbg_var_ptr => true,
...@@ -4494,27 +4504,23 @@ fn genVarDbgInfo(...@@ -4494,27 +4504,23 @@ fn genVarDbgInfo(
4494 else => {},4504 else => {},
4495 }4505 }
4496 },4506 },
4507
4497 .immediate => |x| {4508 .immediate => |x| {
4498 const signedness: std.builtin.Signedness = blk: {
4499 if (ty.zigTypeTag() != .Int) break :blk .unsigned;
4500 break :blk ty.intInfo(self.target.*).signedness;
4501 };
4502 try dbg_info.ensureUnusedCapacity(2);4509 try dbg_info.ensureUnusedCapacity(2);
4503 const fixup = dbg_info.items.len;4510 const fixup = dbg_info.items.len;
4504 dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc4511 dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
4505 1,4512 1,
4506 switch (signedness) {4513 if (ty.isSignedInt()) DW.OP.consts else DW.OP.constu,
4507 .signed => DW.OP.consts,
4508 .unsigned => DW.OP.constu,
4509 },
4510 });4514 });
4511 switch (signedness) {4515 if (ty.isSignedInt()) {
4512 .signed => try leb128.writeILEB128(dbg_info.writer(), @bitCast(i64, x)),4516 try leb128.writeILEB128(dbg_info.writer(), @bitCast(i64, x));
4513 .unsigned => try leb128.writeULEB128(dbg_info.writer(), x),4517 } else {
4518 try leb128.writeULEB128(dbg_info.writer(), x);
4514 }4519 }
4515 try dbg_info.append(DW.OP.stack_value);4520 try dbg_info.append(DW.OP.stack_value);
4516 dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2);4521 dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2);
4517 },4522 },
4523
4518 .undef => {4524 .undef => {
4519 // DW.AT.location, DW.FORM.exprloc4525 // DW.AT.location, DW.FORM.exprloc
4520 // uleb128(exprloc_len)4526 // uleb128(exprloc_len)
...@@ -4530,12 +4536,14 @@ fn genVarDbgInfo(...@@ -4530,12 +4536,14 @@ fn genVarDbgInfo(
4530 dbg_info.appendSliceAssumeCapacity(implicit_value_len.items);4536 dbg_info.appendSliceAssumeCapacity(implicit_value_len.items);
4531 dbg_info.appendNTimesAssumeCapacity(0xaa, abi_size);4537 dbg_info.appendNTimesAssumeCapacity(0xaa, abi_size);
4532 },4538 },
4539
4533 .none => {4540 .none => {
4534 try dbg_info.ensureUnusedCapacity(3);4541 try dbg_info.ensureUnusedCapacity(3);
4535 dbg_info.appendSliceAssumeCapacity(&[3]u8{ // DW.AT.location, DW.FORM.exprloc4542 dbg_info.appendSliceAssumeCapacity(&[3]u8{ // DW.AT.location, DW.FORM.exprloc
4536 2, DW.OP.lit0, DW.OP.stack_value,4543 2, DW.OP.lit0, DW.OP.stack_value,
4537 });4544 });
4538 },4545 },
4546
4539 else => {4547 else => {
4540 try dbg_info.ensureUnusedCapacity(2);4548 try dbg_info.ensureUnusedCapacity(2);
4541 dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc4549 dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
...@@ -4556,7 +4564,7 @@ fn genVarDbgInfo(...@@ -4556,7 +4564,7 @@ fn genVarDbgInfo(
45564564
4557/// Adds a Type to the .debug_info at the current position. The bytes will be populated later,4565/// Adds a Type to the .debug_info at the current position. The bytes will be populated later,
4558/// after codegen for this symbol is done.4566/// after codegen for this symbol is done.
4559fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void {4567fn addDbgInfoTypeReloc(self: Self, ty: Type) !void {
4560 switch (self.debug_output) {4568 switch (self.debug_output) {
4561 .dwarf => |dw| {4569 .dwarf => |dw| {
4562 const dbg_info = &dw.dbg_info;4570 const dbg_info = &dw.dbg_info;
src/arch/x86_64/bits.zig+61-22
...@@ -135,8 +135,6 @@ pub const Condition = enum(u5) {...@@ -135,8 +135,6 @@ pub const Condition = enum(u5) {
135 }135 }
136};136};
137137
138// zig fmt: off
139
140/// Definitions of all of the general purpose x64 registers. The order is semantically meaningful.138/// Definitions of all of the general purpose x64 registers. The order is semantically meaningful.
141/// The registers are defined such that IDs go in descending order of 64-bit,139/// The registers are defined such that IDs go in descending order of 64-bit,
142/// 32-bit, 16-bit, and then 8-bit, and each set contains exactly sixteen140/// 32-bit, 16-bit, and then 8-bit, and each set contains exactly sixteen
...@@ -152,6 +150,7 @@ pub const Condition = enum(u5) {...@@ -152,6 +150,7 @@ pub const Condition = enum(u5) {
152/// The ID can be easily determined by figuring out what range the register is150/// The ID can be easily determined by figuring out what range the register is
153/// in, and then subtracting the base.151/// in, and then subtracting the base.
154pub const Register = enum(u7) {152pub const Register = enum(u7) {
153 // zig fmt: off
155 // 0 through 15, 64-bit registers. 8-15 are extended.154 // 0 through 15, 64-bit registers. 8-15 are extended.
156 // id is just the int value.155 // id is just the int value.
157 rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi,156 rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi,
...@@ -184,6 +183,7 @@ pub const Register = enum(u7) {...@@ -184,6 +183,7 @@ pub const Register = enum(u7) {
184183
185 // Pseudo-value for MIR instructions.184 // Pseudo-value for MIR instructions.
186 none,185 none,
186 // zig fmt: on
187187
188 pub fn id(self: Register) u7 {188 pub fn id(self: Register) u7 {
189 return switch (@enumToInt(self)) {189 return switch (@enumToInt(self)) {
...@@ -192,7 +192,7 @@ pub const Register = enum(u7) {...@@ -192,7 +192,7 @@ pub const Register = enum(u7) {
192 else => unreachable,192 else => unreachable,
193 };193 };
194 }194 }
195 195
196 /// Returns the bit-width of the register.196 /// Returns the bit-width of the register.
197 pub fn size(self: Register) u9 {197 pub fn size(self: Register) u9 {
198 return switch (@enumToInt(self)) {198 return switch (@enumToInt(self)) {
...@@ -258,27 +258,66 @@ pub const Register = enum(u7) {...@@ -258,27 +258,66 @@ pub const Register = enum(u7) {
258 }258 }
259259
260 pub fn dwarfLocOp(self: Register) u8 {260 pub fn dwarfLocOp(self: Register) u8 {
261 return switch (self.to64()) {261 switch (@enumToInt(self)) {
262 .rax => DW.OP.reg0,262 0...63 => return switch (self.to64()) {
263 .rdx => DW.OP.reg1,263 .rax => DW.OP.reg0,
264 .rcx => DW.OP.reg2,264 .rdx => DW.OP.reg1,
265 .rbx => DW.OP.reg3,265 .rcx => DW.OP.reg2,
266 .rsi => DW.OP.reg4,266 .rbx => DW.OP.reg3,
267 .rdi => DW.OP.reg5,267 .rsi => DW.OP.reg4,
268 .rbp => DW.OP.reg6,268 .rdi => DW.OP.reg5,
269 .rsp => DW.OP.reg7,269 .rbp => DW.OP.reg6,
270270 .rsp => DW.OP.reg7,
271 .r8 => DW.OP.reg8,271
272 .r9 => DW.OP.reg9,272 .r8 => DW.OP.reg8,
273 .r10 => DW.OP.reg10,273 .r9 => DW.OP.reg9,
274 .r11 => DW.OP.reg11,274 .r10 => DW.OP.reg10,
275 .r12 => DW.OP.reg12,275 .r11 => DW.OP.reg11,
276 .r13 => DW.OP.reg13,276 .r12 => DW.OP.reg12,
277 .r14 => DW.OP.reg14,277 .r13 => DW.OP.reg13,
278 .r15 => DW.OP.reg15,278 .r14 => DW.OP.reg14,
279 .r15 => DW.OP.reg15,
280
281 else => unreachable,
282 },
283
284 64...79 => return @as(u8, self.enc()) + DW.OP.reg17,
279285
280 else => unreachable,286 else => unreachable,
281 };287 }
288 }
289
290 /// DWARF encodings that push a value onto the DWARF stack that is either
291 /// the contents of a register or the result of adding the contents a given
292 /// register to a given signed offset.
293 pub fn dwarfLocOpDeref(self: Register) u8 {
294 switch (@enumToInt(self)) {
295 0...63 => return switch (self.to64()) {
296 .rax => DW.OP.breg0,
297 .rdx => DW.OP.breg1,
298 .rcx => DW.OP.breg2,
299 .rbx => DW.OP.breg3,
300 .rsi => DW.OP.breg4,
301 .rdi => DW.OP.breg5,
302 .rbp => DW.OP.breg6,
303 .rsp => DW.OP.fbreg,
304
305 .r8 => DW.OP.breg8,
306 .r9 => DW.OP.breg9,
307 .r10 => DW.OP.breg10,
308 .r11 => DW.OP.breg11,
309 .r12 => DW.OP.breg12,
310 .r13 => DW.OP.breg13,
311 .r14 => DW.OP.breg14,
312 .r15 => DW.OP.breg15,
313
314 else => unreachable,
315 },
316
317 64...79 => return @as(u8, self.enc()) + DW.OP.breg17,
318
319 else => unreachable,
320 }
282 }321 }
283};322};
284323
src/link/Dwarf.zig+4-1
...@@ -405,8 +405,11 @@ pub const DeclState = struct {...@@ -405,8 +405,11 @@ pub const DeclState = struct {
405 const value: u64 = if (values) |vals| value: {405 const value: u64 = if (values) |vals| value: {
406 if (vals.count() == 0) break :value @intCast(u64, field_i); // auto-numbered406 if (vals.count() == 0) break :value @intCast(u64, field_i); // auto-numbered
407 const value = vals.keys()[field_i];407 const value = vals.keys()[field_i];
408 // TODO do not assume a 64bit enum value - could be bigger.
409 // See https://github.com/ziglang/zig/issues/645
408 var int_buffer: Value.Payload.U64 = undefined;410 var int_buffer: Value.Payload.U64 = undefined;
409 break :value value.enumToInt(ty, &int_buffer).toUnsignedInt(target);411 const field_int_val = value.enumToInt(ty, &int_buffer);
412 break :value @bitCast(u64, field_int_val.toSignedInt());
410 } else @intCast(u64, field_i);413 } else @intCast(u64, field_i);
411 mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), value, target_endian);414 mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), value, target_endian);
412 }415 }
src/link/MachO.zig+2-3
...@@ -329,8 +329,7 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {...@@ -329,8 +329,7 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {
329329
330 if (!options.strip and options.module != null) {330 if (!options.strip and options.module != null) {
331 // Create dSYM bundle.331 // Create dSYM bundle.
332 const dir = options.module.?.zig_cache_artifact_directory;332 log.debug("creating {s}.dSYM bundle", .{emit.sub_path});
333 log.debug("creating {s}.dSYM bundle in {?s}", .{ emit.sub_path, dir.path });
334333
335 const d_sym_path = try fmt.allocPrint(334 const d_sym_path = try fmt.allocPrint(
336 allocator,335 allocator,
...@@ -339,7 +338,7 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {...@@ -339,7 +338,7 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {
339 );338 );
340 defer allocator.free(d_sym_path);339 defer allocator.free(d_sym_path);
341340
342 var d_sym_bundle = try dir.handle.makeOpenPath(d_sym_path, .{});341 var d_sym_bundle = try emit.directory.handle.makeOpenPath(d_sym_path, .{});
343 defer d_sym_bundle.close();342 defer d_sym_bundle.close();
344343
345 const d_sym_file = try d_sym_bundle.createFile(emit.sub_path, .{344 const d_sym_file = try d_sym_bundle.createFile(emit.sub_path, .{
test/behavior/enum.zig-2
...@@ -1146,8 +1146,6 @@ test "size of enum with only one tag which has explicit integer tag type" {...@@ -1146,8 +1146,6 @@ test "size of enum with only one tag which has explicit integer tag type" {
1146}1146}
11471147
1148test "switch on an extern enum with negative value" {1148test "switch on an extern enum with negative value" {
1149 // TODO x86, wasm backends fail because they assume that enum tag types are unsigned
1150 if (@import("builtin").zig_backend == .stage2_x86_64) return error.SkipZigTest;
1151 if (@import("builtin").zig_backend == .stage2_wasm) return error.SkipZigTest;1149 if (@import("builtin").zig_backend == .stage2_wasm) return error.SkipZigTest;
11521150
1153 const Foo = enum(c_int) {1151 const Foo = enum(c_int) {