diff options
| author | Jacob Young <jacobly0@users.noreply.github.com> | 2024-08-06 11:22:37 -0400 |
|---|---|---|
| committer | Jacob Young <jacobly0@users.noreply.github.com> | 2024-08-16 15:22:55 -0400 |
| commit | ef11bc9899002620d67cfce9c79b6c0dc0f5ea61 (patch) | |
| tree | 7b05fe17340c06e4c40c45ebe249361c0c281c72 /src/arch/x86_64/CodeGen.zig | |
| parent | 90989be0e31a91335f8d1c1eafb84c3b34792a8c (diff) | |
| download | zig-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.zig | 233 |
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 }, }, |
