Commit 065e10c95c

mlugg <mlugg@mlugg.co.uk>
2025-01-01 21:16:48
link: new incremental line number update API
1 parent 136c5a9
src/link/Elf/ZigObject.zig
@@ -1863,22 +1863,9 @@ pub fn updateExports(
     }
 }
 
-/// Must be called only after a successful call to `updateNav`.
-pub fn updateNavLineNumber(
-    self: *ZigObject,
-    pt: Zcu.PerThread,
-    nav_index: InternPool.Nav.Index,
-) !void {
-    const tracy = trace(@src());
-    defer tracy.end();
-
-    const ip = &pt.zcu.intern_pool;
-    const nav = ip.getNav(nav_index);
-
-    log.debug("updateNavLineNumber {}({d})", .{ nav.fqn.fmt(ip), nav_index });
-
+pub fn updateLineNumber(self: *ZigObject, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void {
     if (self.dwarf) |*dwarf| {
-        try dwarf.updateNavLineNumber(pt.zcu, nav_index);
+        try dwarf.updateLineNumber(pt.zcu, ti_id);
     }
 }
 
src/link/MachO/ZigObject.zig
@@ -1432,14 +1432,9 @@ fn updateLazySymbol(
     try macho_file.base.file.?.pwriteAll(code, file_offset);
 }
 
-/// Must be called only after a successful call to `updateNav`.
-pub fn updateNavLineNumber(
-    self: *ZigObject,
-    pt: Zcu.PerThread,
-    nav_index: InternPool.Nav.Index,
-) !void {
+pub fn updateLineNumber(self: *ZigObject, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void {
     if (self.dwarf) |*dwarf| {
-        try dwarf.updateNavLineNumber(pt.zcu, nav_index);
+        try dwarf.updateLineNumber(pt.zcu, ti_id);
     }
 }
 
src/link/Wasm/ZigObject.zig
@@ -1074,15 +1074,9 @@ pub fn createDebugSectionForIndex(zig_object: *ZigObject, wasm: *Wasm, index: *?
     return atom_index;
 }
 
-pub fn updateDeclLineNumber(
-    zig_object: *ZigObject,
-    pt: Zcu.PerThread,
-    decl_index: InternPool.DeclIndex,
-) !void {
+pub fn updateLineNumber(zig_object: *ZigObject, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void {
     if (zig_object.dwarf) |*dw| {
-        const decl = pt.zcu.declPtr(decl_index);
-        log.debug("updateDeclLineNumber {}{*}", .{ decl.fqn.fmt(&pt.zcu.intern_pool), decl });
-        try dw.updateDeclLineNumber(pt.zcu, decl_index);
+        try dw.updateLineNumber(pt.zcu, ti_id);
     }
 }
 
src/link/C.zig
@@ -379,12 +379,12 @@ pub fn updateNav(self: *C, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !
     gop.value_ptr.fwd_decl = try self.addString(object.dg.fwd_decl.items);
 }
 
-pub fn updateNavLineNumber(self: *C, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {
+pub fn updateLineNumber(self: *C, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void {
     // The C backend does not have the ability to fix line numbers without re-generating
     // the entire Decl.
     _ = self;
     _ = pt;
-    _ = nav_index;
+    _ = ti_id;
 }
 
 pub fn flush(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) !void {
src/link/Coff.zig
@@ -2484,11 +2484,11 @@ pub fn getGlobalSymbol(coff: *Coff, name: []const u8, lib_name_name: ?[]const u8
     return global_index;
 }
 
-pub fn updateDeclLineNumber(coff: *Coff, pt: Zcu.PerThread, decl_index: InternPool.DeclIndex) !void {
+pub fn updateLineNumber(coff: *Coff, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void {
     _ = coff;
     _ = pt;
-    _ = decl_index;
-    log.debug("TODO implement updateDeclLineNumber", .{});
+    _ = ti_id;
+    log.debug("TODO implement updateLineNumber", .{});
 }
 
 /// TODO: note if we need to rewrite base relocations by dirtying any of the entries in the global table
src/link/Dwarf.zig
@@ -4156,21 +4156,11 @@ pub fn updateContainerType(dwarf: *Dwarf, pt: Zcu.PerThread, type_index: InternP
     }
 }
 
-pub fn updateNavLineNumber(dwarf: *Dwarf, zcu: *Zcu, nav_index: InternPool.Nav.Index) UpdateError!void {
-    const ip = &zcu.intern_pool;
-
-    const zir_index = ip.getCau(ip.getNav(nav_index).analysis_owner.unwrap() orelse return).zir_index;
-    const inst_info = zir_index.resolveFull(ip).?;
-    assert(inst_info.inst != .main_struct_inst);
-    const file = zcu.fileByIndex(inst_info.file);
-
-    const line = file.zir.getDeclaration(inst_info.inst).src_line;
-    var line_buf: [4]u8 = undefined;
-    std.mem.writeInt(u32, &line_buf, line, dwarf.endian);
-
-    const unit = dwarf.debug_line.section.getUnit(dwarf.mods.get(file.mod).?);
-    const entry = unit.getEntry(dwarf.navs.get(nav_index).?);
-    try dwarf.getFile().?.pwriteAll(&line, dwarf.debug_line.section.off + unit.off + unit.header_len + entry.off + DebugInfo.declEntryLineOff(dwarf));
+pub fn updateLineNumber(dwarf: *Dwarf, zcu: *Zcu, ti_id: InternPool.TrackedInst.Index) UpdateError!void {
+    _ = dwarf;
+    _ = zcu;
+    _ = ti_id;
+    @panic("TODO: Dwarf.updateLineNumber");
 }
 
 pub fn freeNav(dwarf: *Dwarf, nav_index: InternPool.Nav.Index) void {
src/link/Elf.zig
@@ -2372,9 +2372,9 @@ pub fn updateExports(
     return self.zigObjectPtr().?.updateExports(self, pt, exported, export_indices);
 }
 
-pub fn updateNavLineNumber(self: *Elf, pt: Zcu.PerThread, nav: InternPool.Nav.Index) !void {
+pub fn updateLineNumber(self: *Elf, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void {
     if (self.llvm_object) |_| return;
-    return self.zigObjectPtr().?.updateNavLineNumber(pt, nav);
+    return self.zigObjectPtr().?.updateLineNumber(pt, ti_id);
 }
 
 pub fn deleteExport(
src/link/MachO.zig
@@ -3014,9 +3014,9 @@ pub fn updateNav(self: *MachO, pt: Zcu.PerThread, nav: InternPool.Nav.Index) !vo
     return self.getZigObject().?.updateNav(self, pt, nav);
 }
 
-pub fn updateNavLineNumber(self: *MachO, pt: Zcu.PerThread, nav: InternPool.NavIndex) !void {
+pub fn updateLineNumber(self: *MachO, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void {
     if (self.llvm_object) |_| return;
-    return self.getZigObject().?.updateNavLineNumber(pt, nav);
+    return self.getZigObject().?.updateLineNumber(pt, ti_id);
 }
 
 pub fn updateExports(
src/link/Plan9.zig
@@ -1354,11 +1354,10 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {
     }
 }
 
-/// Must be called only after a successful call to `updateDecl`.
-pub fn updateDeclLineNumber(self: *Plan9, pt: Zcu.PerThread, decl_index: InternPool.DeclIndex) !void {
+pub fn updateLineNumber(self: *Plan9, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void {
     _ = self;
     _ = pt;
-    _ = decl_index;
+    _ = ti_id;
 }
 
 pub fn getNavVAddr(
src/link/Wasm.zig
@@ -1574,9 +1574,9 @@ pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav: InternPool.Nav.Index) !voi
     try wasm.zig_object.?.updateNav(wasm, pt, nav);
 }
 
-pub fn updateNavLineNumber(wasm: *Wasm, pt: Zcu.PerThread, nav: InternPool.Nav.Index) !void {
+pub fn updateLineNumber(wasm: *Wasm, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void {
     if (wasm.llvm_object) |_| return;
-    try wasm.zig_object.?.updateNavLineNumber(pt, nav);
+    try wasm.zig_object.?.updateLineNumber(pt, ti_id);
 }
 
 /// From a given symbol location, returns its `wasm.GlobalType`.
src/Zcu/PerThread.zig
@@ -379,6 +379,7 @@ fn cleanupUpdatedFiles(gpa: Allocator, updated_files: *std.AutoArrayHashMapUnman
 pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
     assert(pt.tid == .main);
     const zcu = pt.zcu;
+    const comp = zcu.comp;
     const ip = &zcu.intern_pool;
     const gpa = zcu.gpa;
 
@@ -435,8 +436,19 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
 
             const old_zir = file.prev_zir.?.*;
             const new_zir = file.zir;
-            const old_tag = old_zir.instructions.items(.tag);
-            const old_data = old_zir.instructions.items(.data);
+            const old_tag = old_zir.instructions.items(.tag)[@intFromEnum(old_inst)];
+            const old_data = old_zir.instructions.items(.data)[@intFromEnum(old_inst)];
+
+            switch (old_tag) {
+                .declaration => {
+                    const old_line = old_zir.getDeclaration(old_inst).src_line;
+                    const new_line = new_zir.getDeclaration(new_inst).src_line;
+                    if (old_line != new_line) {
+                        try comp.queueJob(.{ .update_line_number = tracked_inst_index });
+                    }
+                },
+                else => {},
+            }
 
             if (old_zir.getAssociatedSrcHash(old_inst)) |old_hash| hash_changed: {
                 if (new_zir.getAssociatedSrcHash(new_inst)) |new_hash| {
@@ -455,8 +467,8 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
             }
 
             // If this is a `struct_decl` etc, we must invalidate any outdated namespace dependencies.
-            const has_namespace = switch (old_tag[@intFromEnum(old_inst)]) {
-                .extended => switch (old_data[@intFromEnum(old_inst)].extended.opcode) {
+            const has_namespace = switch (old_tag) {
+                .extended => switch (old_data.extended.opcode) {
                     .struct_decl, .union_decl, .opaque_decl, .enum_decl => true,
                     else => false,
                 },
@@ -2517,8 +2529,6 @@ const ScanDeclIter = struct {
             );
             try comp.queueJob(.{ .analyze_comptime_unit = unit });
         }
-
-        // TODO: we used to do line number updates here, but this is an inappropriate place for this logic to live.
     }
 };
 
@@ -3152,6 +3162,15 @@ pub fn linkerUpdateContainerType(pt: Zcu.PerThread, ty: InternPool.Index) !void
     }
 }
 
+pub fn linkerUpdateLineNumber(pt: Zcu.PerThread, ti: InternPool.TrackedInst.Index) !void {
+    if (pt.zcu.comp.bin_file) |lf| {
+        lf.updateLineNumber(pt, ti) catch |err| switch (err) {
+            error.OutOfMemory => return error.OutOfMemory,
+            else => |e| log.err("update line number failed: {s}", .{@errorName(e)}),
+        };
+    }
+}
+
 pub fn reportRetryableAstGenError(
     pt: Zcu.PerThread,
     src: Zcu.AstGenSrc,
src/Compilation.zig
@@ -348,6 +348,7 @@ const Job = union(enum) {
     /// Corresponds to the task in `link.Task`.
     /// Only needed for backends that haven't yet been updated to not race against Sema.
     codegen_type: InternPool.Index,
+    update_line_number: InternPool.TrackedInst.Index,
     /// The `AnalUnit`, which is *not* a `func`, must be semantically analyzed.
     /// This may be its first time being analyzed, or it may be outdated.
     /// If the unit is a function, a `codegen_func` job will then be queued.
@@ -3718,6 +3719,9 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre
         .codegen_type => |ty| {
             comp.dispatchCodegenTask(tid, .{ .codegen_type = ty });
         },
+        .update_line_number => |ti| {
+            comp.dispatchCodegenTask(tid, .{ .update_line_number = ti });
+        },
         .analyze_func => |func| {
             const named_frame = tracy.namedFrame("analyze_func");
             defer named_frame.end();
src/link.zig
@@ -727,16 +727,22 @@ pub const File = struct {
         }
     }
 
-    pub fn updateNavLineNumber(
-        base: *File,
-        pt: Zcu.PerThread,
-        nav_index: InternPool.Nav.Index,
-    ) UpdateNavError!void {
+    /// On an incremental update, fixup the line number of all `Nav`s at the given `TrackedInst`, because
+    /// its line number has changed. The ZIR instruction `ti_id` has tag `.declaration`.
+    pub fn updateLineNumber(base: *File, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) UpdateNavError!void {
+        {
+            const ti = ti_id.resolveFull(&pt.zcu.intern_pool).?;
+            const file = pt.zcu.fileByIndex(ti.file);
+            assert(file.zir_loaded);
+            const inst = file.zir.instructions.get(@intFromEnum(ti.inst));
+            assert(inst.tag == .declaration);
+        }
+
         switch (base.tag) {
             .spirv, .nvptx => {},
             inline else => |tag| {
                 dev.check(tag.devFeature());
-                return @as(*tag.Type(), @fieldParentPtr("base", base)).updateNavineNumber(pt, nav_index);
+                return @as(*tag.Type(), @fieldParentPtr("base", base)).updateLineNumber(pt, ti_id);
             },
         }
     }
@@ -1407,6 +1413,8 @@ pub const Task = union(enum) {
     codegen_func: CodegenFunc,
     codegen_type: InternPool.Index,
 
+    update_line_number: InternPool.TrackedInst.Index,
+
     pub const CodegenFunc = struct {
         /// This will either be a non-generic `func_decl` or a `func_instance`.
         func: InternPool.Index,
@@ -1558,6 +1566,13 @@ pub fn doTask(comp: *Compilation, tid: usize, task: Task) void {
                 error.OutOfMemory => diags.setAllocFailure(),
             };
         },
+        .update_line_number => |ti| {
+            const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));
+            defer pt.deactivate();
+            pt.linkerUpdateLineNumber(ti) catch |err| switch (err) {
+                error.OutOfMemory => diags.setAllocFailure(),
+            };
+        },
     }
 }
 
change_line_number
@@ -0,0 +1,16 @@
+#target=x86_64-linux-selfhosted
+#update=initial version
+#file=main.zig
+const std = @import("std");
+pub fn main() !void {
+    try std.io.getStdOut().writeAll("foo\n");
+}
+#expect_stdout="foo\n"
+#update=change line number
+#file=main.zig
+const std = @import("std");
+
+pub fn main() !void {
+    try std.io.getStdOut().writeAll("foo\n");
+}
+#expect_stdout="foo\n"