Commit 0d696a48da

Andrew Kelley <andrew@ziglang.org>
2020-08-04 09:22:11
stage2 .debug_line: handle Decl line numbers changing
1 parent 30ee08d
Changed files (2)
src-self-hosted
src-self-hosted/link.zig
@@ -85,6 +85,13 @@ pub const File = struct {
         }
     }
 
+    pub fn updateDeclLineNumber(base: *File, module: *Module, decl: *Module.Decl) !void {
+        switch (base.tag) {
+            .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl),
+            .c => {},
+        }
+    }
+
     pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void {
         switch (base.tag) {
             .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),
@@ -203,7 +210,7 @@ pub const File = struct {
 
         pub fn fail(self: *C, src: usize, comptime format: []const u8, args: anytype) !void {
             self.error_msg = try Module.ErrorMsg.create(self.allocator, src, format, args);
-            return error.CGenFailure;
+            return error.AnalysisFail;
         }
 
         pub fn deinit(self: *File.C) void {
@@ -217,7 +224,7 @@ pub const File = struct {
 
         pub fn updateDecl(self: *File.C, module: *Module, decl: *Module.Decl) !void {
             c_codegen.generate(self, decl) catch |err| {
-                if (err == error.CGenFailure) {
+                if (err == error.AnalysisFail) {
                     try module.failed_decls.put(module.gpa, decl, self.error_msg);
                 }
                 return err;
@@ -2088,6 +2095,28 @@ pub const File = struct {
             }
         }
 
+        /// Must be called only after a successful call to `updateDecl`.
+        pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Decl) !void {
+            const tracy = trace(@src());
+            defer tracy.end();
+
+            const scope_file = decl.scope.cast(Module.Scope.File).?;
+            const tree = scope_file.contents.tree;
+            const file_ast_decls = tree.root_node.decls();
+            // TODO Look into improving the performance here by adding a token-index-to-line
+            // lookup table. Currently this involves scanning over the source code for newlines.
+            const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?;
+            const block = fn_proto.body().?.castTag(.Block).?;
+            const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start);
+            const casted_line_off = @intCast(u28, line_delta);
+
+            const shdr = &self.sections.items[self.debug_line_section_index.?];
+            const file_pos = shdr.sh_offset + decl.fn_link.off + self.getRelocDbgLineOff();
+            var data: [4]u8 = undefined;
+            leb128.writeUnsignedFixed(4, &data, casted_line_off);
+            try self.file.?.pwriteAll(&data, file_pos);
+        }
+
         pub fn deleteExport(self: *Elf, exp: Export) void {
             const sym_index = exp.sym_index orelse return;
             self.global_symbol_free_list.append(self.allocator, sym_index) catch {};
src-self-hosted/Module.zig
@@ -89,6 +89,9 @@ const WorkItem = union(enum) {
     /// It may have already be analyzed, or it may have been determined
     /// to be outdated; in this case perform semantic analysis again.
     analyze_decl: *Decl,
+    /// The source file containing the Decl has been updated, and so the
+    /// Decl may need its line number information updated in the debug info.
+    update_line_number: *Decl,
 };
 
 pub const Export = struct {
@@ -1064,22 +1067,14 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
                     error.AnalysisFail => {
                         decl.analysis = .dependency_failure;
                     },
-                    error.CGenFailure => {
-                        // Error is handled by CBE, don't try adding it again
-                    },
                     else => {
                         try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1);
-                        const result = self.failed_decls.getOrPutAssumeCapacity(decl);
-                        if (result.found_existing) {
-                            std.debug.panic("Internal error: attempted to override error '{}' with 'unable to codegen: {}'", .{ result.entry.value.msg, @errorName(err) });
-                        } else {
-                            result.entry.value = try ErrorMsg.create(
-                                self.gpa,
-                                decl.src(),
-                                "unable to codegen: {}",
-                                .{@errorName(err)},
-                            );
-                        }
+                        self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
+                            self.gpa,
+                            decl.src(),
+                            "unable to codegen: {}",
+                            .{@errorName(err)},
+                        ));
                         decl.analysis = .codegen_failure_retryable;
                     },
                 };
@@ -1091,6 +1086,18 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
                 error.AnalysisFail => continue,
             };
         },
+        .update_line_number => |decl| {
+            self.bin_file.updateDeclLineNumber(self, decl) catch |err| {
+                try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1);
+                self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
+                    self.gpa,
+                    decl.src(),
+                    "unable to update line number: {}",
+                    .{@errorName(err)},
+                ));
+                decl.analysis = .codegen_failure_retryable;
+            };
+        },
     };
 }
 
@@ -1530,6 +1537,10 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
                     if (!srcHashEql(decl.contents_hash, contents_hash)) {
                         try self.markOutdatedDecl(decl);
                         decl.contents_hash = contents_hash;
+                    } else if (decl.fn_link.len != 0) {
+                        // TODO Look into detecting when this would be unnecessary by storing enough state
+                        // in `Decl` to notice that the line number did not change.
+                        self.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
                     }
                 }
             } else {