aboutsummaryrefslogtreecommitdiff
path: root/src/arch/x86_64/CodeGen.zig
diff options
context:
space:
mode:
authorJacob Young <jacobly0@users.noreply.github.com>2024-08-06 11:22:37 -0400
committerJacob Young <jacobly0@users.noreply.github.com>2024-08-16 15:22:55 -0400
commitef11bc9899002620d67cfce9c79b6c0dc0f5ea61 (patch)
tree7b05fe17340c06e4c40c45ebe249361c0c281c72 /src/arch/x86_64/CodeGen.zig
parent90989be0e31a91335f8d1c1eafb84c3b34792a8c (diff)
downloadzig-ef11bc9899002620d67cfce9c79b6c0dc0f5ea61.tar.gz
zig-ef11bc9899002620d67cfce9c79b6c0dc0f5ea61.zip
Dwarf: rework self-hosted debug info from scratch
This is in preparation for incremental and actually being able to debug executables built by the x86_64 backend.
Diffstat (limited to 'src/arch/x86_64/CodeGen.zig')
-rw-r--r--src/arch/x86_64/CodeGen.zig233
1 files changed, 138 insertions, 95 deletions
diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig
index 3bf9eaafbe..9a38346580 100644
--- a/src/arch/x86_64/CodeGen.zig
+++ b/src/arch/x86_64/CodeGen.zig
@@ -18,7 +18,6 @@ const Allocator = mem.Allocator;
const CodeGenError = codegen.CodeGenError;
const Compilation = @import("../../Compilation.zig");
const DebugInfoOutput = codegen.DebugInfoOutput;
-const DW = std.dwarf;
const ErrorMsg = Zcu.ErrorMsg;
const Result = codegen.Result;
const Emit = @import("Emit.zig");
@@ -82,6 +81,9 @@ mir_instructions: std.MultiArrayList(Mir.Inst) = .{},
/// MIR extra data
mir_extra: std.ArrayListUnmanaged(u32) = .{},
+stack_args: std.ArrayListUnmanaged(StackVar) = .{},
+stack_vars: std.ArrayListUnmanaged(StackVar) = .{},
+
/// Byte offset within the source file of the ending curly.
end_di_line: u32,
end_di_column: u32,
@@ -726,6 +728,12 @@ const InstTracking = struct {
}
};
+const StackVar = struct {
+ name: []const u8,
+ type: Type,
+ frame_addr: FrameAddr,
+};
+
const FrameAlloc = struct {
abi_size: u31,
spill_pad: u3,
@@ -831,6 +839,8 @@ pub fn generate(
function.exitlude_jump_relocs.deinit(gpa);
function.mir_instructions.deinit(gpa);
function.mir_extra.deinit(gpa);
+ function.stack_args.deinit(gpa);
+ function.stack_vars.deinit(gpa);
}
wip_mir_log.debug("{}:", .{fmtNav(func.owner_nav, ip)});
@@ -903,14 +913,17 @@ pub fn generate(
else => |e| return e,
};
- var mir = Mir{
+ try function.genStackVarDebugInfo(.local_arg, function.stack_args.items);
+ try function.genStackVarDebugInfo(.local_var, function.stack_vars.items);
+
+ var mir: Mir = .{
.instructions = function.mir_instructions.toOwnedSlice(),
.extra = try function.mir_extra.toOwnedSlice(gpa),
.frame_locs = function.frame_locs.toOwnedSlice(),
};
defer mir.deinit(gpa);
- var emit = Emit{
+ var emit: Emit = .{
.lower = .{
.bin_file = bin_file,
.allocator = gpa,
@@ -2425,7 +2438,7 @@ fn computeFrameLayout(self: *Self, cc: std.builtin.CallingConvention) !FrameLayo
const callee_preserved_regs =
abi.getCalleePreservedRegs(abi.resolveCallingConvention(cc, self.target.*));
for (callee_preserved_regs) |reg| {
- if (self.register_manager.isRegAllocated(reg) or true) {
+ if (self.register_manager.isRegAllocated(reg)) {
save_reg_list.push(callee_preserved_regs, reg);
}
}
@@ -5985,10 +5998,7 @@ fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void {
switch (operand) {
.load_frame => |frame_addr| {
if (tag_abi_size <= 8) {
- const off: i32 = if (layout.tag_align.compare(.lt, layout.payload_align))
- @intCast(layout.payload_size)
- else
- 0;
+ const off: i32 = @intCast(layout.tagOffset());
break :blk try self.copyToRegisterWithInstTracking(inst, tag_ty, .{
.load_frame = .{ .index = frame_addr.index, .off = frame_addr.off + off },
});
@@ -6000,10 +6010,7 @@ fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void {
);
},
.register => {
- const shift: u6 = if (layout.tag_align.compare(.lt, layout.payload_align))
- @intCast(layout.payload_size * 8)
- else
- 0;
+ const shift: u6 = @intCast(layout.tagOffset() * 8);
const result = try self.copyToRegisterWithInstTracking(inst, union_ty, operand);
try self.genShiftBinOpMir(
.{ ._r, .sh },
@@ -11819,7 +11826,16 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
while (self.args[arg_index] == .none) arg_index += 1;
self.arg_index = arg_index + 1;
- const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: {
+ const result: MCValue = if (self.debug_output == .none and self.liveness.isUnused(inst)) .unreach else result: {
+ const name = switch (self.debug_output) {
+ .none => "",
+ else => name: {
+ const name_nts = self.air.instructions.items(.data)[@intFromEnum(inst)].arg.name;
+ break :name self.air.nullTerminatedString(@intFromEnum(name_nts));
+ },
+ };
+ if (name.len == 0 and self.liveness.isUnused(inst)) break :result .unreach;
+
const arg_ty = self.typeOfIndex(inst);
const src_mcv = self.args[arg_index];
const dst_mcv = switch (src_mcv) {
@@ -11922,90 +11938,86 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
else => return self.fail("TODO implement arg for {}", .{src_mcv}),
};
- const name_nts = self.air.instructions.items(.data)[@intFromEnum(inst)].arg.name;
- switch (name_nts) {
- .none => {},
- _ => try self.genArgDbgInfo(arg_ty, self.air.nullTerminatedString(@intFromEnum(name_nts)), src_mcv),
- }
+ if (name.len > 0) try self.genVarDebugInfo(.local_arg, .dbg_var_val, name, arg_ty, dst_mcv);
+ if (self.liveness.isUnused(inst)) {
+ assert(self.debug_output != .none and name.len > 0);
+ try self.freeValue(dst_mcv);
+ break :result .none;
+ }
break :result dst_mcv;
};
return self.finishAir(inst, result, .{ .none, .none, .none });
}
-fn genArgDbgInfo(self: Self, ty: Type, name: [:0]const u8, mcv: MCValue) !void {
+fn genVarDebugInfo(
+ self: *Self,
+ var_tag: link.File.Dwarf.WipNav.VarTag,
+ tag: Air.Inst.Tag,
+ name: []const u8,
+ ty: Type,
+ mcv: MCValue,
+) !void {
+ const stack_vars = switch (var_tag) {
+ .local_arg => &self.stack_args,
+ .local_var => &self.stack_vars,
+ };
switch (self.debug_output) {
- .dwarf => |dw| {
- const loc: link.File.Dwarf.NavState.DbgInfoLoc = switch (mcv) {
- .register => |reg| .{ .register = reg.dwarfNum() },
- .register_pair => |regs| .{ .register_pair = .{
- regs[0].dwarfNum(), regs[1].dwarfNum(),
- } },
- // TODO use a frame index
- .load_frame, .elementwise_regs_then_frame => return,
- //.stack_offset => |off| .{
- // .stack = .{
- // // TODO handle -fomit-frame-pointer
- // .fp_register = Register.rbp.dwarfNum(),
- // .offset = -off,
- // },
- //},
- else => unreachable, // not a valid function parameter
- };
- // TODO: this might need adjusting like the linkers do.
- // Instead of flattening the owner and passing Decl.Index here we may
- // want to special case LazySymbol in DWARF linker too.
- try dw.genArgDbgInfo(name, ty, self.owner.nav_index, loc);
+ .dwarf => |dwarf| switch (tag) {
+ else => unreachable,
+ .dbg_var_ptr => {
+ const var_ty = ty.childType(self.pt.zcu);
+ switch (mcv) {
+ else => {
+ log.info("dbg_var_ptr({s}({}))", .{ @tagName(mcv), mcv });
+ unreachable;
+ },
+ .unreach, .dead, .elementwise_regs_then_frame, .reserved_frame, .air_ref => unreachable,
+ .lea_frame => |frame_addr| try stack_vars.append(self.gpa, .{
+ .name = name,
+ .type = var_ty,
+ .frame_addr = frame_addr,
+ }),
+ .lea_symbol => |sym_off| try dwarf.genVarDebugInfo(var_tag, name, var_ty, .{ .plus = .{
+ &.{ .addr = .{ .sym = sym_off.sym } },
+ &.{ .consts = sym_off.off },
+ } }),
+ }
+ },
+ .dbg_var_val => switch (mcv) {
+ .none => try dwarf.genVarDebugInfo(var_tag, name, ty, .empty),
+ .unreach, .dead, .elementwise_regs_then_frame, .reserved_frame, .air_ref => unreachable,
+ .immediate => |immediate| try dwarf.genVarDebugInfo(var_tag, name, ty, .{ .stack_value = &.{
+ .constu = immediate,
+ } }),
+ else => {
+ const frame_index = try self.allocFrameIndex(FrameAlloc.initSpill(ty, self.pt));
+ try self.genSetMem(.{ .frame = frame_index }, 0, ty, mcv, .{});
+ try stack_vars.append(self.gpa, .{
+ .name = name,
+ .type = ty,
+ .frame_addr = .{ .index = frame_index },
+ });
+ },
+ },
},
.plan9 => {},
.none => {},
}
}
-fn genVarDbgInfo(
+fn genStackVarDebugInfo(
self: Self,
- tag: Air.Inst.Tag,
- ty: Type,
- mcv: MCValue,
- name: [:0]const u8,
+ var_tag: link.File.Dwarf.WipNav.VarTag,
+ stack_vars: []const StackVar,
) !void {
- const is_ptr = switch (tag) {
- .dbg_var_ptr => true,
- .dbg_var_val => false,
- else => unreachable,
- };
-
switch (self.debug_output) {
- .dwarf => |dw| {
- const loc: link.File.Dwarf.NavState.DbgInfoLoc = switch (mcv) {
- .register => |reg| .{ .register = reg.dwarfNum() },
- // TODO use a frame index
- .load_frame, .lea_frame => return,
- //=> |off| .{ .stack = .{
- // .fp_register = Register.rbp.dwarfNum(),
- // .offset = -off,
- //} },
- .memory => |address| .{ .memory = address },
- .load_symbol => |sym_off| loc: {
- assert(sym_off.off == 0);
- break :loc .{ .linker_load = .{ .type = .direct, .sym_index = sym_off.sym } };
- }, // TODO
- .load_got => |sym_index| .{ .linker_load = .{ .type = .got, .sym_index = sym_index } },
- .load_direct => |sym_index| .{
- .linker_load = .{ .type = .direct, .sym_index = sym_index },
- },
- .immediate => |x| .{ .immediate = x },
- .undef => .undef,
- .none => .none,
- else => blk: {
- log.debug("TODO generate debug info for {}", .{mcv});
- break :blk .nop;
- },
- };
- // TODO: this might need adjusting like the linkers do.
- // Instead of flattening the owner and passing Decl.Index here we may
- // want to special case LazySymbol in DWARF linker too.
- try dw.genVarDbgInfo(name, ty, self.owner.nav_index, is_ptr, loc);
+ .dwarf => |dwarf| for (stack_vars) |stack_var| {
+ const frame_loc = self.frame_locs.get(@intFromEnum(stack_var.frame_addr.index));
+ try dwarf.genVarDebugInfo(var_tag, stack_var.name, stack_var.type, .{ .plus = .{
+ &.{ .breg = frame_loc.base.dwarfNum() },
+ &.{ .consts = @as(i33, frame_loc.disp) + stack_var.frame_addr.off },
+ } });
},
.plan9 => {},
.none => {},
@@ -13045,7 +13057,7 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
const name = self.air.nullTerminatedString(pl_op.payload);
const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)];
- try self.genVarDbgInfo(tag, ty, mcv, name);
+ try self.genVarDebugInfo(.local_var, tag, name, ty, mcv);
return self.finishAir(inst, .unreach, .{ operand, .none, .none });
}
@@ -13154,13 +13166,17 @@ fn isNull(self: *Self, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) !MC
.lea_direct,
.lea_got,
.lea_tlv,
- .lea_frame,
.lea_symbol,
.elementwise_regs_then_frame,
.reserved_frame,
.air_ref,
=> unreachable,
+ .lea_frame => {
+ self.eflags_inst = null;
+ return .{ .immediate = @intFromBool(false) };
+ },
+
.register => |opt_reg| {
if (some_info.off == 0) {
const some_abi_size: u32 = @intCast(some_info.ty.abiSize(pt));
@@ -13402,7 +13418,8 @@ fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void {
const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
const operand = try self.resolveInst(un_op);
const ty = self.typeOf(un_op);
- const result = switch (try self.isNull(inst, ty, operand)) {
+ const result: MCValue = switch (try self.isNull(inst, ty, operand)) {
+ .immediate => |imm| .{ .immediate = @intFromBool(imm == 0) },
.eflags => |cc| .{ .eflags = cc.negate() },
else => unreachable,
};
@@ -15156,7 +15173,7 @@ fn genSetMem(
})).write(
self,
.{ .base = base, .mod = .{ .rm = .{
- .size = self.memSize(ty),
+ .size = Memory.Size.fromBitSize(@min(self.memSize(ty).bitSize(), src_alias.bitSize())),
.disp = disp,
} } },
src_alias,
@@ -15202,7 +15219,33 @@ fn genSetMem(
@tagName(src_mcv), ty.fmt(pt),
}),
},
- .register_offset,
+ .register_offset => |reg_off| {
+ const src_reg = self.copyToTmpRegister(ty, src_mcv) catch |err| switch (err) {
+ error.OutOfRegisters => {
+ const src_reg = registerAlias(reg_off.reg, abi_size);
+ try self.asmRegisterMemory(.{ ._, .lea }, src_reg, .{
+ .base = .{ .reg = src_reg },
+ .mod = .{ .rm = .{
+ .size = .qword,
+ .disp = reg_off.off,
+ } },
+ });
+ try self.genSetMem(base, disp, ty, .{ .register = reg_off.reg }, opts);
+ return self.asmRegisterMemory(.{ ._, .lea }, src_reg, .{
+ .base = .{ .reg = src_reg },
+ .mod = .{ .rm = .{
+ .size = .qword,
+ .disp = -reg_off.off,
+ } },
+ });
+ },
+ else => |e| return e,
+ };
+ const src_lock = self.register_manager.lockRegAssumeUnused(src_reg);
+ defer self.register_manager.unlockReg(src_lock);
+
+ try self.genSetMem(base, disp, ty, .{ .register = src_reg }, opts);
+ },
.memory,
.indirect,
.load_direct,
@@ -15422,9 +15465,14 @@ fn airBitCast(self: *Self, inst: Air.Inst.Index) !void {
const src_ty = self.typeOf(ty_op.operand);
const result = result: {
+ const src_mcv = try self.resolveInst(ty_op.operand);
+ if (dst_ty.isPtrAtRuntime(mod) and src_ty.isPtrAtRuntime(mod)) switch (src_mcv) {
+ .lea_frame => break :result src_mcv,
+ else => if (self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) break :result src_mcv,
+ };
+
const dst_rc = self.regClassForType(dst_ty);
const src_rc = self.regClassForType(src_ty);
- const src_mcv = try self.resolveInst(ty_op.operand);
const src_lock = if (src_mcv.getReg()) |reg| self.register_manager.lockReg(reg) else null;
defer if (src_lock) |lock| self.register_manager.unlockReg(lock);
@@ -18236,10 +18284,7 @@ fn airUnionInit(self: *Self, inst: Air.Inst.Index) !void {
const tag_val = try pt.enumValueFieldIndex(tag_ty, field_index);
const tag_int_val = try tag_val.intFromEnum(tag_ty, pt);
const tag_int = tag_int_val.toUnsignedInt(pt);
- const tag_off: i32 = if (layout.tag_align.compare(.lt, layout.payload_align))
- @intCast(layout.payload_size)
- else
- 0;
+ const tag_off: i32 = @intCast(layout.tagOffset());
try self.genCopy(
tag_ty,
dst_mcv.address().offset(tag_off).deref(),
@@ -18247,10 +18292,7 @@ fn airUnionInit(self: *Self, inst: Air.Inst.Index) !void {
.{},
);
- const pl_off: i32 = if (layout.tag_align.compare(.lt, layout.payload_align))
- 0
- else
- @intCast(layout.tag_size);
+ const pl_off: i32 = @intCast(layout.payloadOffset());
try self.genCopy(src_ty, dst_mcv.address().offset(pl_off).deref(), src_mcv, .{});
break :result dst_mcv;
@@ -18790,6 +18832,7 @@ fn genTypedValue(self: *Self, val: Value) InnerError!MCValue {
.load_symbol => |sym_index| .{ .load_symbol = .{ .sym = sym_index } },
.lea_symbol => |sym_index| .{ .lea_symbol = .{ .sym = sym_index } },
.load_direct => |sym_index| .{ .load_direct = sym_index },
+ .lea_direct => |sym_index| .{ .lea_direct = sym_index },
.load_got => |sym_index| .{ .lea_got = sym_index },
.load_tlv => |sym_index| .{ .lea_tlv = sym_index },
},