astgen: clean up source line calculation and management

Clarify that `astgen.advanceSourceCursor` already increments absolute
values of the line and columns numbers; i.e., `GenZir.calcLine` is thus
not only obsolete but wrong by design.

Incidentally, this clean up allows for specifying the `FnDecl` line
numbers for DWARF use correctly as relative values with respect to
the start of the parent `Decl`. This `Decl` in turn has its line number
information specified relatively to its parent `Decl`, and so on, until
we reach the global scope.
This commit is contained in:
Jakub Konka 2022-01-31 17:07:45 +01:00 committed by Andrew Kelley
parent abbcf40327
commit 627cf6ce48
5 changed files with 70 additions and 53 deletions

View File

@ -27,9 +27,11 @@ string_bytes: ArrayListUnmanaged(u8) = .{},
/// to avoid starting over the line/column scan for every declaration, which
/// would be O(N^2).
source_offset: u32 = 0,
/// Tracks the current line of `source_offset`.
/// Tracks the corresponding line of `source_offset`.
/// This value is absolute.
source_line: u32 = 0,
/// Tracks the current column of `source_offset`.
/// Tracks the corresponding column of `source_offset`.
/// This value is absolute.
source_column: u32 = 0,
/// Used for temporary allocations; freed after AstGen is complete.
/// The resulting ZIR code has no references to anything in this arena.
@ -2511,7 +2513,7 @@ fn makeDeferScope(
const token_starts = tree.tokens.items(.start);
const node_start = token_starts[tree.firstToken(expr_node)];
const defer_scope = try block_arena.create(Scope.Defer);
astgen.advanceSourceCursor(tree.source, node_start);
astgen.advanceSourceCursor(node_start);
defer_scope.* = .{
.base = .{ .tag = scope_tag },
@ -2775,14 +2777,9 @@ fn emitDbgNode(gz: *GenZir, node: Ast.Node.Index) !void {
if (gz.force_comptime) return;
const astgen = gz.astgen;
const tree = astgen.tree;
const source = tree.source;
const token_starts = tree.tokens.items(.start);
const node_start = token_starts[tree.firstToken(node)];
astgen.advanceSourceCursor(source, node_start);
const line = @intCast(u32, astgen.source_line);
const column = @intCast(u32, astgen.source_column);
astgen.advanceSourceCursorToNode(node);
const line = astgen.source_line - gz.decl_line;
const column = astgen.source_column;
_ = try gz.add(.{ .tag = .dbg_stmt, .data = .{
.dbg_stmt = .{
@ -3188,12 +3185,13 @@ fn fnDecl(
// We insert this at the beginning so that its instruction index marks the
// start of the top level declaration.
const block_inst = try gz.makeBlockInst(.block_inline, fn_proto.ast.proto_node);
astgen.advanceSourceCursorToNode(decl_node);
var decl_gz: GenZir = .{
.force_comptime = true,
.in_defer = false,
.decl_node_index = fn_proto.ast.proto_node,
.decl_line = gz.calcLine(decl_node),
.decl_line = astgen.source_line,
.parent = scope,
.astgen = astgen,
.instructions = gz.instructions,
@ -3391,11 +3389,9 @@ fn fnDecl(
astgen.fn_block = &fn_gz;
defer astgen.fn_block = prev_fn_block;
const token_starts = tree.tokens.items(.start);
const lbrace_start = token_starts[tree.firstToken(body_node)];
astgen.advanceSourceCursor(tree.source, lbrace_start);
const lbrace_line = @intCast(u32, astgen.source_line);
const lbrace_column = @intCast(u32, astgen.source_column);
astgen.advanceSourceCursorToNode(body_node);
const lbrace_line = astgen.source_line - decl_gz.decl_line;
const lbrace_column = astgen.source_column;
_ = try expr(&fn_gz, params_scope, .none, body_node);
try checkUsed(gz, &fn_gz.base, params_scope);
@ -3465,11 +3461,12 @@ fn globalVarDecl(
const name_token = var_decl.ast.mut_token + 1;
const name_str_index = try astgen.identAsString(name_token);
astgen.advanceSourceCursorToNode(node);
var block_scope: GenZir = .{
.parent = scope,
.decl_node_index = node,
.decl_line = gz.calcLine(node),
.decl_line = astgen.source_line,
.astgen = astgen,
.force_comptime = true,
.in_defer = false,
@ -3614,12 +3611,13 @@ fn comptimeDecl(
// top-level declaration.
const block_inst = try gz.makeBlockInst(.block_inline, node);
wip_members.nextDecl(false, false, false, false);
astgen.advanceSourceCursorToNode(node);
var decl_block: GenZir = .{
.force_comptime = true,
.in_defer = false,
.decl_node_index = node,
.decl_line = gz.calcLine(node),
.decl_line = astgen.source_line,
.parent = scope,
.astgen = astgen,
.instructions = gz.instructions,
@ -3668,12 +3666,13 @@ fn usingnamespaceDecl(
// top-level declaration.
const block_inst = try gz.makeBlockInst(.block_inline, node);
wip_members.nextDecl(is_pub, true, false, false);
astgen.advanceSourceCursorToNode(node);
var decl_block: GenZir = .{
.force_comptime = true,
.in_defer = false,
.decl_node_index = node,
.decl_line = gz.calcLine(node),
.decl_line = astgen.source_line,
.parent = scope,
.astgen = astgen,
.instructions = gz.instructions,
@ -3715,12 +3714,13 @@ fn testDecl(
const block_inst = try gz.makeBlockInst(.block_inline, node);
wip_members.nextDecl(false, false, false, false);
astgen.advanceSourceCursorToNode(node);
var decl_block: GenZir = .{
.force_comptime = true,
.in_defer = false,
.decl_node_index = node,
.decl_line = gz.calcLine(node),
.decl_line = astgen.source_line,
.parent = scope,
.astgen = astgen,
.instructions = gz.instructions,
@ -3756,11 +3756,9 @@ fn testDecl(
astgen.fn_block = &fn_block;
defer astgen.fn_block = prev_fn_block;
const token_starts = tree.tokens.items(.start);
const lbrace_start = token_starts[tree.firstToken(body_node)];
astgen.advanceSourceCursor(tree.source, lbrace_start);
const lbrace_line = @intCast(u32, astgen.source_line);
const lbrace_column = @intCast(u32, astgen.source_column);
astgen.advanceSourceCursorToNode(body_node);
const lbrace_line = astgen.source_line - decl_block.decl_line;
const lbrace_column = astgen.source_column;
const block_result = try expr(&fn_block, &fn_block.base, .none, body_node);
if (fn_block.isEmpty() or !fn_block.refIsNoReturn(block_result)) {
@ -3841,10 +3839,11 @@ fn structDeclInner(
// The struct_decl instruction introduces a scope in which the decls of the struct
// are in scope, so that field types, alignments, and default value expressions
// can refer to decls within the struct itself.
astgen.advanceSourceCursorToNode(node);
var block_scope: GenZir = .{
.parent = &namespace.base,
.decl_node_index = node,
.decl_line = gz.calcLine(node),
.decl_line = astgen.source_line,
.astgen = astgen,
.force_comptime = true,
.in_defer = false,
@ -3966,10 +3965,11 @@ fn unionDeclInner(
// The union_decl instruction introduces a scope in which the decls of the union
// are in scope, so that field types, alignments, and default value expressions
// can refer to decls within the union itself.
astgen.advanceSourceCursorToNode(node);
var block_scope: GenZir = .{
.parent = &namespace.base,
.decl_node_index = node,
.decl_line = gz.calcLine(node),
.decl_line = astgen.source_line,
.astgen = astgen,
.force_comptime = true,
.in_defer = false,
@ -4249,10 +4249,11 @@ fn containerDecl(
// The enum_decl instruction introduces a scope in which the decls of the enum
// are in scope, so that tag values can refer to decls within the enum itself.
astgen.advanceSourceCursorToNode(node);
var block_scope: GenZir = .{
.parent = &namespace.base,
.decl_node_index = node,
.decl_line = gz.calcLine(node),
.decl_line = astgen.source_line,
.astgen = astgen,
.force_comptime = true,
.in_defer = false,
@ -6980,7 +6981,7 @@ fn builtinCall(
const token_starts = tree.tokens.items(.start);
const node_start = token_starts[tree.firstToken(node)];
astgen.advanceSourceCursor(tree.source, node_start);
astgen.advanceSourceCursor(node_start);
const result = try gz.addExtendedPayload(.builtin_src, Zir.Inst.LineColumn{
.line = @intCast(u32, astgen.source_line),
@ -9560,18 +9561,6 @@ const GenZir = struct {
return false;
}
fn calcLine(gz: GenZir, node: Ast.Node.Index) u32 {
const astgen = gz.astgen;
const tree = astgen.tree;
const source = tree.source;
const token_starts = tree.tokens.items(.start);
const node_start = token_starts[tree.firstToken(node)];
astgen.advanceSourceCursor(source, node_start);
return @intCast(u32, gz.decl_line + astgen.source_line);
}
fn nodeIndexToRelative(gz: GenZir, node_index: Ast.Node.Index) i32 {
return @bitCast(i32, node_index) - @bitCast(i32, gz.decl_node_index);
}
@ -9704,8 +9693,8 @@ const GenZir = struct {
assert(node_tags[fn_decl] == .fn_decl or node_tags[fn_decl] == .test_decl);
const block = node_datas[fn_decl].rhs;
const rbrace_start = token_starts[tree.lastToken(block)];
astgen.advanceSourceCursor(tree.source, rbrace_start);
const rbrace_line = @intCast(u32, astgen.source_line);
astgen.advanceSourceCursor(rbrace_start);
const rbrace_line = @intCast(u32, astgen.source_line - gz.decl_line);
const rbrace_column = @intCast(u32, astgen.source_column);
const columns = args.lbrace_column | (rbrace_column << 16);
@ -10736,7 +10725,17 @@ fn detectLocalShadowing(
};
}
fn advanceSourceCursor(astgen: *AstGen, source: []const u8, end: usize) void {
/// Advances the source cursor to the beginning of `node`.
fn advanceSourceCursorToNode(astgen: *AstGen, node: Ast.Node.Index) void {
const tree = astgen.tree;
const token_starts = tree.tokens.items(.start);
const node_start = token_starts[tree.firstToken(node)];
astgen.advanceSourceCursor(node_start);
}
/// Advances the source cursor to an absolute byte offset `end` in the file.
fn advanceSourceCursor(astgen: *AstGen, end: usize) void {
const source = astgen.tree.source;
var i = astgen.source_offset;
var line = astgen.source_line;
var column = astgen.source_column;

View File

@ -375,6 +375,7 @@ pub const Decl = struct {
src_node: Ast.Node.Index,
/// Line number corresponding to `src_node`. Stored separately so that source files
/// do not need to be loaded into memory in order to compute debug line numbers.
/// This value is absolute.
src_line: u32,
/// Index to ZIR `extra` array to the entry in the parent's decl structure
/// (the part that says "for every decls_len"). The first item at this index is
@ -4122,7 +4123,8 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi
const has_linksection_or_addrspace = (flags & 0b1000) != 0;
// zig fmt: on
const line = iter.parent_decl.relativeToLine(zir.extra[decl_sub_index + 4]);
const line_off = zir.extra[decl_sub_index + 4];
const line = iter.parent_decl.relativeToLine(line_off);
const decl_name_index = zir.extra[decl_sub_index + 5];
const decl_index = zir.extra[decl_sub_index + 6];
const decl_block_inst_data = zir.instructions.items(.data)[decl_index].pl_node;

View File

@ -2309,9 +2309,9 @@ pub const Inst = struct {
body_len: u32,
pub const SrcLocs = struct {
/// Absolute line index in the source file.
/// Line index in the source file relative to the parent decl.
lbrace_line: u32,
/// Absolute line index in the source file.
/// Line index in the source file relative to the parent decl.
rbrace_line: u32,
/// lbrace_column is least significant bits u16
/// rbrace_column is most significant bits u16

View File

@ -787,12 +787,14 @@ fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
assert(tag == .dbg_line);
const payload = emit.mir.instructions.items(.data)[inst].payload;
const dbg_line_column = emit.mir.extraData(Mir.DbgLineColumn, payload).data;
log.debug("mirDbgLine", .{});
try emit.dbgAdvancePCAndLine(dbg_line_column.line, dbg_line_column.column);
}
fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) InnerError!void {
const delta_line = @intCast(i32, line) - @intCast(i32, emit.prev_di_line);
const delta_pc: usize = emit.code.items.len - emit.prev_di_pc;
log.debug(" (advance pc={d} and line={d})", .{ delta_line, delta_pc });
switch (emit.debug_output) {
.dwarf => |dbg_out| {
// TODO Look into using the DWARF special opcodes to compress this data.
@ -806,7 +808,6 @@ fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) InnerError!void {
leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable;
}
dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.copy);
emit.prev_di_pc = emit.code.items.len;
emit.prev_di_line = line;
emit.prev_di_column = column;
emit.prev_di_pc = emit.code.items.len;
@ -856,6 +857,7 @@ fn mirDbgPrologueEnd(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
switch (emit.debug_output) {
.dwarf => |dbg_out| {
try dbg_out.dbg_line.append(DW.LNS.set_prologue_end);
log.debug("mirDbgPrologueEnd (line={d}, col={d})", .{ emit.prev_di_line, emit.prev_di_column });
try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
},
.plan9 => {},
@ -869,6 +871,7 @@ fn mirDbgEpilogueBegin(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
switch (emit.debug_output) {
.dwarf => |dbg_out| {
try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin);
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 => {},

View File

@ -2507,7 +2507,13 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven
defer deinitRelocs(self.base.allocator, &dbg_info_type_relocs);
const decl = func.owner_decl;
const line_off = @intCast(u28, decl.src_line + func.lbrace_line);
log.debug("updateFunc {s}{*}", .{ decl.name, func.owner_decl });
log.debug(" (decl.src_line={d}, func.lbrace_line={d}, func.rbrace_line={d})", .{
decl.src_line,
func.lbrace_line,
func.rbrace_line,
});
const line = @intCast(u28, decl.src_line + func.lbrace_line);
const ptr_width_bytes = self.ptrWidthBytes();
dbg_line_buffer.appendSliceAssumeCapacity(&[_]u8{
@ -2524,7 +2530,7 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven
// to this function's begin curly.
assert(self.getRelocDbgLineOff() == dbg_line_buffer.items.len);
// Here we use a ULEB128-fixed-4 to make sure this field can be overwritten later.
leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), line_off);
leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), line);
dbg_line_buffer.appendAssumeCapacity(DW.LNS.set_file);
assert(self.getRelocDbgFileIndex() == dbg_line_buffer.items.len);
@ -3070,15 +3076,22 @@ pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Dec
const tracy = trace(@src());
defer tracy.end();
log.debug("updateDeclLineNumber {s}{*}", .{ decl.name, decl });
if (self.llvm_object) |_| return;
const func = decl.val.castTag(.function).?.data;
const casted_line_off = @intCast(u28, decl.src_line + func.lbrace_line);
log.debug(" (decl.src_line={d}, func.lbrace_line={d}, func.rbrace_line={d})", .{
decl.src_line,
func.lbrace_line,
func.rbrace_line,
});
const line = @intCast(u28, decl.src_line + func.lbrace_line);
const shdr = &self.sections.items[self.debug_line_section_index.?];
const file_pos = shdr.sh_offset + decl.fn_link.elf.off + self.getRelocDbgLineOff();
var data: [4]u8 = undefined;
leb128.writeUnsignedFixed(4, &data, casted_line_off);
leb128.writeUnsignedFixed(4, &data, line);
try self.base.file.?.pwriteAll(&data, file_pos);
}