Merge pull request #13495 from ziglang/macho-dsym

stage2: misc DWARF debug info fixes and additions for x86_64 and aarch64
This commit is contained in:
Jakub Konka 2022-11-10 16:50:57 +01:00 committed by GitHub
commit 4b3637820d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 411 additions and 115 deletions

View File

@ -51,13 +51,14 @@ gpa: Allocator,
air: Air, air: Air,
liveness: Liveness, liveness: Liveness,
bin_file: *link.File, bin_file: *link.File,
debug_output: DebugInfoOutput,
target: *const std.Target, target: *const std.Target,
mod_fn: *const Module.Fn, mod_fn: *const Module.Fn,
err_msg: ?*ErrorMsg, err_msg: ?*ErrorMsg,
args: []MCValue, args: []MCValue,
ret_mcv: MCValue, ret_mcv: MCValue,
fn_type: Type, fn_type: Type,
arg_index: usize, arg_index: u32,
src_loc: Module.SrcLoc, src_loc: Module.SrcLoc,
stack_align: u32, stack_align: u32,
@ -75,6 +76,12 @@ end_di_column: u32,
/// which is a relative jump, based on the address following the reloc. /// which is a relative jump, based on the address following the reloc.
exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .{}, 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, /// 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" /// 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. /// of the table of mappings from instructions to `MCValue` from within the branch.
@ -160,6 +167,220 @@ const MCValue = union(enum) {
stack_argument_offset: u32, 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,
.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(), adjusted_offset) 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,
});
},
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 { const Branch = struct {
inst_table: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, MCValue) = .{}, inst_table: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, MCValue) = .{},
@ -262,6 +483,7 @@ pub fn generate(
.gpa = bin_file.allocator, .gpa = bin_file.allocator,
.air = air, .air = air,
.liveness = liveness, .liveness = liveness,
.debug_output = debug_output,
.target = &bin_file.options.target, .target = &bin_file.options.target,
.bin_file = bin_file, .bin_file = bin_file,
.mod_fn = module_fn, .mod_fn = module_fn,
@ -279,6 +501,7 @@ pub fn generate(
defer function.stack.deinit(bin_file.allocator); defer function.stack.deinit(bin_file.allocator);
defer function.blocks.deinit(bin_file.allocator); defer function.blocks.deinit(bin_file.allocator);
defer function.exitlude_jump_relocs.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) { var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {
error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
@ -302,6 +525,10 @@ pub fn generate(
else => |e| return e, else => |e| return e,
}; };
for (function.dbg_info_relocs.items) |reloc| {
try reloc.genDbgInfo(function);
}
var mir = Mir{ var mir = Mir{
.instructions = function.mir_instructions.toOwnedSlice(), .instructions = function.mir_instructions.toOwnedSlice(),
.extra = function.mir_extra.toOwnedSlice(bin_file.allocator), .extra = function.mir_extra.toOwnedSlice(bin_file.allocator),
@ -854,23 +1081,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, /// Adds a Type to the .debug_info at the current position. The bytes will be populated later,
/// after codegen for this symbol is done. /// 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) { switch (self.debug_output) {
.dwarf => |dbg_out| { .dwarf => |dw| {
assert(ty.hasRuntimeBits()); const dbg_info = &dw.dbg_info;
const index = dbg_out.dbg_info.items.len; const index = dbg_info.items.len;
try dbg_out.dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4 try dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4
const mod = self.bin_file.options.module.?;
const gop = try dbg_out.dbg_info_type_relocs.getOrPutContext(self.gpa, ty, .{ const fn_owner_decl = mod.declPtr(self.mod_fn.owner_decl);
.target = self.target.*, const atom = switch (self.bin_file.tag) {
}); .elf => &fn_owner_decl.link.elf.dbg_info_atom,
if (!gop.found_existing) { .macho => &fn_owner_decl.link.macho.dbg_info_atom,
gop.value_ptr.* = .{ else => unreachable,
.off = undefined, };
.relocs = .{}, try dw.addTypeRelocGlobal(atom, ty, @intCast(u32, index));
};
}
try gop.value_ptr.relocs.append(self.gpa, @intCast(u32, index));
}, },
.plan9 => {}, .plan9 => {},
.none => {}, .none => {},
@ -3957,8 +4181,9 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
self.arg_index += 1; self.arg_index += 1;
const ty = self.air.typeOfIndex(inst); const ty = self.air.typeOfIndex(inst);
const result = self.args[arg_index]; const result = self.args[arg_index];
const name = self.mod_fn.getParamName(self.bin_file.options.module.?, arg_index);
const mcv = switch (result) { const mcv = switch (result) {
// Copy registers to the stack // Copy registers to the stack
.register => |reg| blk: { .register => |reg| blk: {
@ -3974,8 +4199,14 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
}, },
else => result, 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)) if (self.liveness.isUnused(inst))
return self.finishAirBookkeeping(); return self.finishAirBookkeeping();
@ -4463,10 +4694,21 @@ fn airDbgBlock(self: *Self, inst: Air.Inst.Index) !void {
fn airDbgVar(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 pl_op = self.air.instructions.items(.data)[inst].pl_op;
const name = self.air.nullTerminatedString(pl_op.payload);
const operand = pl_op.operand; const operand = pl_op.operand;
// TODO emit debug info for this variable const tag = self.air.instructions.items(.tag)[inst];
_ = name; 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 }); return self.finishAir(inst, .dead, .{ operand, .none, .none });
} }

View File

@ -296,6 +296,13 @@ pub const Register = enum(u8) {
pub fn dwarfLocOp(self: Register) u8 { pub fn dwarfLocOp(self: Register) u8 {
return @as(u8, self.enc()) + DW.OP.reg0; 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" { test "Register.enc" {

View File

@ -3797,64 +3797,68 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
const ty = self.air.typeOfIndex(inst); const ty = self.air.typeOfIndex(inst);
const mcv = self.args[arg_index]; const mcv = self.args[arg_index];
const name = self.mod_fn.getParamName(self.bin_file.options.module.?, 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)) if (self.liveness.isUnused(inst))
return self.finishAirBookkeeping(); return self.finishAirBookkeeping();
const dst_mcv: MCValue = blk: { const dst_mcv: MCValue = switch (mcv) {
switch (mcv) { .register => |reg| blk: {
.register => |reg| { self.register_manager.getRegAssumeFree(reg.to64(), inst);
self.register_manager.getRegAssumeFree(reg.to64(), inst); break :blk MCValue{ .register = reg };
switch (self.debug_output) { },
.dwarf => |dw| { .stack_offset => |off| blk: {
const dbg_info = &dw.dbg_info; const offset = @intCast(i32, self.max_end_stack) - off + 16;
try dbg_info.ensureUnusedCapacity(3); break :blk MCValue{ .stack_offset = -offset };
dbg_info.appendAssumeCapacity(@enumToInt(link.File.Dwarf.AbbrevKind.parameter)); },
dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc else => return self.fail("TODO implement arg for {}", .{mcv}),
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}),
}
}; };
try self.genArgDbgInfo(ty, name, dst_mcv);
return self.finishAir(inst, dst_mcv, .{ .none, .none, .none }); 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 { fn airBreakpoint(self: *Self) !void {
_ = try self.addInst(.{ _ = try self.addInst(.{
.tag = .interrupt, .tag = .interrupt,
@ -4424,7 +4428,7 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
} }
fn genVarDbgInfo( fn genVarDbgInfo(
self: *Self, self: Self,
tag: Air.Inst.Tag, tag: Air.Inst.Tag,
ty: Type, ty: Type,
mcv: MCValue, mcv: MCValue,
@ -4445,17 +4449,23 @@ fn genVarDbgInfo(
reg.dwarfLocOp(), reg.dwarfLocOp(),
}); });
}, },
.ptr_stack_offset, .stack_offset => |off| {
.ptr_stack_offset,
.stack_offset,
=> |off| {
try dbg_info.ensureUnusedCapacity(7); try dbg_info.ensureUnusedCapacity(7);
const fixup = dbg_info.items.len; const fixup = dbg_info.items.len;
dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
1, // we will backpatch it after we encode the displacement in LEB128 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; leb128.writeILEB128(dbg_info.writer(), -off) catch unreachable;
dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2); 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 ptr_width = @intCast(u8, @divExact(self.target.cpu.arch.ptrBitWidth(), 8));
const is_ptr = switch (tag) { const is_ptr = switch (tag) {
.dbg_var_ptr => true, .dbg_var_ptr => true,
@ -4494,27 +4504,23 @@ fn genVarDbgInfo(
else => {}, else => {},
} }
}, },
.immediate => |x| { .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); try dbg_info.ensureUnusedCapacity(2);
const fixup = dbg_info.items.len; const fixup = dbg_info.items.len;
dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
1, 1,
switch (signedness) { if (ty.isSignedInt()) DW.OP.consts else DW.OP.constu,
.signed => DW.OP.consts,
.unsigned => DW.OP.constu,
},
}); });
switch (signedness) { if (ty.isSignedInt()) {
.signed => try leb128.writeILEB128(dbg_info.writer(), @bitCast(i64, x)), try leb128.writeILEB128(dbg_info.writer(), @bitCast(i64, x));
.unsigned => try leb128.writeULEB128(dbg_info.writer(), x), } else {
try leb128.writeULEB128(dbg_info.writer(), x);
} }
try dbg_info.append(DW.OP.stack_value); try dbg_info.append(DW.OP.stack_value);
dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2); dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2);
}, },
.undef => { .undef => {
// DW.AT.location, DW.FORM.exprloc // DW.AT.location, DW.FORM.exprloc
// uleb128(exprloc_len) // uleb128(exprloc_len)
@ -4530,12 +4536,14 @@ fn genVarDbgInfo(
dbg_info.appendSliceAssumeCapacity(implicit_value_len.items); dbg_info.appendSliceAssumeCapacity(implicit_value_len.items);
dbg_info.appendNTimesAssumeCapacity(0xaa, abi_size); dbg_info.appendNTimesAssumeCapacity(0xaa, abi_size);
}, },
.none => { .none => {
try dbg_info.ensureUnusedCapacity(3); try dbg_info.ensureUnusedCapacity(3);
dbg_info.appendSliceAssumeCapacity(&[3]u8{ // DW.AT.location, DW.FORM.exprloc dbg_info.appendSliceAssumeCapacity(&[3]u8{ // DW.AT.location, DW.FORM.exprloc
2, DW.OP.lit0, DW.OP.stack_value, 2, DW.OP.lit0, DW.OP.stack_value,
}); });
}, },
else => { else => {
try dbg_info.ensureUnusedCapacity(2); try dbg_info.ensureUnusedCapacity(2);
dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc 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, /// Adds a Type to the .debug_info at the current position. The bytes will be populated later,
/// after codegen for this symbol is done. /// 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) { switch (self.debug_output) {
.dwarf => |dw| { .dwarf => |dw| {
const dbg_info = &dw.dbg_info; const dbg_info = &dw.dbg_info;

View File

@ -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. /// 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, /// 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 /// 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 /// The ID can be easily determined by figuring out what range the register is
/// in, and then subtracting the base. /// in, and then subtracting the base.
pub const Register = enum(u7) { pub const Register = enum(u7) {
// zig fmt: off
// 0 through 15, 64-bit registers. 8-15 are extended. // 0 through 15, 64-bit registers. 8-15 are extended.
// id is just the int value. // id is just the int value.
rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi, rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi,
@ -184,6 +183,7 @@ pub const Register = enum(u7) {
// Pseudo-value for MIR instructions. // Pseudo-value for MIR instructions.
none, none,
// zig fmt: on
pub fn id(self: Register) u7 { pub fn id(self: Register) u7 {
return switch (@enumToInt(self)) { return switch (@enumToInt(self)) {
@ -192,7 +192,7 @@ pub const Register = enum(u7) {
else => unreachable, else => unreachable,
}; };
} }
/// Returns the bit-width of the register. /// Returns the bit-width of the register.
pub fn size(self: Register) u9 { pub fn size(self: Register) u9 {
return switch (@enumToInt(self)) { return switch (@enumToInt(self)) {
@ -258,27 +258,66 @@ pub const Register = enum(u7) {
} }
pub fn dwarfLocOp(self: Register) u8 { pub fn dwarfLocOp(self: Register) u8 {
return switch (self.to64()) { switch (@enumToInt(self)) {
.rax => DW.OP.reg0, 0...63 => return switch (self.to64()) {
.rdx => DW.OP.reg1, .rax => DW.OP.reg0,
.rcx => DW.OP.reg2, .rdx => DW.OP.reg1,
.rbx => DW.OP.reg3, .rcx => DW.OP.reg2,
.rsi => DW.OP.reg4, .rbx => DW.OP.reg3,
.rdi => DW.OP.reg5, .rsi => DW.OP.reg4,
.rbp => DW.OP.reg6, .rdi => DW.OP.reg5,
.rsp => DW.OP.reg7, .rbp => DW.OP.reg6,
.rsp => DW.OP.reg7,
.r8 => DW.OP.reg8, .r8 => DW.OP.reg8,
.r9 => DW.OP.reg9, .r9 => DW.OP.reg9,
.r10 => DW.OP.reg10, .r10 => DW.OP.reg10,
.r11 => DW.OP.reg11, .r11 => DW.OP.reg11,
.r12 => DW.OP.reg12, .r12 => DW.OP.reg12,
.r13 => DW.OP.reg13, .r13 => DW.OP.reg13,
.r14 => DW.OP.reg14, .r14 => DW.OP.reg14,
.r15 => DW.OP.reg15, .r15 => DW.OP.reg15,
else => unreachable,
},
64...79 => return @as(u8, self.enc()) + DW.OP.reg17,
else => unreachable, 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,
}
} }
}; };

View File

@ -405,8 +405,11 @@ pub const DeclState = struct {
const value: u64 = if (values) |vals| value: { const value: u64 = if (values) |vals| value: {
if (vals.count() == 0) break :value @intCast(u64, field_i); // auto-numbered if (vals.count() == 0) break :value @intCast(u64, field_i); // auto-numbered
const value = vals.keys()[field_i]; 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; 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); } else @intCast(u64, field_i);
mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), value, target_endian); mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), value, target_endian);
} }

View File

@ -329,8 +329,7 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {
if (!options.strip and options.module != null) { if (!options.strip and options.module != null) {
// Create dSYM bundle. // Create dSYM bundle.
const dir = options.module.?.zig_cache_artifact_directory; log.debug("creating {s}.dSYM bundle", .{emit.sub_path});
log.debug("creating {s}.dSYM bundle in {?s}", .{ emit.sub_path, dir.path });
const d_sym_path = try fmt.allocPrint( const d_sym_path = try fmt.allocPrint(
allocator, allocator,
@ -339,7 +338,7 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {
); );
defer allocator.free(d_sym_path); 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(); defer d_sym_bundle.close();
const d_sym_file = try d_sym_bundle.createFile(emit.sub_path, .{ const d_sym_file = try d_sym_bundle.createFile(emit.sub_path, .{

View File

@ -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" { 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; if (@import("builtin").zig_backend == .stage2_wasm) return error.SkipZigTest;
const Foo = enum(c_int) { const Foo = enum(c_int) {