From 7007ecdc05c2b1dac8a737386c390a5d07533ea7 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 8 Nov 2022 16:07:45 +0100 Subject: [PATCH 1/5] macho: create dSYM bundle directly in the emit dir --- src/link/MachO.zig | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index cc805ee711..a25d13fdf5 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -329,8 +329,7 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO { if (!options.strip and options.module != null) { // Create dSYM bundle. - const dir = options.module.?.zig_cache_artifact_directory; - log.debug("creating {s}.dSYM bundle in {?s}", .{ emit.sub_path, dir.path }); + log.debug("creating {s}.dSYM bundle", .{emit.sub_path}); const d_sym_path = try fmt.allocPrint( allocator, @@ -339,7 +338,7 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO { ); defer allocator.free(d_sym_path); - var d_sym_bundle = try dir.handle.makeOpenPath(d_sym_path, .{}); + var d_sym_bundle = try emit.directory.handle.makeOpenPath(d_sym_path, .{}); defer d_sym_bundle.close(); const d_sym_file = try d_sym_bundle.createFile(emit.sub_path, .{ From 02852098eebda52ce6373d0440cec4965e6d4478 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 9 Nov 2022 16:47:00 +0100 Subject: [PATCH 2/5] 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`. --- src/arch/aarch64/CodeGen.zig | 281 ++++++++++++++++++++++++++++++++--- 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" { From df09d9c14a22098709bd8816e622e6dd785ac954 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 9 Nov 2022 16:53:33 +0100 Subject: [PATCH 3/5] x86_64: add DWARF encoding for vector registers Clean up how we handle emitting of DWARF debug info for `x86_64` codegen. --- src/arch/x86_64/CodeGen.zig | 138 +++++++++++++++++++----------------- src/arch/x86_64/bits.zig | 81 +++++++++++++++------ 2 files changed, 133 insertions(+), 86 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 7b9ef863ae..2bafbfb3d7 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -3797,64 +3797,68 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { const ty = self.air.typeOfIndex(inst); const mcv = self.args[arg_index]; const name = self.mod_fn.getParamName(self.bin_file.options.module.?, arg_index); - const name_with_null = name.ptr[0 .. name.len + 1]; if (self.liveness.isUnused(inst)) return self.finishAirBookkeeping(); - const dst_mcv: MCValue = blk: { - switch (mcv) { - .register => |reg| { - self.register_manager.getRegAssumeFree(reg.to64(), inst); - switch (self.debug_output) { - .dwarf => |dw| { - const dbg_info = &dw.dbg_info; - 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 self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 - dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string - }, - .plan9 => {}, - .none => {}, - } - break :blk mcv; - }, - .stack_offset => |off| { - const offset = @intCast(i32, self.max_end_stack) - off + 16; - switch (self.debug_output) { - .dwarf => |dw| { - const dbg_info = &dw.dbg_info; - 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 - DW.OP.breg6, // .rbp TODO handle -fomit-frame-pointer - }); - leb128.writeILEB128(dbg_info.writer(), 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 self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 - dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string - - }, - .plan9 => {}, - .none => {}, - } - break :blk MCValue{ .stack_offset = -offset }; - }, - else => return self.fail("TODO implement arg for {}", .{mcv}), - } + const dst_mcv: MCValue = switch (mcv) { + .register => |reg| blk: { + self.register_manager.getRegAssumeFree(reg.to64(), inst); + break :blk MCValue{ .register = reg }; + }, + .stack_offset => |off| blk: { + const offset = @intCast(i32, self.max_end_stack) - off + 16; + break :blk MCValue{ .stack_offset = -offset }; + }, + else => return self.fail("TODO implement arg for {}", .{mcv}), }; + try self.genArgDbgInfo(ty, name, dst_mcv); return self.finishAir(inst, dst_mcv, .{ .none, .none, .none }); } +fn genArgDbgInfo(self: Self, ty: Type, name: [:0]const u8, mcv: MCValue) !void { + const name_with_null = name.ptr[0 .. name.len + 1]; + switch (self.debug_output) { + .dwarf => |dw| { + const dbg_info = &dw.dbg_info; + switch (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 self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 + dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string + }, + + .stack_offset => |off| { + 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.rbp.dwarfLocOpDeref(), // TODO handle -fomit-frame-pointer + }); + leb128.writeILEB128(dbg_info.writer(), -off) catch unreachable; + dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2); + try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); + try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 + dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string + + }, + + else => unreachable, // not a valid function parameter + } + }, + .plan9 => {}, + .none => {}, + } +} + fn airBreakpoint(self: *Self) !void { _ = try self.addInst(.{ .tag = .interrupt, @@ -4424,7 +4428,7 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { } fn genVarDbgInfo( - self: *Self, + self: Self, tag: Air.Inst.Tag, ty: Type, mcv: MCValue, @@ -4445,17 +4449,23 @@ fn genVarDbgInfo( reg.dwarfLocOp(), }); }, - .ptr_stack_offset, .stack_offset => |off| { + + .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 - DW.OP.breg6, // .rbp TODO handle -fomit-frame-pointer + Register.rbp.dwarfLocOpDeref(), // TODO handle -fomit-frame-pointer }); leb128.writeILEB128(dbg_info.writer(), -off) catch unreachable; dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2); }, - .memory, .linker_load => { + + .memory, + .linker_load, + => { const ptr_width = @intCast(u8, @divExact(self.target.cpu.arch.ptrBitWidth(), 8)); const is_ptr = switch (tag) { .dbg_var_ptr => true, @@ -4494,27 +4504,23 @@ fn genVarDbgInfo( else => {}, } }, + .immediate => |x| { - const signedness: std.builtin.Signedness = blk: { - if (ty.zigTypeTag() != .Int) break :blk .unsigned; - break :blk ty.intInfo(self.target.*).signedness; - }; try dbg_info.ensureUnusedCapacity(2); const fixup = dbg_info.items.len; dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc 1, - switch (signedness) { - .signed => DW.OP.consts, - .unsigned => DW.OP.constu, - }, + if (ty.isSignedInt()) DW.OP.consts else DW.OP.constu, }); - switch (signedness) { - .signed => try leb128.writeILEB128(dbg_info.writer(), @bitCast(i64, x)), - .unsigned => try leb128.writeULEB128(dbg_info.writer(), x), + 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) @@ -4530,12 +4536,14 @@ fn genVarDbgInfo( 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, }); }, + else => { try dbg_info.ensureUnusedCapacity(2); dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc @@ -4556,7 +4564,7 @@ fn genVarDbgInfo( /// 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 => |dw| { const dbg_info = &dw.dbg_info; diff --git a/src/arch/x86_64/bits.zig b/src/arch/x86_64/bits.zig index a2d523e84c..cc123b96b6 100644 --- a/src/arch/x86_64/bits.zig +++ b/src/arch/x86_64/bits.zig @@ -135,8 +135,6 @@ pub const Condition = enum(u5) { } }; -// zig fmt: off - /// Definitions of all of the general purpose x64 registers. The order is semantically meaningful. /// The registers are defined such that IDs go in descending order of 64-bit, /// 32-bit, 16-bit, and then 8-bit, and each set contains exactly sixteen @@ -152,6 +150,7 @@ pub const Condition = enum(u5) { /// The ID can be easily determined by figuring out what range the register is /// in, and then subtracting the base. pub const Register = enum(u7) { + // zig fmt: off // 0 through 15, 64-bit registers. 8-15 are extended. // id is just the int value. rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi, @@ -184,6 +183,7 @@ pub const Register = enum(u7) { // Pseudo-value for MIR instructions. none, + // zig fmt: on pub fn id(self: Register) u7 { return switch (@enumToInt(self)) { @@ -192,7 +192,7 @@ pub const Register = enum(u7) { else => unreachable, }; } - + /// Returns the bit-width of the register. pub fn size(self: Register) u9 { return switch (@enumToInt(self)) { @@ -258,27 +258,66 @@ pub const Register = enum(u7) { } pub fn dwarfLocOp(self: Register) u8 { - return switch (self.to64()) { - .rax => DW.OP.reg0, - .rdx => DW.OP.reg1, - .rcx => DW.OP.reg2, - .rbx => DW.OP.reg3, - .rsi => DW.OP.reg4, - .rdi => DW.OP.reg5, - .rbp => DW.OP.reg6, - .rsp => DW.OP.reg7, + switch (@enumToInt(self)) { + 0...63 => return switch (self.to64()) { + .rax => DW.OP.reg0, + .rdx => DW.OP.reg1, + .rcx => DW.OP.reg2, + .rbx => DW.OP.reg3, + .rsi => DW.OP.reg4, + .rdi => DW.OP.reg5, + .rbp => DW.OP.reg6, + .rsp => DW.OP.reg7, - .r8 => DW.OP.reg8, - .r9 => DW.OP.reg9, - .r10 => DW.OP.reg10, - .r11 => DW.OP.reg11, - .r12 => DW.OP.reg12, - .r13 => DW.OP.reg13, - .r14 => DW.OP.reg14, - .r15 => DW.OP.reg15, + .r8 => DW.OP.reg8, + .r9 => DW.OP.reg9, + .r10 => DW.OP.reg10, + .r11 => DW.OP.reg11, + .r12 => DW.OP.reg12, + .r13 => DW.OP.reg13, + .r14 => DW.OP.reg14, + .r15 => DW.OP.reg15, + + else => unreachable, + }, + + 64...79 => return @as(u8, self.enc()) + DW.OP.reg17, else => unreachable, - }; + } + } + + /// 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 { + switch (@enumToInt(self)) { + 0...63 => return switch (self.to64()) { + .rax => DW.OP.breg0, + .rdx => DW.OP.breg1, + .rcx => DW.OP.breg2, + .rbx => DW.OP.breg3, + .rsi => DW.OP.breg4, + .rdi => DW.OP.breg5, + .rbp => DW.OP.breg6, + .rsp => DW.OP.fbreg, + + .r8 => DW.OP.breg8, + .r9 => DW.OP.breg9, + .r10 => DW.OP.breg10, + .r11 => DW.OP.breg11, + .r12 => DW.OP.breg12, + .r13 => DW.OP.breg13, + .r14 => DW.OP.breg14, + .r15 => DW.OP.breg15, + + else => unreachable, + }, + + 64...79 => return @as(u8, self.enc()) + DW.OP.breg17, + + else => unreachable, + } } }; From 31e755df6f3ae7e7c5a1457ca69f2d2cfb0af415 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 9 Nov 2022 19:58:14 +0100 Subject: [PATCH 4/5] aarch64: handle .stack_argument_offset as a valid local var --- src/arch/aarch64/CodeGen.zig | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index a117e4a083..e672364dc2 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -261,14 +261,23 @@ const DbgInfoReloc = struct { .ptr_stack_offset, .stack_offset, - => |off| { + .stack_argument_offset, + => |offset| { + const adjusted_offset = switch (reloc.mcv) { + .ptr_stack_offset, + .stack_offset, + => -@intCast(i32, offset), + .stack_argument_offset => @intCast(i32, function.saved_regs_stack_space + offset), + else => unreachable, + }; + 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; + leb128.writeILEB128(dbg_info.writer(), adjusted_offset) catch unreachable; dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2); }, @@ -353,8 +362,6 @@ const DbgInfoReloc = struct { }); }, - .stack_argument_offset => unreachable, - else => { try dbg_info.ensureUnusedCapacity(2); dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc From 0914e0a4ecabc1c9f4b4a8675955ab634065449e Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 10 Nov 2022 09:36:45 +0100 Subject: [PATCH 5/5] dwarf: do not assume unsigned 64bit integer for the enum value --- src/link/Dwarf.zig | 5 ++++- test/behavior/enum.zig | 2 -- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index cbe34f7499..f821cb9709 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -405,8 +405,11 @@ pub const DeclState = struct { const value: u64 = if (values) |vals| value: { if (vals.count() == 0) break :value @intCast(u64, field_i); // auto-numbered const value = vals.keys()[field_i]; + // TODO do not assume a 64bit enum value - could be bigger. + // See https://github.com/ziglang/zig/issues/645 var int_buffer: Value.Payload.U64 = undefined; - break :value value.enumToInt(ty, &int_buffer).toUnsignedInt(target); + const field_int_val = value.enumToInt(ty, &int_buffer); + break :value @bitCast(u64, field_int_val.toSignedInt()); } else @intCast(u64, field_i); mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), value, target_endian); } diff --git a/test/behavior/enum.zig b/test/behavior/enum.zig index b00c7dbca2..a6e417f2ae 100644 --- a/test/behavior/enum.zig +++ b/test/behavior/enum.zig @@ -1146,8 +1146,6 @@ test "size of enum with only one tag which has explicit integer tag type" { } test "switch on an extern enum with negative value" { - // TODO x86, wasm backends fail because they assume that enum tag types are unsigned - if (@import("builtin").zig_backend == .stage2_x86_64) return error.SkipZigTest; if (@import("builtin").zig_backend == .stage2_wasm) return error.SkipZigTest; const Foo = enum(c_int) {