Commit 627cf6ce48

Jakub Konka <kubkon@jakubkonka.com>
2022-01-31 17:07:45
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.
1 parent abbcf40
Changed files (5)
src/arch/x86_64/Emit.zig
@@ -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 => {},
src/link/Elf.zig
@@ -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);
 }
 
src/AstGen.zig
@@ -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;
src/Module.zig
@@ -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;
src/Zir.zig
@@ -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