authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-03 19:14:09-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-03 19:14:09-07:00
logac10841fa9321c71fa0e682521dd39872d43c132
tree0d9a732545c9bab6942593b2c794d6251671e46e
parent4e023c6fa85c3263dbf388be5ef84dae7f1b0022

stage2 .debug_line: simpler strategy for incremental compilation

See #5963

3 files changed, 121 insertions(+), 180 deletions(-)

src-self-hosted/Module.zig-4
......@@ -506,10 +506,6 @@ pub const Scope = struct {
506506 /// Direct children of the file.
507507 decls: ArrayListUnmanaged(*Decl),
508508
509 /// Represents the file in the linker code. The linker code
510 /// uses this field to store data relevant to its purposes.
511 link: link.File.Elf.SrcFile = link.File.Elf.SrcFile.empty,
512
513509 pub fn unload(self: *File, gpa: *Allocator) void {
514510 switch (self.status) {
515511 .never_loaded,
src-self-hosted/codegen.zig+3-1
......@@ -469,7 +469,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
469469
470470 try self.dbgSetPrologueEnd();
471471 try self.genBody(self.mod_fn.analysis.success);
472 try self.dbgSetEpilogueBegin();
473472
474473 const stack_end = self.branch_stack.items[0].max_end_stack;
475474 if (stack_end > math.maxInt(i32))
......@@ -491,6 +490,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
491490 mem.writeIntLittle(i32, self.code.items[jmp_reloc..][0..4], s32_amt);
492491 }
493492
493 // Important to be after the possible self.code.items.len -= 5 above.
494 try self.dbgSetEpilogueBegin();
495
494496 try self.code.ensureCapacity(self.code.items.len + 9);
495497 // add rsp, x
496498 if (aligned_stack_end > math.maxInt(i8)) {
src-self-hosted/link.zig+118-175
......@@ -344,8 +344,11 @@ pub const File = struct {
344344 text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = std.ArrayListUnmanaged(*TextBlock){},
345345 last_text_block: ?*TextBlock = null,
346346
347 first_dbg_line_file: ?*SrcFile = null,
348 last_dbg_line_file: ?*SrcFile = null,
347 /// A list of `SrcFn` whose Line Number Programs have surplus capacity.
348 /// This is the same concept as `text_block_free_list`; see those doc comments.
349 dbg_line_fn_free_list: std.AutoHashMapUnmanaged(*SrcFn, void) = .{},
350 dbg_line_fn_first: ?*SrcFn = null,
351 dbg_line_fn_last: ?*SrcFn = null,
349352
350353 /// `alloc_num / alloc_den` is the factor of padding when allocating.
351354 const alloc_num = 4;
......@@ -411,46 +414,20 @@ pub const File = struct {
411414 };
412415
413416 pub const SrcFn = struct {
414 /// Offset from the `SrcFile` that contains this function.
417 /// Offset from the beginning of the Debug Line Program header that contains this function.
415418 off: u32,
416419 /// Size of the line number program component belonging to this function, not
417420 /// including padding.
418421 len: u32,
419422
420 pub const empty: SrcFn = .{
421 .off = 0,
422 .len = 0,
423 };
424 };
425
426 pub const SrcFile = struct {
427 /// Byte offset from the start of the Line Number Program that contains this file.
428 off: u32,
429 /// Length in bytes, not including padding, of this file component within the
430 /// Line Number Program that contains it.
431 len: u32,
432
433 /// An ordered list of all the `SrcFn` in this file. This list is not redundant with
434 /// the source Decl list, for two reasons:
435 /// * Lazy decl analysis: some source functions do not correspond to any compiled functions.
436 /// * Generic functions: some source functions correspond to many compiled functions.
437 /// This list corresponds to the file data in the Line Number Program. When a new `SrcFn`
438 /// is inserted, the list must be shifted to accomodate it, and likewise the Line
439 /// Number Program data must be shifted within the ELF file to accomodate (if there is
440 /// not enough padding).
441 /// It is a hash map so that we can look up the index based on the `*SrcFn` and therefore
442 /// find the next and previous functions.
443 fns: std.AutoHashMapUnmanaged(*SrcFn, void),
444
445423 /// Points to the previous and next neighbors, based on the offset from .debug_line.
446 /// This can be used to find, for example, the capacity of this `SrcFile`.
447 prev: ?*SrcFile,
448 next: ?*SrcFile,
424 /// This can be used to find, for example, the capacity of this `SrcFn`.
425 prev: ?*SrcFn,
426 next: ?*SrcFn,
449427
450 pub const empty: SrcFile = .{
428 pub const empty: SrcFn = .{
451429 .off = 0,
452430 .len = 0,
453 .fns = .{},
454431 .prev = null,
455432 .next = null,
456433 };
......@@ -593,11 +570,11 @@ pub const File = struct {
593570 }
594571
595572 fn getDebugLineProgramOff(self: Elf) u32 {
596 return self.first_dbg_line_file.?.off;
573 return self.dbg_line_fn_first.?.off;
597574 }
598575
599576 fn getDebugLineProgramEnd(self: Elf) u32 {
600 return self.last_dbg_line_file.?.off + self.last_dbg_line_file.?.len;
577 return self.dbg_line_fn_last.?.off + self.dbg_line_fn_last.?.len;
601578 }
602579
603580 /// Returns end pos of collision, if any.
......@@ -1207,7 +1184,7 @@ pub const File = struct {
12071184
12081185 // The size of this header is variable, depending on the number of directories,
12091186 // files, and padding. We have a function to compute the upper bound size, however,
1210 // because it's needed for determining where to put the offset of the first `SrcFile`.
1187 // because it's needed for determining where to put the offset of the first `SrcFn`.
12111188 try di_buf.ensureCapacity(self.dbgLineNeededHeaderBytes());
12121189
12131190 // initial length - length of the .debug_line contribution for this compilation unit,
......@@ -1280,18 +1257,13 @@ pub const File = struct {
12801257 },
12811258 }
12821259
1283 // We use a NOP jmp because consumers empirically do not respect the header length field.
1284 const after_jmp = di_buf.items.len + 6;
1285 if (after_jmp > dbg_line_prg_off) {
1260 // We use NOPs because consumers empirically do not respect the header length field.
1261 if (di_buf.items.len > dbg_line_prg_off) {
12861262 // Move the first N files to the end to make more padding for the header.
12871263 @panic("TODO: handle .debug_line header exceeding its padding");
12881264 }
1289 const jmp_amt = dbg_line_prg_off - after_jmp + 1;
1290 di_buf.appendAssumeCapacity(DW.LNS_extended_op);
1291 leb128.writeUnsignedFixed(4, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u28, jmp_amt));
1292 di_buf.appendAssumeCapacity(DW.LNE_hi_user);
1293
1294 try self.file.?.pwriteAll(di_buf.items, debug_line_sect.sh_offset);
1265 const jmp_amt = dbg_line_prg_off - di_buf.items.len;
1266 try self.pwriteWithNops(di_buf.items, jmp_amt, debug_line_sect.sh_offset);
12951267 self.debug_line_header_dirty = false;
12961268 }
12971269
......@@ -1826,6 +1798,16 @@ pub const File = struct {
18261798 // For functions we need to add a prologue to the debug line program.
18271799 try dbg_line_buffer.ensureCapacity(26);
18281800
1801 const scope_file = decl.scope.cast(Module.Scope.File).?;
1802 const tree = scope_file.contents.tree;
1803 const file_ast_decls = tree.root_node.decls();
1804 // TODO Look into improving the performance here by adding a token-index-to-line
1805 // lookup table. Currently this involves scanning over the source code for newlines.
1806 const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?;
1807 const block = fn_proto.body().?.castTag(.Block).?;
1808 const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start);
1809 const casted_line_off = @intCast(u28, line_delta);
1810
18291811 const ptr_width_bytes = self.ptrWidthBytes();
18301812 dbg_line_buffer.appendSliceAssumeCapacity(&[_]u8{
18311813 DW.LNS_extended_op,
......@@ -1840,9 +1822,15 @@ pub const File = struct {
18401822 // This is the "relocatable" relative line offset from the previous function's end curly
18411823 // to this function's begin curly.
18421824 assert(self.getRelocDbgLineOff() == dbg_line_buffer.items.len);
1843 // Here we allocate 4 bytes for the relocation. This field is a ULEB128, however,
1844 // it is possible to encode small values as still taking up 4 bytes.
1845 dbg_line_buffer.items.len += 4;
1825 // Here we use a ULEB128-fixed-4 to make sure this field can be overwritten later.
1826 leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), casted_line_off);
1827
1828 dbg_line_buffer.appendAssumeCapacity(DW.LNS_set_file);
1829 assert(self.getRelocDbgFileIndex() == dbg_line_buffer.items.len);
1830 // Once we support more than one source file, this will have the ability to be more
1831 // than one possible value.
1832 const file_index = 1;
1833 leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), file_index);
18461834
18471835 // Emit a line for the begin curly with prologue_end=false. The codegen will
18481836 // do the work of setting prologue_end=true and epilogue_begin=true.
......@@ -1916,14 +1904,6 @@ pub const File = struct {
19161904
19171905 // If the Decl is a function, we need to update the .debug_line program.
19181906 if (is_fn) {
1919 // For padding between functions, we terminate with `LNS_extended_op` with sub-op
1920 // `LNE_hi_user`, using a fixed 4-byte ULEB128 for the opcode size. This is always
1921 // found at the very end of the SrcFile's Line Number Program component.
1922 try dbg_line_buffer.ensureCapacity(dbg_line_buffer.items.len + 6);
1923 dbg_line_buffer.appendAssumeCapacity(DW.LNS_extended_op);
1924 leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), 1);
1925 dbg_line_buffer.appendAssumeCapacity(DW.LNE_hi_user);
1926
19271907 // Perform the relocation based on vaddr.
19281908 const target_endian = self.base.options.target.cpu.arch.endian();
19291909 switch (self.ptr_width) {
......@@ -1937,94 +1917,53 @@ pub const File = struct {
19371917 },
19381918 }
19391919
1940 // Now we want to write the line offset relocation, however, first we must
1941 // "plug in" the SrcFn into its parent SrcFile, so that we know what function the line
1942 // number is offset from. It must go in the same order as the functions are found
1943 // in the Zig source. When we insert a function before another one, the latter one
1944 // must have its line offset relocation updated.
1920 try dbg_line_buffer.appendSlice(&[_]u8{ DW.LNS_extended_op, 1, DW.LNE_end_sequence });
1921
1922 // Now we have the full contents and may allocate a region to store it.
19451923
19461924 const debug_line_sect = &self.sections.items[self.debug_line_section_index.?];
1947 const scope_file = decl.scope.cast(Module.Scope.File).?;
1948 const src_file = &scope_file.link;
19491925 const src_fn = &typed_value.val.cast(Value.Payload.Function).?.func.link;
1950 var src_fn_index: usize = undefined;
1951 if (src_file.len == 0) {
1952 // This is the first function of the SrcFile.
1953 assert(src_file.fns.entries.items.len == 0);
1954 src_fn_index = 0;
1955 try src_file.fns.put(self.allocator, src_fn, {});
1956
1957 if (self.last_dbg_line_file) |last| {
1958 src_file.prev = last;
1959 self.last_dbg_line_file = src_file;
1960
1961 // Update the previous last SrcFile's terminating NOP to skip to the start
1962 // of the new last SrcFile's start.
1963 @panic("TODO updateDecl for .debug_line: add new SrcFile: append");
1964 } else {
1965 // This is the first file (and function) of the Line Number Program.
1966 self.first_dbg_line_file = src_file;
1967 self.last_dbg_line_file = src_file;
1968
1969 src_fn.off = dbg_line_file_header_len;
1926 if (self.dbg_line_fn_last) |last| {
1927 if (src_fn.prev == null and src_fn.next == null) {
1928 // Append new function.
1929 src_fn.prev = last;
1930 last.next = src_fn;
1931 self.dbg_line_fn_last = src_fn;
1932
1933 src_fn.off = last.off + (last.len * alloc_num / alloc_den);
19701934 src_fn.len = @intCast(u32, dbg_line_buffer.items.len);
1971
1972 src_file.off = self.dbgLineNeededHeaderBytes() * alloc_num / alloc_den;
1973 src_file.len = src_fn.off + src_fn.len + dbg_line_file_trailer_len;
1974
1975 const needed_size = src_file.off + src_file.len;
1976 if (needed_size > debug_line_sect.sh_size) {
1977 debug_line_sect.sh_offset = self.findFreeSpace(needed_size, 1);
1978 }
1979 debug_line_sect.sh_size = needed_size;
1980 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
1981 self.debug_line_header_dirty = true;
1982
1983 try self.updateDbgLineFile(src_file);
1935 } else {
1936 // Update existing function.
1937 @panic("TODO updateDecl for .debug_line: add new SrcFn: update");
19841938 }
19851939 } else {
1986 @panic("TODO updateDecl for .debug_line: update existing SrcFile");
1987 //src_fn_index = @panic("TODO");
1940 // This is the first function of the Line Number Program.
1941 self.dbg_line_fn_first = src_fn;
1942 self.dbg_line_fn_last = src_fn;
1943
1944 src_fn.off = self.dbgLineNeededHeaderBytes() * alloc_num / alloc_den;
1945 src_fn.len = @intCast(u32, dbg_line_buffer.items.len);
19881946 }
1989 const line_off: u28 = blk: {
1990 const tree = scope_file.contents.tree;
1991 const file_ast_decls = tree.root_node.decls();
1992 // TODO Look into improving the performance here by adding a token-index-to-line
1993 // lookup table. Currently this involves scanning over the source code for newlines
1994 // (but only from the previous decl to the current one).
1995 if (src_fn_index == 0) {
1996 // Since it's the first function in the file, the line number delta is just the
1997 // line number of the open curly from the beginning of the file.
1998 const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?;
1999 const block = fn_proto.body().?.castTag(.Block).?;
2000 const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start);
2001 // No need to add one; this is a delta from DWARF's starting line number (1).
2002 break :blk @intCast(u28, line_delta);
2003 } else {
2004 const prev_src_fn = src_file.fns.entries.items[src_fn_index - 1].key;
2005 const mod_fn = @fieldParentPtr(Module.Fn, "link", prev_src_fn);
2006 const prev_fn_proto = file_ast_decls[mod_fn.owner_decl.src_index].castTag(.FnProto).?;
2007 const this_fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?;
2008 const prev_block = prev_fn_proto.body().?.castTag(.Block).?;
2009 const this_block = this_fn_proto.body().?.castTag(.Block).?;
2010 // Find the difference between prev decl end curly and this decl begin curly.
2011 const line_delta = std.zig.lineDelta(tree.source,
2012 tree.token_locs[prev_block.rbrace].start,
2013 tree.token_locs[this_block.lbrace].start,
2014 );
2015 // No need to add one; this is a delta from the previous line number.
2016 break :blk @intCast(u28, line_delta);
2017 }
2018 };
20191947
2020 // Here we use a ULEB128 but we write 4 bytes regardless (possibly wasting space) because
2021 // that is the amount of space we allocated for this field.
2022 leb128.writeUnsignedFixed(4, dbg_line_buffer.items[self.getRelocDbgLineOff()..][0..4], line_off);
1948 const needed_size = src_fn.off + src_fn.len;
1949 if (needed_size != debug_line_sect.sh_size) {
1950 if (needed_size > self.allocatedSize(debug_line_sect.sh_offset)) {
1951 const new_offset = self.findFreeSpace(needed_size, 1);
1952 const existing_size = src_fn.off;
1953 const amt = try self.file.?.copyRangeAll(debug_line_sect.sh_offset, self.file.?, new_offset, existing_size);
1954 if (amt != existing_size) return error.InputOutput;
1955 debug_line_sect.sh_offset = new_offset;
1956 }
1957 debug_line_sect.sh_size = needed_size;
1958 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
1959 self.debug_line_header_dirty = true;
1960 }
1961 const padding_size: u32 = if (src_fn.next) |next| next.off - (src_fn.off + src_fn.len) else 0;
20231962
20241963 // We only have support for one compilation unit so far, so the offsets are directly
20251964 // from the .debug_line section.
2026 const file_pos = debug_line_sect.sh_offset + src_file.off + src_fn.off;
2027 try self.file.?.pwriteAll(dbg_line_buffer.items, file_pos);
1965 const file_pos = debug_line_sect.sh_offset + src_fn.off;
1966 try self.pwriteWithNops(dbg_line_buffer.items, padding_size, file_pos);
20281967 }
20291968
20301969 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
......@@ -2116,47 +2055,6 @@ pub const File = struct {
21162055 self.global_symbols.items[sym_index].st_info = 0;
21172056 }
21182057
2119 const dbg_line_file_header_len = 5; // DW.LNS_set_file + ULEB128-fixed-4 file_index
2120 const dbg_line_file_trailer_len = 9; // DW.LNE_end_sequence + 6-byte terminating NOP
2121
2122 fn updateDbgLineFile(self: *Elf, src_file: *SrcFile) !void {
2123 const target_endian = self.base.options.target.cpu.arch.endian();
2124 const shdr = &self.sections.items[self.debug_line_section_index.?];
2125 const header_off = shdr.sh_offset + src_file.off;
2126 {
2127 var header: [dbg_line_file_header_len]u8 = undefined;
2128 header[0] = DW.LNS_set_file;
2129 // Once we support more than one source file, this will have the ability to be more
2130 // than one possible value.
2131 const file_index = 1;
2132 leb128.writeUnsignedFixed(4, header[1..5], file_index);
2133 try self.file.?.pwriteAll(&header, header_off);
2134 }
2135 {
2136 const last_src_fn = src_file.fns.entries.items[src_file.fns.entries.items.len - 1].key;
2137 const trailer_off = header_off + last_src_fn.off + last_src_fn.len;
2138 const padding_to_next = blk: {
2139 if (src_file.next) |next| {
2140 break :blk next.off - (src_file.off + src_file.len);
2141 } else {
2142 // No need for padding after this one; we will add padding to it when a SrcFile
2143 // is added after it.
2144 break :blk 0;
2145 }
2146 };
2147 var trailer: [dbg_line_file_trailer_len]u8 = undefined;
2148
2149 trailer[0] = DW.LNS_extended_op;
2150 trailer[1] = 1;
2151 trailer[2] = DW.LNE_end_sequence;
2152
2153 trailer[3] = DW.LNS_extended_op;
2154 leb128.writeUnsignedFixed(4, trailer[4..8], @intCast(u28, padding_to_next + 1));
2155 trailer[8] = DW.LNE_hi_user;
2156 try self.file.?.pwriteAll(&trailer, trailer_off);
2157 }
2158 }
2159
21602058 fn writeProgHeader(self: *Elf, index: usize) !void {
21612059 const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
21622060 const offset = self.program_headers.items[index].p_offset;
......@@ -2366,6 +2264,10 @@ pub const File = struct {
23662264 return dbg_line_vaddr_reloc_index + self.ptrWidthBytes() + 1;
23672265 }
23682266
2267 fn getRelocDbgFileIndex(self: Elf) usize {
2268 return self.getRelocDbgLineOff() + 5;
2269 }
2270
23692271 fn dbgLineNeededHeaderBytes(self: Elf) u32 {
23702272 const directory_entry_format_count = 1;
23712273 const file_name_entry_format_count = 1;
......@@ -2376,9 +2278,50 @@ pub const File = struct {
23762278 // These are encoded as DW.FORM_string rather than DW.FORM_strp as we would like
23772279 // because of a workaround for readelf and gdb failing to understand DWARFv5 correctly.
23782280 self.base.options.root_pkg.root_src_dir_path.len +
2379 self.base.options.root_pkg.root_src_path.len * 2);
2281 self.base.options.root_pkg.root_src_path.len);
23802282
23812283 }
2284
2285 /// Writes to the file a buffer, followed by the specified number of bytes of NOPs.
2286 /// Asserts `padding_size >= 2` and less than 126,976 bytes (if this limit is ever
2287 /// reached, this function can be improved to make more than one pwritev call).
2288 fn pwriteWithNops(self: *Elf, buf: []const u8, padding_size: usize, offset: usize) !void {
2289 const page_of_nops = [1]u8{DW.LNS_negate_stmt} ** 4096;
2290 const three_byte_nop = [3]u8{DW.LNS_advance_pc, 0b1000_0000, 0};
2291 var vecs: [32]std.os.iovec_const = undefined;
2292 var vec_index: usize = 0;
2293 vecs[vec_index] = .{
2294 .iov_base = buf.ptr,
2295 .iov_len = buf.len,
2296 };
2297 vec_index += 1;
2298 var padding_left = padding_size;
2299 if (padding_left % 2 != 0) {
2300 vecs[vec_index] = .{
2301 .iov_base = &three_byte_nop,
2302 .iov_len = three_byte_nop.len,
2303 };
2304 vec_index += 1;
2305 padding_left -= three_byte_nop.len;
2306 }
2307 while (padding_left > page_of_nops.len) {
2308 vecs[vec_index] = .{
2309 .iov_base = &page_of_nops,
2310 .iov_len = page_of_nops.len,
2311 };
2312 vec_index += 1;
2313 padding_left -= page_of_nops.len;
2314 }
2315 if (padding_left > 0) {
2316 vecs[vec_index] = .{
2317 .iov_base = &page_of_nops,
2318 .iov_len = padding_left,
2319 };
2320 vec_index += 1;
2321 }
2322 try self.file.?.pwritevAll(vecs[0..vec_index], offset);
2323 }
2324
23822325 };
23832326};
23842327