From 0d696a48dadffbb3dcd3f7ada6867129da4d70c8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 4 Aug 2020 00:22:11 -0700 Subject: [PATCH] stage2 .debug_line: handle Decl line numbers changing --- src-self-hosted/Module.zig | 39 ++++++++++++++++++++++++-------------- src-self-hosted/link.zig | 33 ++++++++++++++++++++++++++++++-- 2 files changed, 56 insertions(+), 16 deletions(-) diff --git a/src-self-hosted/Module.zig b/src-self-hosted/Module.zig index 335bb06908..5a260c4cd2 100644 --- a/src-self-hosted/Module.zig +++ b/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 { diff --git a/src-self-hosted/link.zig b/src-self-hosted/link.zig index b96c1ae3a0..c3f5146113 100644 --- a/src-self-hosted/link.zig +++ b/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 {};