dwarf: fix stepping through an inline loop containing one statement

Previously, stepping from the single statement within the loop would
always exit the loop because all of the code unrolled from the loop is
associated with the same line and treated by the debugger as one line.
This commit is contained in:
Jacob Young 2024-11-24 04:11:52 -05:00 committed by Andrew Kelley
parent 6d781e0955
commit c894ac09a3
25 changed files with 721 additions and 221 deletions

View File

@ -6024,7 +6024,7 @@ fn tryExpr(
if (!parent_gz.is_comptime) {
try emitDbgNode(parent_gz, node);
}
const try_lc = LineColumn{ astgen.source_line - parent_gz.decl_line, astgen.source_column };
const try_lc: LineColumn = .{ astgen.source_line - parent_gz.decl_line, astgen.source_column };
const operand_rl: ResultInfo.Loc, const block_tag: Zir.Inst.Tag = switch (ri.rl) {
.ref => .{ .ref, .try_ptr },
@ -6577,6 +6577,7 @@ fn whileExpr(
const astgen = parent_gz.astgen;
const tree = astgen.tree;
const token_tags = tree.tokens.items(.tag);
const token_starts = tree.tokens.items(.start);
const need_rl = astgen.nodes_need_rl.contains(node);
const block_ri: ResultInfo = if (need_rl) ri else .{
@ -6774,6 +6775,16 @@ fn whileExpr(
try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break";
if (!continue_scope.endsWithNoReturn()) {
astgen.advanceSourceCursor(token_starts[tree.lastToken(then_node)]);
try emitDbgStmt(parent_gz, .{ astgen.source_line - parent_gz.decl_line, astgen.source_column });
_ = try parent_gz.add(.{
.tag = .extended,
.data = .{ .extended = .{
.opcode = .dbg_empty_stmt,
.small = undefined,
.operand = undefined,
} },
});
_ = try continue_scope.addBreak(break_tag, continue_block, .void_value);
}
try continue_scope.setBlockBody(continue_block);
@ -6882,6 +6893,7 @@ fn forExpr(
}
const tree = astgen.tree;
const token_tags = tree.tokens.items(.tag);
const token_starts = tree.tokens.items(.start);
const node_tags = tree.nodes.items(.tag);
const node_data = tree.nodes.items(.data);
const gpa = astgen.gpa;
@ -7087,8 +7099,18 @@ fn forExpr(
try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break";
astgen.advanceSourceCursor(token_starts[tree.lastToken(then_node)]);
try emitDbgStmt(parent_gz, .{ astgen.source_line - parent_gz.decl_line, astgen.source_column });
_ = try parent_gz.add(.{
.tag = .extended,
.data = .{ .extended = .{
.opcode = .dbg_empty_stmt,
.small = undefined,
.operand = undefined,
} },
});
const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break";
_ = try then_scope.addBreak(break_tag, cond_block, .void_value);
var else_scope = parent_gz.makeSubBlock(&cond_scope.base);
@ -7135,6 +7157,7 @@ fn forExpr(
.lhs = index_ptr,
.rhs = index_plus_one,
});
const repeat_tag: Zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat;
_ = try loop_scope.addNode(repeat_tag, node);
@ -7279,7 +7302,7 @@ fn switchExprErrUnion(
};
astgen.advanceSourceCursorToNode(operand_node);
const operand_lc = LineColumn{ astgen.source_line - parent_gz.decl_line, astgen.source_column };
const operand_lc: LineColumn = .{ astgen.source_line - parent_gz.decl_line, astgen.source_column };
const raw_operand = try reachableExpr(parent_gz, scope, operand_ri, operand_node, switch_node);
const item_ri: ResultInfo = .{ .rl = .none };
@ -7868,7 +7891,7 @@ fn switchExpr(
const operand_ri: ResultInfo = .{ .rl = if (any_payload_is_ref) .ref else .none };
astgen.advanceSourceCursorToNode(operand_node);
const operand_lc = LineColumn{ astgen.source_line - parent_gz.decl_line, astgen.source_column };
const operand_lc: LineColumn = .{ astgen.source_line - parent_gz.decl_line, astgen.source_column };
const raw_operand = try expr(parent_gz, scope, operand_ri, operand_node);
const item_ri: ResultInfo = .{ .rl = .none };
@ -8214,7 +8237,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
if (!gz.is_comptime) {
try emitDbgNode(gz, node);
}
const ret_lc = LineColumn{ astgen.source_line - gz.decl_line, astgen.source_column };
const ret_lc: LineColumn = .{ astgen.source_line - gz.decl_line, astgen.source_column };
const defer_outer = &astgen.fn_block.?.base;

View File

@ -2088,6 +2088,8 @@ pub const Inst = struct {
/// `operand` is `Zir.Inst.Ref` of the loaded LHS (*not* its type).
/// `small` is an `Inst.InplaceOp`.
inplace_arith_result_ty,
/// Marks a statement that can be stepped to but produces no code.
dbg_empty_stmt,
pub const InstData = struct {
opcode: Extended,
@ -4062,6 +4064,7 @@ fn findDeclsInner(
.branch_hint,
.inplace_arith_result_ty,
.tuple_decl,
.dbg_empty_stmt,
=> return,
// `@TypeOf` has a body.

View File

@ -460,6 +460,8 @@ pub const Inst = struct {
/// Result type is always void.
/// Uses the `dbg_stmt` field.
dbg_stmt,
/// Marks a statement that can be stepped to but produces no code.
dbg_empty_stmt,
/// A block that represents an inlined function call.
/// Uses the `ty_pl` field. Payload is `DbgInlineBlock`.
dbg_inline_block,
@ -1468,6 +1470,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool)
.breakpoint,
.dbg_stmt,
.dbg_empty_stmt,
.dbg_var_ptr,
.dbg_var_val,
.dbg_arg_inline,
@ -1629,6 +1632,7 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool {
.try_ptr,
.try_ptr_cold,
.dbg_stmt,
.dbg_empty_stmt,
.dbg_inline_block,
.dbg_var_ptr,
.dbg_var_val,

View File

@ -417,6 +417,7 @@ fn checkBody(air: Air, body: []const Air.Inst.Index, zcu: *Zcu) bool {
.work_group_size,
.work_group_id,
.dbg_stmt,
.dbg_empty_stmt,
.err_return_trace,
.save_err_return_trace_index,
.repeat,

View File

@ -334,6 +334,7 @@ pub fn categorizeOperand(
.repeat,
.switch_dispatch,
.dbg_stmt,
.dbg_empty_stmt,
.unreach,
.ret_addr,
.frame_addr,
@ -973,6 +974,7 @@ fn analyzeInst(
.ret_ptr,
.breakpoint,
.dbg_stmt,
.dbg_empty_stmt,
.ret_addr,
.frame_addr,
.wasm_memory_size,

View File

@ -56,6 +56,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
.ret_ptr,
.breakpoint,
.dbg_stmt,
.dbg_empty_stmt,
.ret_addr,
.frame_addr,
.wasm_memory_size,

View File

@ -1355,6 +1355,11 @@ fn analyzeBodyInner(
.field_parent_ptr => try sema.zirFieldParentPtr(block, extended),
.builtin_value => try sema.zirBuiltinValue(block, extended),
.inplace_arith_result_ty => try sema.zirInplaceArithResultTy(extended),
.dbg_empty_stmt => {
try sema.zirDbgEmptyStmt(block, inst);
i += 1;
continue;
},
};
},
@ -6671,6 +6676,11 @@ fn zirDbgStmt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!voi
});
}
fn zirDbgEmptyStmt(_: *Sema, block: *Block, _: Zir.Inst.Index) CompileError!void {
if (block.is_comptime or block.ownerModule().strip) return;
_ = try block.addNoOp(.dbg_empty_stmt);
}
fn zirDbgVar(
sema: *Sema,
block: *Block,

View File

@ -800,6 +800,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.try_ptr_cold => try self.airTryPtr(inst),
.dbg_stmt => try self.airDbgStmt(inst),
.dbg_empty_stmt => self.finishAirBookkeeping(),
.dbg_inline_block => try self.airDbgInlineBlock(inst),
.dbg_var_ptr,
.dbg_var_val,

View File

@ -787,6 +787,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.try_ptr_cold => try self.airTryPtr(inst),
.dbg_stmt => try self.airDbgStmt(inst),
.dbg_empty_stmt => self.finishAirBookkeeping(),
.dbg_inline_block => try self.airDbgInlineBlock(inst),
.dbg_var_ptr,
.dbg_var_val,

View File

@ -1593,6 +1593,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {
.frame_addr => try func.airFrameAddress(inst),
.cond_br => try func.airCondBr(inst),
.dbg_stmt => try func.airDbgStmt(inst),
.dbg_empty_stmt => func.finishAirBookkeeping(),
.fptrunc => try func.airFptrunc(inst),
.fpext => try func.airFpext(inst),
.intcast => try func.airIntCast(inst),

View File

@ -642,6 +642,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.try_ptr_cold => @panic("TODO try self.airTryPtrCold(inst)"),
.dbg_stmt => try self.airDbgStmt(inst),
.dbg_empty_stmt => self.finishAirBookkeeping(),
.dbg_inline_block => try self.airDbgInlineBlock(inst),
.dbg_var_ptr,
.dbg_var_val,

View File

@ -1924,6 +1924,7 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
.try_ptr_cold => func.airTryPtr(inst),
.dbg_stmt => func.airDbgStmt(inst),
.dbg_empty_stmt => try func.finishAir(inst, .none, &.{}),
.dbg_inline_block => func.airDbgInlineBlock(inst),
.dbg_var_ptr => func.airDbgVar(inst, .local_var, true),
.dbg_var_val => func.airDbgVar(inst, .local_var, false),

View File

@ -961,9 +961,16 @@ pub fn generate(
},
.debug_output = debug_output,
.code = code,
.prev_di_loc = .{
.line = func.lbrace_line,
.column = func.lbrace_column,
.is_stmt = switch (debug_output) {
.dwarf => |dwarf| dwarf.dwarf.debug_line.header.default_is_stmt,
.plan9 => undefined,
.none => undefined,
},
},
.prev_di_pc = 0,
.prev_di_line = func.lbrace_line,
.prev_di_column = func.lbrace_column,
};
defer emit.deinit();
emit.emitMir() catch |err| switch (err) {
@ -1066,9 +1073,8 @@ pub fn generateLazy(
},
.debug_output = debug_output,
.code = code,
.prev_di_loc = undefined, // no debug info yet
.prev_di_pc = undefined, // no debug info yet
.prev_di_line = undefined, // no debug info yet
.prev_di_column = undefined, // no debug info yet
};
defer emit.deinit();
emit.emitMir() catch |err| switch (err) {
@ -1194,13 +1200,16 @@ fn formatWipMir(
switch (mir_inst.ops) {
else => unreachable,
.pseudo_dbg_prologue_end_none,
.pseudo_dbg_line_line_column,
.pseudo_dbg_epilogue_begin_none,
.pseudo_dbg_enter_block_none,
.pseudo_dbg_leave_block_none,
.pseudo_dbg_var_args_none,
.pseudo_dead_none,
=> {},
.pseudo_dbg_line_stmt_line_column, .pseudo_dbg_line_line_column => try writer.print(
" {[line]d}, {[column]d}",
mir_inst.data.line_column,
),
.pseudo_dbg_enter_inline_func, .pseudo_dbg_leave_inline_func => try writer.print(" {}", .{
ip.getNav(ip.indexToKey(mir_inst.data.func).func.owner_nav).name.fmt(ip),
}),
@ -1281,14 +1290,7 @@ fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index {
try self.mir_instructions.ensureUnusedCapacity(gpa, 1);
const result_index: Mir.Inst.Index = @intCast(self.mir_instructions.len);
self.mir_instructions.appendAssumeCapacity(inst);
if (inst.tag != .pseudo or switch (inst.ops) {
else => true,
.pseudo_dbg_prologue_end_none,
.pseudo_dbg_line_line_column,
.pseudo_dbg_epilogue_begin_none,
.pseudo_dead_none,
=> false,
}) wip_mir_log.debug("{}", .{self.fmtWipMir(result_index)});
wip_mir_log.debug("{}", .{self.fmtWipMir(result_index)});
return result_index;
}
@ -2218,7 +2220,7 @@ fn gen(self: *Self) InnerError!void {
// Drop them off at the rbrace.
_ = try self.addInst(.{
.tag = .pseudo,
.ops = .pseudo_dbg_line_line_column,
.ops = .pseudo_dbg_line_stmt_line_column,
.data = .{ .line_column = .{
.line = self.end_di_line,
.column = self.end_di_column,
@ -2426,6 +2428,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.try_ptr_cold => try self.airTryPtr(inst), // TODO
.dbg_stmt => try self.airDbgStmt(inst),
.dbg_empty_stmt => try self.airDbgEmptyStmt(),
.dbg_inline_block => try self.airDbgInlineBlock(inst),
.dbg_var_ptr,
.dbg_var_val,
@ -13281,7 +13284,7 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
const dbg_stmt = self.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt;
_ = try self.addInst(.{
.tag = .pseudo,
.ops = .pseudo_dbg_line_line_column,
.ops = .pseudo_dbg_line_stmt_line_column,
.data = .{ .line_column = .{
.line = dbg_stmt.line,
.column = dbg_stmt.column,
@ -13290,6 +13293,14 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
self.finishAirBookkeeping();
}
fn airDbgEmptyStmt(self: *Self) !void {
if (self.mir_instructions.len > 0 and
self.mir_instructions.items(.ops)[self.mir_instructions.len - 1] == .pseudo_dbg_line_stmt_line_column)
self.mir_instructions.items(.ops)[self.mir_instructions.len - 1] = .pseudo_dbg_line_line_column;
try self.asmOpOnly(.{ ._, .nop });
self.finishAirBookkeeping();
}
fn airDbgInlineBlock(self: *Self, inst: Air.Inst.Index) !void {
const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
const extra = self.air.extraData(Air.DbgInlineBlock, ty_pl.payload);

View File

@ -6,8 +6,7 @@ atom_index: u32,
debug_output: link.File.DebugInfoOutput,
code: *std.ArrayList(u8),
prev_di_line: u32,
prev_di_column: u32,
prev_di_loc: Loc,
/// Relative to the beginning of `code`.
prev_di_pc: usize,
@ -263,77 +262,71 @@ pub fn emitMir(emit: *Emit) Error!void {
else => unreachable,
.pseudo => switch (mir_inst.ops) {
else => unreachable,
.pseudo_dbg_prologue_end_none => {
switch (emit.debug_output) {
.dwarf => |dw| try dw.setPrologueEnd(),
.plan9 => {},
.none => {},
}
.pseudo_dbg_prologue_end_none => switch (emit.debug_output) {
.dwarf => |dwarf| try dwarf.setPrologueEnd(),
.plan9 => {},
.none => {},
},
.pseudo_dbg_line_line_column => try emit.dbgAdvancePCAndLine(
mir_inst.data.line_column.line,
mir_inst.data.line_column.column,
),
.pseudo_dbg_epilogue_begin_none => {
switch (emit.debug_output) {
.dwarf => |dw| {
try dw.setEpilogueBegin();
log.debug("mirDbgEpilogueBegin (line={d}, col={d})", .{
emit.prev_di_line, emit.prev_di_column,
});
try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
},
.plan9 => {},
.none => {},
}
.pseudo_dbg_line_stmt_line_column => try emit.dbgAdvancePCAndLine(.{
.line = mir_inst.data.line_column.line,
.column = mir_inst.data.line_column.column,
.is_stmt = true,
}),
.pseudo_dbg_line_line_column => try emit.dbgAdvancePCAndLine(.{
.line = mir_inst.data.line_column.line,
.column = mir_inst.data.line_column.column,
.is_stmt = false,
}),
.pseudo_dbg_epilogue_begin_none => switch (emit.debug_output) {
.dwarf => |dwarf| {
try dwarf.setEpilogueBegin();
log.debug("mirDbgEpilogueBegin (line={d}, col={d})", .{
emit.prev_di_loc.line, emit.prev_di_loc.column,
});
try emit.dbgAdvancePCAndLine(emit.prev_di_loc);
},
.plan9 => {},
.none => {},
},
.pseudo_dbg_enter_block_none => {
switch (emit.debug_output) {
.dwarf => |dw| {
log.debug("mirDbgEnterBlock (line={d}, col={d})", .{
emit.prev_di_line, emit.prev_di_column,
});
try dw.enterBlock(emit.code.items.len);
},
.plan9 => {},
.none => {},
}
.pseudo_dbg_enter_block_none => switch (emit.debug_output) {
.dwarf => |dwarf| {
log.debug("mirDbgEnterBlock (line={d}, col={d})", .{
emit.prev_di_loc.line, emit.prev_di_loc.column,
});
try dwarf.enterBlock(emit.code.items.len);
},
.plan9 => {},
.none => {},
},
.pseudo_dbg_leave_block_none => {
switch (emit.debug_output) {
.dwarf => |dw| {
log.debug("mirDbgLeaveBlock (line={d}, col={d})", .{
emit.prev_di_line, emit.prev_di_column,
});
try dw.leaveBlock(emit.code.items.len);
},
.plan9 => {},
.none => {},
}
.pseudo_dbg_leave_block_none => switch (emit.debug_output) {
.dwarf => |dwarf| {
log.debug("mirDbgLeaveBlock (line={d}, col={d})", .{
emit.prev_di_loc.line, emit.prev_di_loc.column,
});
try dwarf.leaveBlock(emit.code.items.len);
},
.plan9 => {},
.none => {},
},
.pseudo_dbg_enter_inline_func => {
switch (emit.debug_output) {
.dwarf => |dw| {
log.debug("mirDbgEnterInline (line={d}, col={d})", .{
emit.prev_di_line, emit.prev_di_column,
});
try dw.enterInlineFunc(mir_inst.data.func, emit.code.items.len, emit.prev_di_line, emit.prev_di_column);
},
.plan9 => {},
.none => {},
}
.pseudo_dbg_enter_inline_func => switch (emit.debug_output) {
.dwarf => |dwarf| {
log.debug("mirDbgEnterInline (line={d}, col={d})", .{
emit.prev_di_loc.line, emit.prev_di_loc.column,
});
try dwarf.enterInlineFunc(mir_inst.data.func, emit.code.items.len, emit.prev_di_loc.line, emit.prev_di_loc.column);
},
.plan9 => {},
.none => {},
},
.pseudo_dbg_leave_inline_func => {
switch (emit.debug_output) {
.dwarf => |dw| {
log.debug("mirDbgLeaveInline (line={d}, col={d})", .{
emit.prev_di_line, emit.prev_di_column,
});
try dw.leaveInlineFunc(mir_inst.data.func, emit.code.items.len);
},
.plan9 => {},
.none => {},
}
.pseudo_dbg_leave_inline_func => switch (emit.debug_output) {
.dwarf => |dwarf| {
log.debug("mirDbgLeaveInline (line={d}, col={d})", .{
emit.prev_di_loc.line, emit.prev_di_loc.column,
});
try dwarf.leaveInlineFunc(mir_inst.data.func, emit.code.items.len);
},
.plan9 => {},
.none => {},
},
.pseudo_dbg_local_a,
.pseudo_dbg_local_ai_s,
@ -344,129 +337,125 @@ pub fn emitMir(emit: *Emit) Error!void {
.pseudo_dbg_local_aro,
.pseudo_dbg_local_af,
.pseudo_dbg_local_am,
=> {
switch (emit.debug_output) {
.dwarf => |dw| {
var loc_buf: [2]link.File.Dwarf.Loc = undefined;
const air_inst_index, const loc: link.File.Dwarf.Loc = switch (mir_inst.ops) {
=> switch (emit.debug_output) {
.dwarf => |dwarf| {
var loc_buf: [2]link.File.Dwarf.Loc = undefined;
const air_inst_index, const loc: link.File.Dwarf.Loc = switch (mir_inst.ops) {
else => unreachable,
.pseudo_dbg_local_a => .{ mir_inst.data.a.air_inst, .empty },
.pseudo_dbg_local_ai_s,
.pseudo_dbg_local_ai_u,
.pseudo_dbg_local_ai_64,
=> .{ mir_inst.data.ai.air_inst, .{ .stack_value = stack_value: {
loc_buf[0] = switch (emit.lower.imm(mir_inst.ops, mir_inst.data.ai.i)) {
.signed => |s| .{ .consts = s },
.unsigned => |u| .{ .constu = u },
};
break :stack_value &loc_buf[0];
} } },
.pseudo_dbg_local_as => .{ mir_inst.data.as.air_inst, .{ .addr = .{
.sym = mir_inst.data.as.sym_index,
} } },
.pseudo_dbg_local_aso => loc: {
const sym_off = emit.lower.mir.extraData(
bits.SymbolOffset,
mir_inst.data.ax.payload,
).data;
break :loc .{ mir_inst.data.ax.air_inst, .{ .plus = .{
sym: {
loc_buf[0] = .{ .addr = .{ .sym = sym_off.sym_index } };
break :sym &loc_buf[0];
},
off: {
loc_buf[1] = .{ .consts = sym_off.off };
break :off &loc_buf[1];
},
} } };
},
.pseudo_dbg_local_aro => loc: {
const air_off = emit.lower.mir.extraData(
Mir.AirOffset,
mir_inst.data.rx.payload,
).data;
break :loc .{ air_off.air_inst, .{ .plus = .{
reg: {
loc_buf[0] = .{ .breg = mir_inst.data.rx.r1.dwarfNum() };
break :reg &loc_buf[0];
},
off: {
loc_buf[1] = .{ .consts = air_off.off };
break :off &loc_buf[1];
},
} } };
},
.pseudo_dbg_local_af => loc: {
const reg_off = emit.lower.mir.resolveFrameAddr(emit.lower.mir.extraData(
bits.FrameAddr,
mir_inst.data.ax.payload,
).data);
break :loc .{ mir_inst.data.ax.air_inst, .{ .plus = .{
reg: {
loc_buf[0] = .{ .breg = reg_off.reg.dwarfNum() };
break :reg &loc_buf[0];
},
off: {
loc_buf[1] = .{ .consts = reg_off.off };
break :off &loc_buf[1];
},
} } };
},
.pseudo_dbg_local_am => loc: {
const mem = emit.lower.mem(mir_inst.data.ax.payload);
break :loc .{ mir_inst.data.ax.air_inst, .{ .plus = .{
base: {
loc_buf[0] = switch (mem.base()) {
.none => .{ .constu = 0 },
.reg => |reg| .{ .breg = reg.dwarfNum() },
.frame => unreachable,
.reloc => |sym_index| .{ .addr = .{ .sym = sym_index } },
};
break :base &loc_buf[0];
},
disp: {
loc_buf[1] = switch (mem.disp()) {
.signed => |s| .{ .consts = s },
.unsigned => |u| .{ .constu = u },
};
break :disp &loc_buf[1];
},
} } };
},
};
const ip = &emit.lower.bin_file.comp.zcu.?.intern_pool;
const air_inst = emit.air.instructions.get(@intFromEnum(air_inst_index));
const name: Air.NullTerminatedString = switch (air_inst.tag) {
else => unreachable,
.arg => air_inst.data.arg.name,
.dbg_var_ptr, .dbg_var_val, .dbg_arg_inline => @enumFromInt(air_inst.data.pl_op.payload),
};
try dwarf.genLocalDebugInfo(
switch (air_inst.tag) {
else => unreachable,
.pseudo_dbg_local_a => .{ mir_inst.data.a.air_inst, .empty },
.pseudo_dbg_local_ai_s,
.pseudo_dbg_local_ai_u,
.pseudo_dbg_local_ai_64,
=> .{ mir_inst.data.ai.air_inst, .{ .stack_value = stack_value: {
loc_buf[0] = switch (emit.lower.imm(mir_inst.ops, mir_inst.data.ai.i)) {
.signed => |s| .{ .consts = s },
.unsigned => |u| .{ .constu = u },
};
break :stack_value &loc_buf[0];
} } },
.pseudo_dbg_local_as => .{ mir_inst.data.as.air_inst, .{ .addr = .{
.sym = mir_inst.data.as.sym_index,
} } },
.pseudo_dbg_local_aso => loc: {
const sym_off = emit.lower.mir.extraData(
bits.SymbolOffset,
mir_inst.data.ax.payload,
).data;
break :loc .{ mir_inst.data.ax.air_inst, .{ .plus = .{
sym: {
loc_buf[0] = .{ .addr = .{ .sym = sym_off.sym_index } };
break :sym &loc_buf[0];
},
off: {
loc_buf[1] = .{ .consts = sym_off.off };
break :off &loc_buf[1];
},
} } };
},
.pseudo_dbg_local_aro => loc: {
const air_off = emit.lower.mir.extraData(
Mir.AirOffset,
mir_inst.data.rx.payload,
).data;
break :loc .{ air_off.air_inst, .{ .plus = .{
reg: {
loc_buf[0] = .{ .breg = mir_inst.data.rx.r1.dwarfNum() };
break :reg &loc_buf[0];
},
off: {
loc_buf[1] = .{ .consts = air_off.off };
break :off &loc_buf[1];
},
} } };
},
.pseudo_dbg_local_af => loc: {
const reg_off = emit.lower.mir.resolveFrameAddr(emit.lower.mir.extraData(
bits.FrameAddr,
mir_inst.data.ax.payload,
).data);
break :loc .{ mir_inst.data.ax.air_inst, .{ .plus = .{
reg: {
loc_buf[0] = .{ .breg = reg_off.reg.dwarfNum() };
break :reg &loc_buf[0];
},
off: {
loc_buf[1] = .{ .consts = reg_off.off };
break :off &loc_buf[1];
},
} } };
},
.pseudo_dbg_local_am => loc: {
const mem = emit.lower.mem(mir_inst.data.ax.payload);
break :loc .{ mir_inst.data.ax.air_inst, .{ .plus = .{
base: {
loc_buf[0] = switch (mem.base()) {
.none => .{ .constu = 0 },
.reg => |reg| .{ .breg = reg.dwarfNum() },
.frame => unreachable,
.reloc => |sym_index| .{ .addr = .{ .sym = sym_index } },
};
break :base &loc_buf[0];
},
disp: {
loc_buf[1] = switch (mem.disp()) {
.signed => |s| .{ .consts = s },
.unsigned => |u| .{ .constu = u },
};
break :disp &loc_buf[1];
},
} } };
},
};
const ip = &emit.lower.bin_file.comp.zcu.?.intern_pool;
const air_inst = emit.air.instructions.get(@intFromEnum(air_inst_index));
const name: Air.NullTerminatedString = switch (air_inst.tag) {
.arg, .dbg_arg_inline => .local_arg,
.dbg_var_ptr, .dbg_var_val => .local_var,
},
name.toSlice(emit.air),
switch (air_inst.tag) {
else => unreachable,
.arg => air_inst.data.arg.name,
.dbg_var_ptr, .dbg_var_val, .dbg_arg_inline => @enumFromInt(air_inst.data.pl_op.payload),
};
try dw.genLocalDebugInfo(
switch (air_inst.tag) {
else => unreachable,
.arg, .dbg_arg_inline => .local_arg,
.dbg_var_ptr, .dbg_var_val => .local_var,
},
name.toSlice(emit.air),
switch (air_inst.tag) {
else => unreachable,
.arg => emit.air.typeOfIndex(air_inst_index, ip),
.dbg_var_ptr => emit.air.typeOf(air_inst.data.pl_op.operand, ip).childTypeIp(ip),
.dbg_var_val, .dbg_arg_inline => emit.air.typeOf(air_inst.data.pl_op.operand, ip),
},
loc,
);
},
.plan9 => {},
.none => {},
}
.arg => emit.air.typeOfIndex(air_inst_index, ip),
.dbg_var_ptr => emit.air.typeOf(air_inst.data.pl_op.operand, ip).childTypeIp(ip),
.dbg_var_val, .dbg_arg_inline => emit.air.typeOf(air_inst.data.pl_op.operand, ip),
},
loc,
);
},
.plan9 => {},
.none => {},
},
.pseudo_dbg_var_args_none => {
switch (emit.debug_output) {
.dwarf => |dw| try dw.genVarArgsDebugInfo(),
.plan9 => {},
.none => {},
}
.pseudo_dbg_var_args_none => switch (emit.debug_output) {
.dwarf => |dwarf| try dwarf.genVarArgsDebugInfo(),
.plan9 => {},
.none => {},
},
.pseudo_dead_none => {},
},
@ -515,16 +504,22 @@ fn fixupRelocs(emit: *Emit) Error!void {
}
}
fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) Error!void {
const delta_line = @as(i33, line) - @as(i33, emit.prev_di_line);
const Loc = struct {
line: u32,
column: u32,
is_stmt: bool,
};
fn dbgAdvancePCAndLine(emit: *Emit, loc: Loc) Error!void {
const delta_line = @as(i33, loc.line) - @as(i33, emit.prev_di_loc.line);
const delta_pc: usize = emit.code.items.len - emit.prev_di_pc;
log.debug(" (advance pc={d} and line={d})", .{ delta_pc, delta_line });
switch (emit.debug_output) {
.dwarf => |dw| {
if (column != emit.prev_di_column) try dw.setColumn(column);
try dw.advancePCAndLine(delta_line, delta_pc);
emit.prev_di_line = line;
emit.prev_di_column = column;
.dwarf => |dwarf| {
if (loc.is_stmt != emit.prev_di_loc.is_stmt) try dwarf.negateStmt();
if (loc.column != emit.prev_di_loc.column) try dwarf.setColumn(loc.column);
try dwarf.advancePCAndLine(delta_line, delta_pc);
emit.prev_di_loc = loc;
emit.prev_di_pc = emit.code.items.len;
},
.plan9 => |dbg_out| {
@ -553,11 +548,10 @@ fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) Error!void {
// we don't need to do anything, because adding the pc quanta does it for us
} else unreachable;
if (dbg_out.start_line == null)
dbg_out.start_line = emit.prev_di_line;
dbg_out.end_line = line;
dbg_out.start_line = emit.prev_di_loc.line;
dbg_out.end_line = loc.line;
// only do this if the pc changed
emit.prev_di_line = line;
emit.prev_di_column = column;
emit.prev_di_loc = loc;
emit.prev_di_pc = emit.code.items.len;
},
.none => {},

View File

@ -310,6 +310,7 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
}),
.pseudo_dbg_prologue_end_none,
.pseudo_dbg_line_stmt_line_column,
.pseudo_dbg_line_line_column,
.pseudo_dbg_epilogue_begin_none,
.pseudo_dbg_enter_block_none,

View File

@ -930,7 +930,10 @@ pub const Inst = struct {
/// End of prologue
pseudo_dbg_prologue_end_none,
/// Update debug line
/// Update debug line with is_stmt register set
/// Uses `line_column` payload.
pseudo_dbg_line_stmt_line_column,
/// Update debug line with is_stmt register clear
/// Uses `line_column` payload.
pseudo_dbg_line_line_column,
/// Start of epilogue

View File

@ -3289,6 +3289,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
.try_ptr_cold => try airTryPtr(f, inst),
.dbg_stmt => try airDbgStmt(f, inst),
.dbg_empty_stmt => try airDbgEmptyStmt(f, inst),
.dbg_var_ptr, .dbg_var_val, .dbg_arg_inline => try airDbgVar(f, inst),
.float_from_int,
@ -4601,6 +4602,11 @@ fn airDbgStmt(f: *Function, inst: Air.Inst.Index) !CValue {
return .none;
}
fn airDbgEmptyStmt(f: *Function, _: Air.Inst.Index) !CValue {
try f.object.writer().writeAll("(void)0;\n");
return .none;
}
fn airDbgInlineBlock(f: *Function, inst: Air.Inst.Index) !CValue {
const pt = f.object.dg.pt;
const zcu = pt.zcu;

View File

@ -5391,6 +5391,7 @@ pub const FuncGen = struct {
.inferred_alloc, .inferred_alloc_comptime => unreachable,
.dbg_stmt => try self.airDbgStmt(inst),
.dbg_empty_stmt => try self.airDbgEmptyStmt(inst),
.dbg_var_ptr => try self.airDbgVarPtr(inst),
.dbg_var_val => try self.airDbgVarVal(inst, false),
.dbg_arg_inline => try self.airDbgVarVal(inst, true),
@ -7433,6 +7434,12 @@ pub const FuncGen = struct {
return .none;
}
fn airDbgEmptyStmt(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value {
_ = self;
_ = inst;
return .none;
}
fn airDbgInlineBlock(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value {
const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
const extra = self.air.extraData(Air.DbgInlineBlock, ty_pl.payload);

View File

@ -81,6 +81,7 @@ pub const Env = enum {
=> true,
.cc_command,
.translate_c_command,
.fmt_command,
.jit_command,
.fetch_command,
.init_command,
@ -168,6 +169,7 @@ pub const Feature = enum {
clang_command,
cc_command,
translate_c_command,
fmt_command,
jit_command,
fetch_command,
init_command,

View File

@ -1474,6 +1474,11 @@ pub const WipNav = struct {
try uleb128(dlw, column + 1);
}
pub fn negateStmt(wip_nav: *WipNav) error{OutOfMemory}!void {
const dlw = wip_nav.debug_line.writer(wip_nav.dwarf.gpa);
try dlw.writeByte(DW.LNS.negate_stmt);
}
pub fn setPrologueEnd(wip_nav: *WipNav) error{OutOfMemory}!void {
const dlw = wip_nav.debug_line.writer(wip_nav.dwarf.gpa);
try dlw.writeByte(DW.LNS.set_prologue_end);

View File

@ -1496,7 +1496,7 @@ pub fn updateFunc(
});
defer gpa.free(name);
const osec = if (self.text_index) |sect_sym_index|
self.symbol(sect_sym_index).output_section_index
self.symbol(sect_sym_index).outputShndx(elf_file).?
else osec: {
const osec = try elf_file.addSection(.{
.name = try elf_file.insertShString(".text"),

View File

@ -309,6 +309,7 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
.server = use_server,
});
} else if (mem.eql(u8, cmd, "fmt")) {
dev.check(.fmt_command);
return @import("fmt.zig").run(gpa, arena, cmd_args);
} else if (mem.eql(u8, cmd, "objcopy")) {
return jitCmd(gpa, arena, cmd_args, .{

View File

@ -202,6 +202,7 @@ const Writer = struct {
.trap,
.breakpoint,
.dbg_empty_stmt,
.unreach,
.ret_addr,
.frame_addr,

View File

@ -621,6 +621,8 @@ const Writer = struct {
.field_parent_ptr => try self.writeFieldParentPtr(stream, extended),
.builtin_value => try self.writeBuiltinValue(stream, extended),
.inplace_arith_result_ty => try self.writeInplaceArithResultTy(stream, extended),
.dbg_empty_stmt => try stream.writeAll("))"),
}
}

View File

@ -808,6 +808,424 @@ pub fn addTestsForTarget(db: *Debugger, target: Target) void {
\\1 breakpoints deleted; 0 breakpoint locations disabled.
},
);
db.addLldbTest(
"step_single_stmt_loops",
target,
&.{
.{
.path = "step_single_stmt_loops.zig",
.source =
\\pub fn main() void {
\\ var x: u32 = 0;
\\ for (0..3) |_| {
\\ x +%= 1;
\\ }
\\ {
\\ var i: u32 = 0;
\\ while (i < 3) : (i +%= 1) {
\\ x +%= 1;
\\ }
\\ }
\\ {
\\ var i: u32 = 0;
\\ while (i < 3) {
\\ i +%= 1;
\\ }
\\ }
\\ inline for (0..3) |_| {
\\ x +%= 1;
\\ }
\\ {
\\ comptime var i: u32 = 0;
\\ inline while (i < 3) : (i +%= 1) {
\\ x +%= 1;
\\ }
\\ }
\\ {
\\ comptime var i: u32 = 0;
\\ inline while (i < 3) {
\\ i +%= 1;
\\ }
\\ }
\\ x +%= 1;
\\}
\\
,
},
},
\\breakpoint set --name step_single_stmt_loops.main
\\process launch
\\thread step-in
\\#00
\\frame variable x
\\thread step-in
\\#01
\\frame variable x
\\thread step-in
\\#02
\\frame variable x
\\thread step-in
\\#03
\\frame variable x
\\thread step-in
\\#04
\\frame variable x
\\thread step-in
\\#05
\\frame variable x
\\thread step-in
\\#06
\\frame variable x
\\thread step-in
\\#07
\\frame variable x
\\thread step-in
\\#08
\\frame variable x
\\thread step-in
\\#09
\\frame variable x
\\thread step-in
\\#10
\\frame variable x
\\thread step-in
\\#11
\\frame variable x
\\thread step-in
\\#12
\\frame variable x
\\thread step-in
\\#13
\\frame variable x
\\thread step-in
\\#14
\\frame variable x
\\thread step-in
\\#15
\\frame variable x
\\thread step-in
\\#16
\\frame variable x
\\thread step-in
\\#17
\\frame variable x
\\thread step-in
\\#18
\\frame variable x
\\thread step-in
\\#19
\\frame variable x
\\thread step-in
\\#20
\\frame variable x
\\thread step-in
\\#21
\\frame variable x
\\thread step-in
\\#22
\\frame variable x
\\thread step-in
\\#23
\\frame variable x
\\thread step-in
\\#24
\\frame variable x
\\thread step-in
\\#25
\\frame variable x
\\thread step-in
\\#26
\\frame variable x
\\thread step-in
\\#27
\\frame variable x
\\thread step-in
\\#28
\\frame variable x
\\thread step-in
\\#29
\\frame variable x
\\thread step-in
\\#30
\\frame variable x
\\thread step-in
\\#31
\\frame variable x
\\thread step-in
\\#32
\\frame variable x
\\thread step-in
\\#33
\\frame variable x
\\thread step-in
\\#34
\\frame variable x
\\thread step-in
\\#35
\\frame variable x
\\thread step-in
\\#36
\\frame variable x
\\thread step-in
\\#37
\\frame variable x
\\thread step-in
\\#38
\\frame variable x
\\thread step-in
\\#39
\\frame variable x
\\thread step-in
\\#40
\\frame variable x
\\thread step-in
\\#41
\\frame variable x
\\thread step-in
\\#42
\\frame variable x
\\thread step-in
\\#43
\\frame variable x
\\thread step-in
\\#44
\\frame variable x
\\thread step-in
\\#45
\\frame variable x
\\
,
&.{
\\(lldb) #00
\\(lldb) frame variable x
\\(u32) x = 0
\\(lldb) thread step-in
,
\\(lldb) #01
\\(lldb) frame variable x
\\(u32) x = 0
\\(lldb) thread step-in
,
\\(lldb) #02
\\(lldb) frame variable x
\\(u32) x = 1
\\(lldb) thread step-in
,
\\(lldb) #03
\\(lldb) frame variable x
\\(u32) x = 1
\\(lldb) thread step-in
,
\\(lldb) #04
\\(lldb) frame variable x
\\(u32) x = 1
\\(lldb) thread step-in
,
\\(lldb) #05
\\(lldb) frame variable x
\\(u32) x = 2
\\(lldb) thread step-in
,
\\(lldb) #06
\\(lldb) frame variable x
\\(u32) x = 2
\\(lldb) thread step-in
,
\\(lldb) #07
\\(lldb) frame variable x
\\(u32) x = 2
\\(lldb) thread step-in
,
\\(lldb) #08
\\(lldb) frame variable x
\\(u32) x = 3
\\(lldb) thread step-in
,
\\(lldb) #09
\\(lldb) frame variable x
\\(u32) x = 3
\\(lldb) thread step-in
,
\\(lldb) #10
\\(lldb) frame variable x
\\(u32) x = 3
\\(lldb) thread step-in
,
\\(lldb) #11
\\(lldb) frame variable x
\\(u32) x = 3
\\(lldb) thread step-in
,
\\(lldb) #12
\\(lldb) frame variable x
\\(u32) x = 3
\\(lldb) thread step-in
,
\\(lldb) #13
\\(lldb) frame variable x
\\(u32) x = 4
\\(lldb) thread step-in
,
\\(lldb) #14
\\(lldb) frame variable x
\\(u32) x = 4
\\(lldb) thread step-in
,
\\(lldb) #15
\\(lldb) frame variable x
\\(u32) x = 4
\\(lldb) thread step-in
,
\\(lldb) #16
\\(lldb) frame variable x
\\(u32) x = 5
\\(lldb) thread step-in
,
\\(lldb) #17
\\(lldb) frame variable x
\\(u32) x = 5
\\(lldb) thread step-in
,
\\(lldb) #18
\\(lldb) frame variable x
\\(u32) x = 5
\\(lldb) thread step-in
,
\\(lldb) #19
\\(lldb) frame variable x
\\(u32) x = 6
\\(lldb) thread step-in
,
\\(lldb) #20
\\(lldb) frame variable x
\\(u32) x = 6
\\(lldb) thread step-in
,
\\(lldb) #21
\\(lldb) frame variable x
\\(u32) x = 6
\\(lldb) thread step-in
,
\\(lldb) #22
\\(lldb) frame variable x
\\(u32) x = 6
\\(lldb) thread step-in
,
\\(lldb) #23
\\(lldb) frame variable x
\\(u32) x = 6
\\(lldb) thread step-in
,
\\(lldb) #24
\\(lldb) frame variable x
\\(u32) x = 6
\\(lldb) thread step-in
,
\\(lldb) #25
\\(lldb) frame variable x
\\(u32) x = 6
\\(lldb) thread step-in
,
\\(lldb) #26
\\(lldb) frame variable x
\\(u32) x = 6
\\(lldb) thread step-in
,
\\(lldb) #27
\\(lldb) frame variable x
\\(u32) x = 6
\\(lldb) thread step-in
,
\\(lldb) #28
\\(lldb) frame variable x
\\(u32) x = 6
\\(lldb) thread step-in
,
\\(lldb) #29
\\(lldb) frame variable x
\\(u32) x = 6
\\(lldb) thread step-in
,
\\(lldb) #30
\\(lldb) frame variable x
\\(u32) x = 6
\\(lldb) thread step-in
,
\\(lldb) #31
\\(lldb) frame variable x
\\(u32) x = 6
\\(lldb) thread step-in
,
\\(lldb) #32
\\(lldb) frame variable x
\\(u32) x = 6
\\(lldb) thread step-in
,
\\(lldb) #33
\\(lldb) frame variable x
\\(u32) x = 7
\\(lldb) thread step-in
,
\\(lldb) #34
\\(lldb) frame variable x
\\(u32) x = 7
\\(lldb) thread step-in
,
\\(lldb) #35
\\(lldb) frame variable x
\\(u32) x = 8
\\(lldb) thread step-in
,
\\(lldb) #36
\\(lldb) frame variable x
\\(u32) x = 8
\\(lldb) thread step-in
,
\\(lldb) #37
\\(lldb) frame variable x
\\(u32) x = 9
\\(lldb) thread step-in
,
\\(lldb) #38
\\(lldb) frame variable x
\\(u32) x = 9
\\(lldb) thread step-in
,
\\(lldb) #39
\\(lldb) frame variable x
\\(u32) x = 10
\\(lldb) thread step-in
,
\\(lldb) #40
\\(lldb) frame variable x
\\(u32) x = 10
\\(lldb) thread step-in
,
\\(lldb) #41
\\(lldb) frame variable x
\\(u32) x = 11
\\(lldb) thread step-in
,
\\(lldb) #42
\\(lldb) frame variable x
\\(u32) x = 11
\\(lldb) thread step-in
,
\\(lldb) #43
\\(lldb) frame variable x
\\(u32) x = 12
\\(lldb) thread step-in
,
\\(lldb) #44
\\(lldb) frame variable x
\\(u32) x = 12
\\(lldb) thread step-in
,
\\(lldb) #45
\\(lldb) frame variable x
\\(u32) x = 12
},
);
db.addLldbTest(
"inline_call",
target,