authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-04 20:53:47+00:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-08-04 20:53:47+00:00
log952a397b0e006444e770e51d32cce93186959bdb
treee7a8cf6fc9883bdb071ac35b59743af532aff2e2
parent4ab2f947f9fcd2c6a4181c509d7c1ab27c6e4d58
parent331f6a07a98206c3b5c096e73860ef1b7a3dfe85
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #5978 from ziglang/stage2-dwarf-incr

self-hosted: line number debug information

14 files changed, 829 insertions(+), 213 deletions(-)

build.zig+3
...@@ -77,6 +77,9 @@ pub fn build(b: *Builder) !void {...@@ -77,6 +77,9 @@ pub fn build(b: *Builder) !void {
77 const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse false;77 const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse false;
78 if (link_libc) exe.linkLibC();78 if (link_libc) exe.linkLibC();
7979
80 const log_scopes = b.option([]const []const u8, "log", "Which log scopes to enable") orelse &[0][]const u8{};
81
82 exe.addBuildOption([]const []const u8, "log_scopes", log_scopes);
80 exe.addBuildOption(bool, "enable_tracy", tracy != null);83 exe.addBuildOption(bool, "enable_tracy", tracy != null);
81 if (tracy) |tracy_path| {84 if (tracy) |tracy_path| {
82 const client_cpp = fs.path.join(85 const client_cpp = fs.path.join(
lib/std/build.zig+31-19
...@@ -430,9 +430,9 @@ pub const Builder = struct {...@@ -430,9 +430,9 @@ pub const Builder = struct {
430 const entry = self.user_input_options.getEntry(name) orelse return null;430 const entry = self.user_input_options.getEntry(name) orelse return null;
431 entry.value.used = true;431 entry.value.used = true;
432 switch (type_id) {432 switch (type_id) {
433 TypeId.Bool => switch (entry.value.value) {433 .Bool => switch (entry.value.value) {
434 UserValue.Flag => return true,434 .Flag => return true,
435 UserValue.Scalar => |s| {435 .Scalar => |s| {
436 if (mem.eql(u8, s, "true")) {436 if (mem.eql(u8, s, "true")) {
437 return true;437 return true;
438 } else if (mem.eql(u8, s, "false")) {438 } else if (mem.eql(u8, s, "false")) {
...@@ -443,21 +443,21 @@ pub const Builder = struct {...@@ -443,21 +443,21 @@ pub const Builder = struct {
443 return null;443 return null;
444 }444 }
445 },445 },
446 UserValue.List => {446 .List => {
447 warn("Expected -D{} to be a boolean, but received a list.\n", .{name});447 warn("Expected -D{} to be a boolean, but received a list.\n", .{name});
448 self.markInvalidUserInput();448 self.markInvalidUserInput();
449 return null;449 return null;
450 },450 },
451 },451 },
452 TypeId.Int => panic("TODO integer options to build script", .{}),452 .Int => panic("TODO integer options to build script", .{}),
453 TypeId.Float => panic("TODO float options to build script", .{}),453 .Float => panic("TODO float options to build script", .{}),
454 TypeId.Enum => switch (entry.value.value) {454 .Enum => switch (entry.value.value) {
455 UserValue.Flag => {455 .Flag => {
456 warn("Expected -D{} to be a string, but received a boolean.\n", .{name});456 warn("Expected -D{} to be a string, but received a boolean.\n", .{name});
457 self.markInvalidUserInput();457 self.markInvalidUserInput();
458 return null;458 return null;
459 },459 },
460 UserValue.Scalar => |s| {460 .Scalar => |s| {
461 if (std.meta.stringToEnum(T, s)) |enum_lit| {461 if (std.meta.stringToEnum(T, s)) |enum_lit| {
462 return enum_lit;462 return enum_lit;
463 } else {463 } else {
...@@ -466,33 +466,35 @@ pub const Builder = struct {...@@ -466,33 +466,35 @@ pub const Builder = struct {
466 return null;466 return null;
467 }467 }
468 },468 },
469 UserValue.List => {469 .List => {
470 warn("Expected -D{} to be a string, but received a list.\n", .{name});470 warn("Expected -D{} to be a string, but received a list.\n", .{name});
471 self.markInvalidUserInput();471 self.markInvalidUserInput();
472 return null;472 return null;
473 },473 },
474 },474 },
475 TypeId.String => switch (entry.value.value) {475 .String => switch (entry.value.value) {
476 UserValue.Flag => {476 .Flag => {
477 warn("Expected -D{} to be a string, but received a boolean.\n", .{name});477 warn("Expected -D{} to be a string, but received a boolean.\n", .{name});
478 self.markInvalidUserInput();478 self.markInvalidUserInput();
479 return null;479 return null;
480 },480 },
481 UserValue.List => {481 .List => {
482 warn("Expected -D{} to be a string, but received a list.\n", .{name});482 warn("Expected -D{} to be a string, but received a list.\n", .{name});
483 self.markInvalidUserInput();483 self.markInvalidUserInput();
484 return null;484 return null;
485 },485 },
486 UserValue.Scalar => |s| return s,486 .Scalar => |s| return s,
487 },487 },
488 TypeId.List => switch (entry.value.value) {488 .List => switch (entry.value.value) {
489 UserValue.Flag => {489 .Flag => {
490 warn("Expected -D{} to be a list, but received a boolean.\n", .{name});490 warn("Expected -D{} to be a list, but received a boolean.\n", .{name});
491 self.markInvalidUserInput();491 self.markInvalidUserInput();
492 return null;492 return null;
493 },493 },
494 UserValue.Scalar => |s| return &[_][]const u8{s},494 .Scalar => |s| {
495 UserValue.List => |lst| return lst.span(),495 return self.allocator.dupe([]const u8, &[_][]const u8{s}) catch unreachable;
496 },
497 .List => |lst| return lst.span(),
496 },498 },
497 }499 }
498 }500 }
...@@ -1706,9 +1708,19 @@ pub const LibExeObjStep = struct {...@@ -1706,9 +1708,19 @@ pub const LibExeObjStep = struct {
17061708
1707 pub fn addBuildOption(self: *LibExeObjStep, comptime T: type, name: []const u8, value: T) void {1709 pub fn addBuildOption(self: *LibExeObjStep, comptime T: type, name: []const u8, value: T) void {
1708 const out = self.build_options_contents.outStream();1710 const out = self.build_options_contents.outStream();
1711 if (T == []const []const u8) {
1712 out.print("pub const {}: []const []const u8 = &[_][]const u8{{\n", .{name}) catch unreachable;
1713 for (value) |slice| {
1714 out.writeAll(" ") catch unreachable;
1715 std.zig.renderStringLiteral(slice, out) catch unreachable;
1716 out.writeAll(",\n") catch unreachable;
1717 }
1718 out.writeAll("};\n") catch unreachable;
1719 return;
1720 }
1709 switch (@typeInfo(T)) {1721 switch (@typeInfo(T)) {
1710 .Enum => |enum_info| {1722 .Enum => |enum_info| {
1711 out.print("const {} = enum {{\n", .{@typeName(T)}) catch unreachable;1723 out.print("pub const {} = enum {{\n", .{@typeName(T)}) catch unreachable;
1712 inline for (enum_info.fields) |field| {1724 inline for (enum_info.fields) |field| {
1713 out.print(" {},\n", .{field.name}) catch unreachable;1725 out.print(" {},\n", .{field.name}) catch unreachable;
1714 }1726 }
lib/std/hash_map.zig+13-5
...@@ -196,6 +196,10 @@ pub fn HashMap(...@@ -196,6 +196,10 @@ pub fn HashMap(
196 return self.unmanaged.getEntry(key);196 return self.unmanaged.getEntry(key);
197 }197 }
198198
199 pub fn getIndex(self: Self, key: K) ?usize {
200 return self.unmanaged.getIndex(key);
201 }
202
199 pub fn get(self: Self, key: K) ?V {203 pub fn get(self: Self, key: K) ?V {
200 return self.unmanaged.get(key);204 return self.unmanaged.get(key);
201 }205 }
...@@ -479,17 +483,21 @@ pub fn HashMapUnmanaged(...@@ -479,17 +483,21 @@ pub fn HashMapUnmanaged(
479 }483 }
480484
481 pub fn getEntry(self: Self, key: K) ?*Entry {485 pub fn getEntry(self: Self, key: K) ?*Entry {
486 const index = self.getIndex(key) orelse return null;
487 return &self.entries.items[index];
488 }
489
490 pub fn getIndex(self: Self, key: K) ?usize {
482 const header = self.index_header orelse {491 const header = self.index_header orelse {
483 // Linear scan.492 // Linear scan.
484 const h = if (store_hash) hash(key) else {};493 const h = if (store_hash) hash(key) else {};
485 for (self.entries.items) |*item| {494 for (self.entries.items) |*item, i| {
486 if (item.hash == h and eql(key, item.key)) {495 if (item.hash == h and eql(key, item.key)) {
487 return item;496 return i;
488 }497 }
489 }498 }
490 return null;499 return null;
491 };500 };
492
493 switch (header.capacityIndexType()) {501 switch (header.capacityIndexType()) {
494 .u8 => return self.getInternal(key, header, u8),502 .u8 => return self.getInternal(key, header, u8),
495 .u16 => return self.getInternal(key, header, u16),503 .u16 => return self.getInternal(key, header, u16),
...@@ -711,7 +719,7 @@ pub fn HashMapUnmanaged(...@@ -711,7 +719,7 @@ pub fn HashMapUnmanaged(
711 unreachable;719 unreachable;
712 }720 }
713721
714 fn getInternal(self: Self, key: K, header: *IndexHeader, comptime I: type) ?*Entry {722 fn getInternal(self: Self, key: K, header: *IndexHeader, comptime I: type) ?usize {
715 const indexes = header.indexes(I);723 const indexes = header.indexes(I);
716 const h = hash(key);724 const h = hash(key);
717 const start_index = header.constrainIndex(h);725 const start_index = header.constrainIndex(h);
...@@ -725,7 +733,7 @@ pub fn HashMapUnmanaged(...@@ -725,7 +733,7 @@ pub fn HashMapUnmanaged(
725 const entry = &self.entries.items[index.entry_index];733 const entry = &self.entries.items[index.entry_index];
726 const hash_match = if (store_hash) h == entry.hash else true;734 const hash_match = if (store_hash) h == entry.hash else true;
727 if (hash_match and eql(key, entry.key))735 if (hash_match and eql(key, entry.key))
728 return entry;736 return index.entry_index;
729 }737 }
730 return null;738 return null;
731 }739 }
lib/std/zig.zig+16
...@@ -43,6 +43,22 @@ pub fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usi...@@ -43,6 +43,22 @@ pub fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usi
43 return .{ .line = line, .column = column };43 return .{ .line = line, .column = column };
44}44}
4545
46pub fn lineDelta(source: []const u8, start: usize, end: usize) isize {
47 var line: isize = 0;
48 if (end >= start) {
49 for (source[start..end]) |byte| switch (byte) {
50 '\n' => line += 1,
51 else => continue,
52 };
53 } else {
54 for (source[end..start]) |byte| switch (byte) {
55 '\n' => line -= 1,
56 else => continue,
57 };
58 }
59 return line;
60}
61
46/// Returns the standard file system basename of a binary generated by the Zig compiler.62/// Returns the standard file system basename of a binary generated by the Zig compiler.
47pub fn binNameAlloc(63pub fn binNameAlloc(
48 allocator: *std.mem.Allocator,64 allocator: *std.mem.Allocator,
lib/std/zig/ast.zig+6-2
...@@ -1299,6 +1299,10 @@ pub const Node = struct {...@@ -1299,6 +1299,10 @@ pub const Node = struct {
1299 });1299 });
1300 }1300 }
13011301
1302 pub fn body(self: *const FnProto) ?*Node {
1303 return self.getTrailer("body_node");
1304 }
1305
1302 pub fn getTrailer(self: *const FnProto, comptime name: []const u8) ?TrailerFlags.Field(name) {1306 pub fn getTrailer(self: *const FnProto, comptime name: []const u8) ?TrailerFlags.Field(name) {
1303 const trailers_start = @alignCast(1307 const trailers_start = @alignCast(
1304 @alignOf(ParamDecl),1308 @alignOf(ParamDecl),
...@@ -1381,7 +1385,7 @@ pub const Node = struct {...@@ -1381,7 +1385,7 @@ pub const Node = struct {
1381 .Invalid => {},1385 .Invalid => {},
1382 }1386 }
13831387
1384 if (self.getTrailer("body_node")) |body_node| {1388 if (self.body()) |body_node| {
1385 if (i < 1) return body_node;1389 if (i < 1) return body_node;
1386 i -= 1;1390 i -= 1;
1387 }1391 }
...@@ -1397,7 +1401,7 @@ pub const Node = struct {...@@ -1397,7 +1401,7 @@ pub const Node = struct {
1397 }1401 }
13981402
1399 pub fn lastToken(self: *const FnProto) TokenIndex {1403 pub fn lastToken(self: *const FnProto) TokenIndex {
1400 if (self.getTrailer("body_node")) |body_node| return body_node.lastToken();1404 if (self.body()) |body_node| return body_node.lastToken();
1401 switch (self.return_type) {1405 switch (self.return_type) {
1402 .Explicit, .InferErrorSet => |node| return node.lastToken(),1406 .Explicit, .InferErrorSet => |node| return node.lastToken(),
1403 .Invalid => |tok| return tok,1407 .Invalid => |tok| return tok,
src-self-hosted/Module.zig+63-40
...@@ -6,6 +6,7 @@ const Value = @import("value.zig").Value;...@@ -6,6 +6,7 @@ const Value = @import("value.zig").Value;
6const Type = @import("type.zig").Type;6const Type = @import("type.zig").Type;
7const TypedValue = @import("TypedValue.zig");7const TypedValue = @import("TypedValue.zig");
8const assert = std.debug.assert;8const assert = std.debug.assert;
9const log = std.log;
9const BigIntConst = std.math.big.int.Const;10const BigIntConst = std.math.big.int.Const;
10const BigIntMutable = std.math.big.int.Mutable;11const BigIntMutable = std.math.big.int.Mutable;
11const Target = std.Target;12const Target = std.Target;
...@@ -88,6 +89,9 @@ const WorkItem = union(enum) {...@@ -88,6 +89,9 @@ const WorkItem = union(enum) {
88 /// It may have already be analyzed, or it may have been determined89 /// It may have already be analyzed, or it may have been determined
89 /// to be outdated; in this case perform semantic analysis again.90 /// to be outdated; in this case perform semantic analysis again.
90 analyze_decl: *Decl,91 analyze_decl: *Decl,
92 /// The source file containing the Decl has been updated, and so the
93 /// Decl may need its line number information updated in the debug info.
94 update_line_number: *Decl,
91};95};
9296
93pub const Export = struct {97pub const Export = struct {
...@@ -175,6 +179,13 @@ pub const Decl = struct {...@@ -175,6 +179,13 @@ pub const Decl = struct {
175 /// This is populated regardless of semantic analysis and code generation.179 /// This is populated regardless of semantic analysis and code generation.
176 link: link.File.Elf.TextBlock = link.File.Elf.TextBlock.empty,180 link: link.File.Elf.TextBlock = link.File.Elf.TextBlock.empty,
177181
182 /// Represents the function in the linked output file, if the `Decl` is a function.
183 /// This is stored here and not in `Fn` because `Decl` survives across updates but
184 /// `Fn` does not.
185 /// TODO Look into making `Fn` a longer lived structure and moving this field there
186 /// to save on memory usage.
187 fn_link: link.File.Elf.SrcFn = link.File.Elf.SrcFn.empty,
188
178 contents_hash: std.zig.SrcHash,189 contents_hash: std.zig.SrcHash,
179190
180 /// The shallow set of other decls whose typed_value could possibly change if this Decl's191 /// The shallow set of other decls whose typed_value could possibly change if this Decl's
...@@ -235,7 +246,7 @@ pub const Decl = struct {...@@ -235,7 +246,7 @@ pub const Decl = struct {
235246
236 pub fn dump(self: *Decl) void {247 pub fn dump(self: *Decl) void {
237 const loc = std.zig.findLineColumn(self.scope.source.bytes, self.src);248 const loc = std.zig.findLineColumn(self.scope.source.bytes, self.src);
238 std.debug.warn("{}:{}:{} name={} status={}", .{249 std.debug.print("{}:{}:{} name={} status={}", .{
239 self.scope.sub_file_path,250 self.scope.sub_file_path,
240 loc.line + 1,251 loc.line + 1,
241 loc.column + 1,252 loc.column + 1,
...@@ -243,9 +254,9 @@ pub const Decl = struct {...@@ -243,9 +254,9 @@ pub const Decl = struct {
243 @tagName(self.analysis),254 @tagName(self.analysis),
244 });255 });
245 if (self.typedValueManaged()) |tvm| {256 if (self.typedValueManaged()) |tvm| {
246 std.debug.warn(" ty={} val={}", .{ tvm.typed_value.ty, tvm.typed_value.val });257 std.debug.print(" ty={} val={}", .{ tvm.typed_value.ty, tvm.typed_value.val });
247 }258 }
248 std.debug.warn("\n", .{});259 std.debug.print("\n", .{});
249 }260 }
250261
251 pub fn typedValueManaged(self: *Decl) ?*TypedValue.Managed {262 pub fn typedValueManaged(self: *Decl) ?*TypedValue.Managed {
...@@ -541,7 +552,7 @@ pub const Scope = struct {...@@ -541,7 +552,7 @@ pub const Scope = struct {
541552
542 pub fn dumpSrc(self: *File, src: usize) void {553 pub fn dumpSrc(self: *File, src: usize) void {
543 const loc = std.zig.findLineColumn(self.source.bytes, src);554 const loc = std.zig.findLineColumn(self.source.bytes, src);
544 std.debug.warn("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 });555 std.debug.print("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 });
545 }556 }
546557
547 pub fn getSource(self: *File, module: *Module) ![:0]const u8 {558 pub fn getSource(self: *File, module: *Module) ![:0]const u8 {
...@@ -643,7 +654,7 @@ pub const Scope = struct {...@@ -643,7 +654,7 @@ pub const Scope = struct {
643654
644 pub fn dumpSrc(self: *ZIRModule, src: usize) void {655 pub fn dumpSrc(self: *ZIRModule, src: usize) void {
645 const loc = std.zig.findLineColumn(self.source.bytes, src);656 const loc = std.zig.findLineColumn(self.source.bytes, src);
646 std.debug.warn("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 });657 std.debug.print("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 });
647 }658 }
648659
649 pub fn getSource(self: *ZIRModule, module: *Module) ![:0]const u8 {660 pub fn getSource(self: *ZIRModule, module: *Module) ![:0]const u8 {
...@@ -792,7 +803,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {...@@ -792,7 +803,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
792 const bin_file_dir = options.bin_file_dir orelse std.fs.cwd();803 const bin_file_dir = options.bin_file_dir orelse std.fs.cwd();
793 const bin_file = try link.File.openPath(gpa, bin_file_dir, options.bin_file_path, .{804 const bin_file = try link.File.openPath(gpa, bin_file_dir, options.bin_file_path, .{
794 .root_name = root_name,805 .root_name = root_name,
795 .root_src_dir_path = options.root_pkg.root_src_dir_path,806 .root_pkg = options.root_pkg,
796 .target = options.target,807 .target = options.target,
797 .output_mode = options.output_mode,808 .output_mode = options.output_mode,
798 .link_mode = options.link_mode orelse .Static,809 .link_mode = options.link_mode orelse .Static,
...@@ -885,6 +896,7 @@ pub fn deinit(self: *Module) void {...@@ -885,6 +896,7 @@ pub fn deinit(self: *Module) void {
885896
886fn freeExportList(gpa: *Allocator, export_list: []*Export) void {897fn freeExportList(gpa: *Allocator, export_list: []*Export) void {
887 for (export_list) |exp| {898 for (export_list) |exp| {
899 gpa.free(exp.options.name);
888 gpa.destroy(exp);900 gpa.destroy(exp);
889 }901 }
890 gpa.free(export_list);902 gpa.free(export_list);
...@@ -943,7 +955,6 @@ pub fn update(self: *Module) !void {...@@ -943,7 +955,6 @@ pub fn update(self: *Module) !void {
943 }955 }
944956
945 self.link_error_flags = self.bin_file.errorFlags();957 self.link_error_flags = self.bin_file.errorFlags();
946 std.log.debug(.module, "link_error_flags: {}\n", .{self.link_error_flags});
947958
948 // If there are any errors, we anticipate the source files being loaded959 // If there are any errors, we anticipate the source files being loaded
949 // to report error messages. Otherwise we unload all source files to save memory.960 // to report error messages. Otherwise we unload all source files to save memory.
...@@ -1057,22 +1068,14 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {...@@ -1057,22 +1068,14 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
1057 error.AnalysisFail => {1068 error.AnalysisFail => {
1058 decl.analysis = .dependency_failure;1069 decl.analysis = .dependency_failure;
1059 },1070 },
1060 error.CGenFailure => {
1061 // Error is handled by CBE, don't try adding it again
1062 },
1063 else => {1071 else => {
1064 try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1);1072 try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1);
1065 const result = self.failed_decls.getOrPutAssumeCapacity(decl);1073 self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
1066 if (result.found_existing) {1074 self.gpa,
1067 std.debug.panic("Internal error: attempted to override error '{}' with 'unable to codegen: {}'", .{ result.entry.value.msg, @errorName(err) });1075 decl.src(),
1068 } else {1076 "unable to codegen: {}",
1069 result.entry.value = try ErrorMsg.create(1077 .{@errorName(err)},
1070 self.gpa,1078 ));
1071 decl.src(),
1072 "unable to codegen: {}",
1073 .{@errorName(err)},
1074 );
1075 }
1076 decl.analysis = .codegen_failure_retryable;1079 decl.analysis = .codegen_failure_retryable;
1077 },1080 },
1078 };1081 };
...@@ -1084,6 +1087,18 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {...@@ -1084,6 +1087,18 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
1084 error.AnalysisFail => continue,1087 error.AnalysisFail => continue,
1085 };1088 };
1086 },1089 },
1090 .update_line_number => |decl| {
1091 self.bin_file.updateDeclLineNumber(self, decl) catch |err| {
1092 try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1);
1093 self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
1094 self.gpa,
1095 decl.src(),
1096 "unable to update line number: {}",
1097 .{@errorName(err)},
1098 ));
1099 decl.analysis = .codegen_failure_retryable;
1100 };
1101 },
1087 };1102 };
1088}1103}
10891104
...@@ -1101,12 +1116,10 @@ pub fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {...@@ -1101,12 +1116,10 @@ pub fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {
1101 .codegen_failure_retryable,1116 .codegen_failure_retryable,
1102 => return error.AnalysisFail,1117 => return error.AnalysisFail,
11031118
1104 .complete, .outdated => blk: {1119 .complete => return,
1105 if (decl.generation == self.generation) {1120
1106 assert(decl.analysis == .complete);1121 .outdated => blk: {
1107 return;1122 log.debug(.module, "re-analyzing {}\n", .{decl.name});
1108 }
1109 //std.debug.warn("re-analyzing {}\n", .{decl.name});
11101123
1111 // The exports this Decl performs will be re-discovered, so we remove them here1124 // The exports this Decl performs will be re-discovered, so we remove them here
1112 // prior to re-analysis.1125 // prior to re-analysis.
...@@ -1481,6 +1494,9 @@ fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {...@@ -1481,6 +1494,9 @@ fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {
1481}1494}
14821495
1483fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {1496fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
1497 const tracy = trace(@src());
1498 defer tracy.end();
1499
1484 // We may be analyzing it for the first time, or this may be1500 // We may be analyzing it for the first time, or this may be
1485 // an incremental update. This code handles both cases.1501 // an incremental update. This code handles both cases.
1486 const tree = try self.getAstTree(root_scope);1502 const tree = try self.getAstTree(root_scope);
...@@ -1522,6 +1538,10 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {...@@ -1522,6 +1538,10 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
1522 if (!srcHashEql(decl.contents_hash, contents_hash)) {1538 if (!srcHashEql(decl.contents_hash, contents_hash)) {
1523 try self.markOutdatedDecl(decl);1539 try self.markOutdatedDecl(decl);
1524 decl.contents_hash = contents_hash;1540 decl.contents_hash = contents_hash;
1541 } else if (decl.fn_link.len != 0) {
1542 // TODO Look into detecting when this would be unnecessary by storing enough state
1543 // in `Decl` to notice that the line number did not change.
1544 self.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
1525 }1545 }
1526 }1546 }
1527 } else {1547 } else {
...@@ -1540,7 +1560,7 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {...@@ -1540,7 +1560,7 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
1540 // Handle explicitly deleted decls from the source code. Not to be confused1560 // Handle explicitly deleted decls from the source code. Not to be confused
1541 // with when we delete decls because they are no longer referenced.1561 // with when we delete decls because they are no longer referenced.
1542 for (deleted_decls.items()) |entry| {1562 for (deleted_decls.items()) |entry| {
1543 //std.debug.warn("noticed '{}' deleted from source\n", .{entry.key.name});1563 log.debug(.module, "noticed '{}' deleted from source\n", .{entry.key.name});
1544 try self.deleteDecl(entry.key);1564 try self.deleteDecl(entry.key);
1545 }1565 }
1546}1566}
...@@ -1569,7 +1589,6 @@ fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void {...@@ -1569,7 +1589,6 @@ fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void {
1569 const name_hash = root_scope.fullyQualifiedNameHash(src_decl.name);1589 const name_hash = root_scope.fullyQualifiedNameHash(src_decl.name);
1570 if (self.decl_table.get(name_hash)) |decl| {1590 if (self.decl_table.get(name_hash)) |decl| {
1571 deleted_decls.removeAssertDiscard(decl);1591 deleted_decls.removeAssertDiscard(decl);
1572 //std.debug.warn("'{}' contents: '{}'\n", .{ src_decl.name, src_decl.contents });
1573 if (!srcHashEql(src_decl.contents_hash, decl.contents_hash)) {1592 if (!srcHashEql(src_decl.contents_hash, decl.contents_hash)) {
1574 try self.markOutdatedDecl(decl);1593 try self.markOutdatedDecl(decl);
1575 decl.contents_hash = src_decl.contents_hash;1594 decl.contents_hash = src_decl.contents_hash;
...@@ -1594,7 +1613,7 @@ fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void {...@@ -1594,7 +1613,7 @@ fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void {
1594 // Handle explicitly deleted decls from the source code. Not to be confused1613 // Handle explicitly deleted decls from the source code. Not to be confused
1595 // with when we delete decls because they are no longer referenced.1614 // with when we delete decls because they are no longer referenced.
1596 for (deleted_decls.items()) |entry| {1615 for (deleted_decls.items()) |entry| {
1597 //std.debug.warn("noticed '{}' deleted from source\n", .{entry.key.name});1616 log.debug(.module, "noticed '{}' deleted from source\n", .{entry.key.name});
1598 try self.deleteDecl(entry.key);1617 try self.deleteDecl(entry.key);
1599 }1618 }
1600}1619}
...@@ -1606,7 +1625,7 @@ fn deleteDecl(self: *Module, decl: *Decl) !void {...@@ -1606,7 +1625,7 @@ fn deleteDecl(self: *Module, decl: *Decl) !void {
1606 // not be present in the set, and this does nothing.1625 // not be present in the set, and this does nothing.
1607 decl.scope.removeDecl(decl);1626 decl.scope.removeDecl(decl);
16081627
1609 //std.debug.warn("deleting decl '{}'\n", .{decl.name});1628 log.debug(.module, "deleting decl '{}'\n", .{decl.name});
1610 const name_hash = decl.fullyQualifiedNameHash();1629 const name_hash = decl.fullyQualifiedNameHash();
1611 self.decl_table.removeAssertDiscard(name_hash);1630 self.decl_table.removeAssertDiscard(name_hash);
1612 // Remove itself from its dependencies, because we are about to destroy the decl pointer.1631 // Remove itself from its dependencies, because we are about to destroy the decl pointer.
...@@ -1668,6 +1687,7 @@ fn deleteDeclExports(self: *Module, decl: *Decl) void {...@@ -1668,6 +1687,7 @@ fn deleteDeclExports(self: *Module, decl: *Decl) void {
1668 entry.value.destroy(self.gpa);1687 entry.value.destroy(self.gpa);
1669 }1688 }
1670 _ = self.symbol_exports.remove(exp.options.name);1689 _ = self.symbol_exports.remove(exp.options.name);
1690 self.gpa.free(exp.options.name);
1671 self.gpa.destroy(exp);1691 self.gpa.destroy(exp);
1672 }1692 }
1673 self.gpa.free(kv.value);1693 self.gpa.free(kv.value);
...@@ -1692,17 +1712,17 @@ fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {...@@ -1692,17 +1712,17 @@ fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
1692 const fn_zir = func.analysis.queued;1712 const fn_zir = func.analysis.queued;
1693 defer fn_zir.arena.promote(self.gpa).deinit();1713 defer fn_zir.arena.promote(self.gpa).deinit();
1694 func.analysis = .{ .in_progress = {} };1714 func.analysis = .{ .in_progress = {} };
1695 //std.debug.warn("set {} to in_progress\n", .{decl.name});1715 log.debug(.module, "set {} to in_progress\n", .{decl.name});
16961716
1697 try zir_sema.analyzeBody(self, &inner_block.base, fn_zir.body);1717 try zir_sema.analyzeBody(self, &inner_block.base, fn_zir.body);
16981718
1699 const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items);1719 const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items);
1700 func.analysis = .{ .success = .{ .instructions = instructions } };1720 func.analysis = .{ .success = .{ .instructions = instructions } };
1701 //std.debug.warn("set {} to success\n", .{decl.name});1721 log.debug(.module, "set {} to success\n", .{decl.name});
1702}1722}
17031723
1704fn markOutdatedDecl(self: *Module, decl: *Decl) !void {1724fn markOutdatedDecl(self: *Module, decl: *Decl) !void {
1705 //std.debug.warn("mark {} outdated\n", .{decl.name});1725 log.debug(.module, "mark {} outdated\n", .{decl.name});
1706 try self.work_queue.writeItem(.{ .analyze_decl = decl });1726 try self.work_queue.writeItem(.{ .analyze_decl = decl });
1707 if (self.failed_decls.remove(decl)) |entry| {1727 if (self.failed_decls.remove(decl)) |entry| {
1708 entry.value.destroy(self.gpa);1728 entry.value.destroy(self.gpa);
...@@ -1768,7 +1788,7 @@ pub fn resolveDefinedValue(self: *Module, scope: *Scope, base: *Inst) !?Value {...@@ -1768,7 +1788,7 @@ pub fn resolveDefinedValue(self: *Module, scope: *Scope, base: *Inst) !?Value {
1768 return null;1788 return null;
1769}1789}
17701790
1771pub fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const u8, exported_decl: *Decl) !void {1791pub fn analyzeExport(self: *Module, scope: *Scope, src: usize, borrowed_symbol_name: []const u8, exported_decl: *Decl) !void {
1772 try self.ensureDeclAnalyzed(exported_decl);1792 try self.ensureDeclAnalyzed(exported_decl);
1773 const typed_value = exported_decl.typed_value.most_recent.typed_value;1793 const typed_value = exported_decl.typed_value.most_recent.typed_value;
1774 switch (typed_value.ty.zigTypeTag()) {1794 switch (typed_value.ty.zigTypeTag()) {
...@@ -1782,6 +1802,9 @@ pub fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []co...@@ -1782,6 +1802,9 @@ pub fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []co
1782 const new_export = try self.gpa.create(Export);1802 const new_export = try self.gpa.create(Export);
1783 errdefer self.gpa.destroy(new_export);1803 errdefer self.gpa.destroy(new_export);
17841804
1805 const symbol_name = try self.gpa.dupe(u8, borrowed_symbol_name);
1806 errdefer self.gpa.free(symbol_name);
1807
1785 const owner_decl = scope.decl().?;1808 const owner_decl = scope.decl().?;
17861809
1787 new_export.* = .{1810 new_export.* = .{
...@@ -1794,7 +1817,7 @@ pub fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []co...@@ -1794,7 +1817,7 @@ pub fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []co
1794 };1817 };
17951818
1796 // Add to export_owners table.1819 // Add to export_owners table.
1797 const eo_gop = self.export_owners.getOrPut(self.gpa, owner_decl) catch unreachable;1820 const eo_gop = self.export_owners.getOrPutAssumeCapacity(owner_decl);
1798 if (!eo_gop.found_existing) {1821 if (!eo_gop.found_existing) {
1799 eo_gop.entry.value = &[0]*Export{};1822 eo_gop.entry.value = &[0]*Export{};
1800 }1823 }
...@@ -1803,7 +1826,7 @@ pub fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []co...@@ -1803,7 +1826,7 @@ pub fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []co
1803 errdefer eo_gop.entry.value = self.gpa.shrink(eo_gop.entry.value, eo_gop.entry.value.len - 1);1826 errdefer eo_gop.entry.value = self.gpa.shrink(eo_gop.entry.value, eo_gop.entry.value.len - 1);
18041827
1805 // Add to exported_decl table.1828 // Add to exported_decl table.
1806 const de_gop = self.decl_exports.getOrPut(self.gpa, exported_decl) catch unreachable;1829 const de_gop = self.decl_exports.getOrPutAssumeCapacity(exported_decl);
1807 if (!de_gop.found_existing) {1830 if (!de_gop.found_existing) {
1808 de_gop.entry.value = &[0]*Export{};1831 de_gop.entry.value = &[0]*Export{};
1809 }1832 }
...@@ -2811,7 +2834,7 @@ pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void {...@@ -2811,7 +2834,7 @@ pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void {
2811 const source = zir_module.getSource(self) catch @panic("dumpInst failed to get source");2834 const source = zir_module.getSource(self) catch @panic("dumpInst failed to get source");
2812 const loc = std.zig.findLineColumn(source, inst.src);2835 const loc = std.zig.findLineColumn(source, inst.src);
2813 if (inst.tag == .constant) {2836 if (inst.tag == .constant) {
2814 std.debug.warn("constant ty={} val={} src={}:{}:{}\n", .{2837 std.debug.print("constant ty={} val={} src={}:{}:{}\n", .{
2815 inst.ty,2838 inst.ty,
2816 inst.castTag(.constant).?.val,2839 inst.castTag(.constant).?.val,
2817 zir_module.subFilePath(),2840 zir_module.subFilePath(),
...@@ -2819,7 +2842,7 @@ pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void {...@@ -2819,7 +2842,7 @@ pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void {
2819 loc.column + 1,2842 loc.column + 1,
2820 });2843 });
2821 } else if (inst.deaths == 0) {2844 } else if (inst.deaths == 0) {
2822 std.debug.warn("{} ty={} src={}:{}:{}\n", .{2845 std.debug.print("{} ty={} src={}:{}:{}\n", .{
2823 @tagName(inst.tag),2846 @tagName(inst.tag),
2824 inst.ty,2847 inst.ty,
2825 zir_module.subFilePath(),2848 zir_module.subFilePath(),
...@@ -2827,7 +2850,7 @@ pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void {...@@ -2827,7 +2850,7 @@ pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void {
2827 loc.column + 1,2850 loc.column + 1,
2828 });2851 });
2829 } else {2852 } else {
2830 std.debug.warn("{} ty={} deaths={b} src={}:{}:{}\n", .{2853 std.debug.print("{} ty={} deaths={b} src={}:{}:{}\n", .{
2831 @tagName(inst.tag),2854 @tagName(inst.tag),
2832 inst.ty,2855 inst.ty,
2833 inst.deaths,2856 inst.deaths,
src-self-hosted/astgen.zig+2-1
...@@ -120,6 +120,8 @@ pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block...@@ -120,6 +120,8 @@ pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block
120120
121 var scope = parent_scope;121 var scope = parent_scope;
122 for (block_node.statements()) |statement| {122 for (block_node.statements()) |statement| {
123 const src = scope.tree().token_locs[statement.firstToken()].start;
124 _ = try addZIRNoOp(mod, scope, src, .dbg_stmt);
123 switch (statement.tag) {125 switch (statement.tag) {
124 .VarDecl => {126 .VarDecl => {
125 const var_decl_node = statement.castTag(.VarDecl).?;127 const var_decl_node = statement.castTag(.VarDecl).?;
...@@ -146,7 +148,6 @@ pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block...@@ -146,7 +148,6 @@ pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block
146 else => {148 else => {
147 const possibly_unused_result = try expr(mod, scope, .none, statement);149 const possibly_unused_result = try expr(mod, scope, .none, statement);
148 if (!possibly_unused_result.tag.isNoReturn()) {150 if (!possibly_unused_result.tag.isNoReturn()) {
149 const src = scope.tree().token_locs[statement.firstToken()].start;
150 _ = try addZIRUnOp(mod, scope, src, .ensure_result_used, possibly_unused_result);151 _ = try addZIRUnOp(mod, scope, src, .ensure_result_used, possibly_unused_result);
151 }152 }
152 },153 },
src-self-hosted/codegen.zig+141-62
...@@ -12,6 +12,11 @@ const ErrorMsg = Module.ErrorMsg;...@@ -12,6 +12,11 @@ const ErrorMsg = Module.ErrorMsg;
12const Target = std.Target;12const Target = std.Target;
13const Allocator = mem.Allocator;13const Allocator = mem.Allocator;
14const trace = @import("tracy.zig").trace;14const trace = @import("tracy.zig").trace;
15const DW = std.dwarf;
16const leb128 = std.debug.leb;
17
18// TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented.
19// zig fmt: off
1520
16/// The codegen-related data that is stored in `ir.Inst.Block` instructions.21/// The codegen-related data that is stored in `ir.Inst.Block` instructions.
17pub const BlockData = struct {22pub const BlockData = struct {
...@@ -44,6 +49,7 @@ pub fn generateSymbol(...@@ -44,6 +49,7 @@ pub fn generateSymbol(
44 src: usize,49 src: usize,
45 typed_value: TypedValue,50 typed_value: TypedValue,
46 code: *std.ArrayList(u8),51 code: *std.ArrayList(u8),
52 dbg_line: *std.ArrayList(u8),
47) GenerateSymbolError!Result {53) GenerateSymbolError!Result {
48 const tracy = trace(@src());54 const tracy = trace(@src());
49 defer tracy.end();55 defer tracy.end();
...@@ -51,57 +57,57 @@ pub fn generateSymbol(...@@ -51,57 +57,57 @@ pub fn generateSymbol(
51 switch (typed_value.ty.zigTypeTag()) {57 switch (typed_value.ty.zigTypeTag()) {
52 .Fn => {58 .Fn => {
53 switch (bin_file.base.options.target.cpu.arch) {59 switch (bin_file.base.options.target.cpu.arch) {
54 //.arm => return Function(.arm).generateSymbol(bin_file, src, typed_value, code),60 //.arm => return Function(.arm).generateSymbol(bin_file, src, typed_value, code, dbg_line),
55 //.armeb => return Function(.armeb).generateSymbol(bin_file, src, typed_value, code),61 //.armeb => return Function(.armeb).generateSymbol(bin_file, src, typed_value, code, dbg_line),
56 //.aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code),62 //.aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
57 //.aarch64_be => return Function(.aarch64_be).generateSymbol(bin_file, src, typed_value, code),63 //.aarch64_be => return Function(.aarch64_be).generateSymbol(bin_file, src, typed_value, code, dbg_line),
58 //.aarch64_32 => return Function(.aarch64_32).generateSymbol(bin_file, src, typed_value, code),64 //.aarch64_32 => return Function(.aarch64_32).generateSymbol(bin_file, src, typed_value, code, dbg_line),
59 //.arc => return Function(.arc).generateSymbol(bin_file, src, typed_value, code),65 //.arc => return Function(.arc).generateSymbol(bin_file, src, typed_value, code, dbg_line),
60 //.avr => return Function(.avr).generateSymbol(bin_file, src, typed_value, code),66 //.avr => return Function(.avr).generateSymbol(bin_file, src, typed_value, code, dbg_line),
61 //.bpfel => return Function(.bpfel).generateSymbol(bin_file, src, typed_value, code),67 //.bpfel => return Function(.bpfel).generateSymbol(bin_file, src, typed_value, code, dbg_line),
62 //.bpfeb => return Function(.bpfeb).generateSymbol(bin_file, src, typed_value, code),68 //.bpfeb => return Function(.bpfeb).generateSymbol(bin_file, src, typed_value, code, dbg_line),
63 //.hexagon => return Function(.hexagon).generateSymbol(bin_file, src, typed_value, code),69 //.hexagon => return Function(.hexagon).generateSymbol(bin_file, src, typed_value, code, dbg_line),
64 //.mips => return Function(.mips).generateSymbol(bin_file, src, typed_value, code),70 //.mips => return Function(.mips).generateSymbol(bin_file, src, typed_value, code, dbg_line),
65 //.mipsel => return Function(.mipsel).generateSymbol(bin_file, src, typed_value, code),71 //.mipsel => return Function(.mipsel).generateSymbol(bin_file, src, typed_value, code, dbg_line),
66 //.mips64 => return Function(.mips64).generateSymbol(bin_file, src, typed_value, code),72 //.mips64 => return Function(.mips64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
67 //.mips64el => return Function(.mips64el).generateSymbol(bin_file, src, typed_value, code),73 //.mips64el => return Function(.mips64el).generateSymbol(bin_file, src, typed_value, code, dbg_line),
68 //.msp430 => return Function(.msp430).generateSymbol(bin_file, src, typed_value, code),74 //.msp430 => return Function(.msp430).generateSymbol(bin_file, src, typed_value, code, dbg_line),
69 //.powerpc => return Function(.powerpc).generateSymbol(bin_file, src, typed_value, code),75 //.powerpc => return Function(.powerpc).generateSymbol(bin_file, src, typed_value, code, dbg_line),
70 //.powerpc64 => return Function(.powerpc64).generateSymbol(bin_file, src, typed_value, code),76 //.powerpc64 => return Function(.powerpc64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
71 //.powerpc64le => return Function(.powerpc64le).generateSymbol(bin_file, src, typed_value, code),77 //.powerpc64le => return Function(.powerpc64le).generateSymbol(bin_file, src, typed_value, code, dbg_line),
72 //.r600 => return Function(.r600).generateSymbol(bin_file, src, typed_value, code),78 //.r600 => return Function(.r600).generateSymbol(bin_file, src, typed_value, code, dbg_line),
73 //.amdgcn => return Function(.amdgcn).generateSymbol(bin_file, src, typed_value, code),79 //.amdgcn => return Function(.amdgcn).generateSymbol(bin_file, src, typed_value, code, dbg_line),
74 //.riscv32 => return Function(.riscv32).generateSymbol(bin_file, src, typed_value, code),80 //.riscv32 => return Function(.riscv32).generateSymbol(bin_file, src, typed_value, code, dbg_line),
75 //.riscv64 => return Function(.riscv64).generateSymbol(bin_file, src, typed_value, code),81 //.riscv64 => return Function(.riscv64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
76 //.sparc => return Function(.sparc).generateSymbol(bin_file, src, typed_value, code),82 //.sparc => return Function(.sparc).generateSymbol(bin_file, src, typed_value, code, dbg_line),
77 //.sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code),83 //.sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code, dbg_line),
78 //.sparcel => return Function(.sparcel).generateSymbol(bin_file, src, typed_value, code),84 //.sparcel => return Function(.sparcel).generateSymbol(bin_file, src, typed_value, code, dbg_line),
79 //.s390x => return Function(.s390x).generateSymbol(bin_file, src, typed_value, code),85 //.s390x => return Function(.s390x).generateSymbol(bin_file, src, typed_value, code, dbg_line),
80 //.tce => return Function(.tce).generateSymbol(bin_file, src, typed_value, code),86 //.tce => return Function(.tce).generateSymbol(bin_file, src, typed_value, code, dbg_line),
81 //.tcele => return Function(.tcele).generateSymbol(bin_file, src, typed_value, code),87 //.tcele => return Function(.tcele).generateSymbol(bin_file, src, typed_value, code, dbg_line),
82 //.thumb => return Function(.thumb).generateSymbol(bin_file, src, typed_value, code),88 //.thumb => return Function(.thumb).generateSymbol(bin_file, src, typed_value, code, dbg_line),
83 //.thumbeb => return Function(.thumbeb).generateSymbol(bin_file, src, typed_value, code),89 //.thumbeb => return Function(.thumbeb).generateSymbol(bin_file, src, typed_value, code, dbg_line),
84 //.i386 => return Function(.i386).generateSymbol(bin_file, src, typed_value, code),90 //.i386 => return Function(.i386).generateSymbol(bin_file, src, typed_value, code, dbg_line),
85 .x86_64 => return Function(.x86_64).generateSymbol(bin_file, src, typed_value, code),91 .x86_64 => return Function(.x86_64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
86 //.xcore => return Function(.xcore).generateSymbol(bin_file, src, typed_value, code),92 //.xcore => return Function(.xcore).generateSymbol(bin_file, src, typed_value, code, dbg_line),
87 //.nvptx => return Function(.nvptx).generateSymbol(bin_file, src, typed_value, code),93 //.nvptx => return Function(.nvptx).generateSymbol(bin_file, src, typed_value, code, dbg_line),
88 //.nvptx64 => return Function(.nvptx64).generateSymbol(bin_file, src, typed_value, code),94 //.nvptx64 => return Function(.nvptx64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
89 //.le32 => return Function(.le32).generateSymbol(bin_file, src, typed_value, code),95 //.le32 => return Function(.le32).generateSymbol(bin_file, src, typed_value, code, dbg_line),
90 //.le64 => return Function(.le64).generateSymbol(bin_file, src, typed_value, code),96 //.le64 => return Function(.le64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
91 //.amdil => return Function(.amdil).generateSymbol(bin_file, src, typed_value, code),97 //.amdil => return Function(.amdil).generateSymbol(bin_file, src, typed_value, code, dbg_line),
92 //.amdil64 => return Function(.amdil64).generateSymbol(bin_file, src, typed_value, code),98 //.amdil64 => return Function(.amdil64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
93 //.hsail => return Function(.hsail).generateSymbol(bin_file, src, typed_value, code),99 //.hsail => return Function(.hsail).generateSymbol(bin_file, src, typed_value, code, dbg_line),
94 //.hsail64 => return Function(.hsail64).generateSymbol(bin_file, src, typed_value, code),100 //.hsail64 => return Function(.hsail64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
95 //.spir => return Function(.spir).generateSymbol(bin_file, src, typed_value, code),101 //.spir => return Function(.spir).generateSymbol(bin_file, src, typed_value, code, dbg_line),
96 //.spir64 => return Function(.spir64).generateSymbol(bin_file, src, typed_value, code),102 //.spir64 => return Function(.spir64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
97 //.kalimba => return Function(.kalimba).generateSymbol(bin_file, src, typed_value, code),103 //.kalimba => return Function(.kalimba).generateSymbol(bin_file, src, typed_value, code, dbg_line),
98 //.shave => return Function(.shave).generateSymbol(bin_file, src, typed_value, code),104 //.shave => return Function(.shave).generateSymbol(bin_file, src, typed_value, code, dbg_line),
99 //.lanai => return Function(.lanai).generateSymbol(bin_file, src, typed_value, code),105 //.lanai => return Function(.lanai).generateSymbol(bin_file, src, typed_value, code, dbg_line),
100 //.wasm32 => return Function(.wasm32).generateSymbol(bin_file, src, typed_value, code),106 //.wasm32 => return Function(.wasm32).generateSymbol(bin_file, src, typed_value, code, dbg_line),
101 //.wasm64 => return Function(.wasm64).generateSymbol(bin_file, src, typed_value, code),107 //.wasm64 => return Function(.wasm64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
102 //.renderscript32 => return Function(.renderscript32).generateSymbol(bin_file, src, typed_value, code),108 //.renderscript32 => return Function(.renderscript32).generateSymbol(bin_file, src, typed_value, code, dbg_line),
103 //.renderscript64 => return Function(.renderscript64).generateSymbol(bin_file, src, typed_value, code),109 //.renderscript64 => return Function(.renderscript64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
104 //.ve => return Function(.ve).generateSymbol(bin_file, src, typed_value, code),110 //.ve => return Function(.ve).generateSymbol(bin_file, src, typed_value, code, dbg_line),
105 else => @panic("Backend architectures that don't have good support yet are commented out, to improve compilation performance. If you are interested in one of these other backends feel free to uncomment them. Eventually these will be completed, but stage1 is slow and a memory hog."),111 else => @panic("Backend architectures that don't have good support yet are commented out, to improve compilation performance. If you are interested in one of these other backends feel free to uncomment them. Eventually these will be completed, but stage1 is slow and a memory hog."),
106 }112 }
107 },113 },
...@@ -114,7 +120,7 @@ pub fn generateSymbol(...@@ -114,7 +120,7 @@ pub fn generateSymbol(
114 switch (try generateSymbol(bin_file, src, .{120 switch (try generateSymbol(bin_file, src, .{
115 .ty = typed_value.ty.elemType(),121 .ty = typed_value.ty.elemType(),
116 .val = sentinel,122 .val = sentinel,
117 }, code)) {123 }, code, dbg_line)) {
118 .appended => return Result{ .appended = {} },124 .appended => return Result{ .appended = {} },
119 .externally_managed => |slice| {125 .externally_managed => |slice| {
120 code.appendSliceAssumeCapacity(slice);126 code.appendSliceAssumeCapacity(slice);
...@@ -206,6 +212,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -206,6 +212,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
206 target: *const std.Target,212 target: *const std.Target,
207 mod_fn: *const Module.Fn,213 mod_fn: *const Module.Fn,
208 code: *std.ArrayList(u8),214 code: *std.ArrayList(u8),
215 dbg_line: *std.ArrayList(u8),
209 err_msg: ?*ErrorMsg,216 err_msg: ?*ErrorMsg,
210 args: []MCValue,217 args: []MCValue,
211 ret_mcv: MCValue,218 ret_mcv: MCValue,
...@@ -214,6 +221,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -214,6 +221,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
214 src: usize,221 src: usize,
215 stack_align: u32,222 stack_align: u32,
216223
224 /// Byte offset within the source file.
225 prev_di_src: usize,
226 /// Relative to the beginning of `code`.
227 prev_di_pc: usize,
228 /// Used to find newlines and count line deltas.
229 source: []const u8,
230 /// Byte offset within the source file of the ending curly.
231 rbrace_src: usize,
232
217 /// The value is an offset into the `Function` `code` from the beginning.233 /// The value is an offset into the `Function` `code` from the beginning.
218 /// To perform the reloc, write 32-bit signed little-endian integer234 /// To perform the reloc, write 32-bit signed little-endian integer
219 /// which is a relative jump, based on the address following the reloc.235 /// which is a relative jump, based on the address following the reloc.
...@@ -365,6 +381,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -365,6 +381,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
365 src: usize,381 src: usize,
366 typed_value: TypedValue,382 typed_value: TypedValue,
367 code: *std.ArrayList(u8),383 code: *std.ArrayList(u8),
384 dbg_line: *std.ArrayList(u8),
368 ) GenerateSymbolError!Result {385 ) GenerateSymbolError!Result {
369 const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;386 const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;
370387
...@@ -379,12 +396,29 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -379,12 +396,29 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
379 const branch = try branch_stack.addOne();396 const branch = try branch_stack.addOne();
380 branch.* = .{};397 branch.* = .{};
381398
399 const src_data: struct {lbrace_src: usize, rbrace_src: usize, source: []const u8} = blk: {
400 if (module_fn.owner_decl.scope.cast(Module.Scope.File)) |scope_file| {
401 const tree = scope_file.contents.tree;
402 const fn_proto = tree.root_node.decls()[module_fn.owner_decl.src_index].castTag(.FnProto).?;
403 const block = fn_proto.body().?.castTag(.Block).?;
404 const lbrace_src = tree.token_locs[block.lbrace].start;
405 const rbrace_src = tree.token_locs[block.rbrace].start;
406 break :blk .{ .lbrace_src = lbrace_src, .rbrace_src = rbrace_src, .source = tree.source };
407 } else if (module_fn.owner_decl.scope.cast(Module.Scope.ZIRModule)) |zir_module| {
408 const byte_off = zir_module.contents.module.decls[module_fn.owner_decl.src_index].inst.src;
409 break :blk .{ .lbrace_src = byte_off, .rbrace_src = byte_off, .source = zir_module.source.bytes };
410 } else {
411 unreachable;
412 }
413 };
414
382 var function = Self{415 var function = Self{
383 .gpa = bin_file.allocator,416 .gpa = bin_file.allocator,
384 .target = &bin_file.base.options.target,417 .target = &bin_file.base.options.target,
385 .bin_file = bin_file,418 .bin_file = bin_file,
386 .mod_fn = module_fn,419 .mod_fn = module_fn,
387 .code = code,420 .code = code,
421 .dbg_line = dbg_line,
388 .err_msg = null,422 .err_msg = null,
389 .args = undefined, // populated after `resolveCallingConventionValues`423 .args = undefined, // populated after `resolveCallingConventionValues`
390 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`424 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`
...@@ -393,6 +427,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -393,6 +427,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
393 .branch_stack = &branch_stack,427 .branch_stack = &branch_stack,
394 .src = src,428 .src = src,
395 .stack_align = undefined,429 .stack_align = undefined,
430 .prev_di_pc = 0,
431 .prev_di_src = src_data.lbrace_src,
432 .rbrace_src = src_data.rbrace_src,
433 .source = src_data.source,
396 };434 };
397 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);435 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
398436
...@@ -431,20 +469,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -431,20 +469,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
431 // TODO During semantic analysis, check if there are no function calls. If there469 // TODO During semantic analysis, check if there are no function calls. If there
432 // are none, here we can omit the part where we subtract and then add rsp.470 // are none, here we can omit the part where we subtract and then add rsp.
433 self.code.appendSliceAssumeCapacity(&[_]u8{471 self.code.appendSliceAssumeCapacity(&[_]u8{
434 // push rbp472 0x55, // push rbp
435 0x55,473 0x48, 0x89, 0xe5, // mov rbp, rsp
436 // mov rbp, rsp474 0x48, 0x81, 0xec, // sub rsp, imm32 (with reloc)
437 0x48,
438 0x89,
439 0xe5,
440 // sub rsp, imm32 (with reloc)
441 0x48,
442 0x81,
443 0xec,
444 });475 });
445 const reloc_index = self.code.items.len;476 const reloc_index = self.code.items.len;
446 self.code.items.len += 4;477 self.code.items.len += 4;
447478
479 try self.dbgSetPrologueEnd();
448 try self.genBody(self.mod_fn.analysis.success);480 try self.genBody(self.mod_fn.analysis.success);
449481
450 const stack_end = self.branch_stack.items[0].max_end_stack;482 const stack_end = self.branch_stack.items[0].max_end_stack;
...@@ -467,6 +499,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -467,6 +499,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
467 mem.writeIntLittle(i32, self.code.items[jmp_reloc..][0..4], s32_amt);499 mem.writeIntLittle(i32, self.code.items[jmp_reloc..][0..4], s32_amt);
468 }500 }
469501
502 // Important to be after the possible self.code.items.len -= 5 above.
503 try self.dbgSetEpilogueBegin();
504
470 try self.code.ensureCapacity(self.code.items.len + 9);505 try self.code.ensureCapacity(self.code.items.len + 9);
471 // add rsp, x506 // add rsp, x
472 if (aligned_stack_end > math.maxInt(i8)) {507 if (aligned_stack_end > math.maxInt(i8)) {
...@@ -485,13 +520,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -485,13 +520,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
485 0xc3, // ret520 0xc3, // ret
486 });521 });
487 } else {522 } else {
523 try self.dbgSetPrologueEnd();
488 try self.genBody(self.mod_fn.analysis.success);524 try self.genBody(self.mod_fn.analysis.success);
525 try self.dbgSetEpilogueBegin();
489 }526 }
490 },527 },
491 else => {528 else => {
529 try self.dbgSetPrologueEnd();
492 try self.genBody(self.mod_fn.analysis.success);530 try self.genBody(self.mod_fn.analysis.success);
531 try self.dbgSetEpilogueBegin();
493 },532 },
494 }533 }
534 // Drop them off at the rbrace.
535 try self.dbgAdvancePCAndLine(self.rbrace_src);
495 }536 }
496537
497 fn genBody(self: *Self, body: ir.Body) InnerError!void {538 fn genBody(self: *Self, body: ir.Body) InnerError!void {
...@@ -508,6 +549,38 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -508,6 +549,38 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
508 }549 }
509 }550 }
510551
552 fn dbgSetPrologueEnd(self: *Self) InnerError!void {
553 try self.dbg_line.append(DW.LNS_set_prologue_end);
554 try self.dbgAdvancePCAndLine(self.prev_di_src);
555 }
556
557 fn dbgSetEpilogueBegin(self: *Self) InnerError!void {
558 try self.dbg_line.append(DW.LNS_set_epilogue_begin);
559 try self.dbgAdvancePCAndLine(self.prev_di_src);
560 }
561
562 fn dbgAdvancePCAndLine(self: *Self, src: usize) InnerError!void {
563 // TODO Look into improving the performance here by adding a token-index-to-line
564 // lookup table, and changing ir.Inst from storing byte offset to token. Currently
565 // this involves scanning over the source code for newlines
566 // (but only from the previous byte offset to the new one).
567 const delta_line = std.zig.lineDelta(self.source, self.prev_di_src, src);
568 const delta_pc = self.code.items.len - self.prev_di_pc;
569 self.prev_di_src = src;
570 self.prev_di_pc = self.code.items.len;
571 // TODO Look into using the DWARF special opcodes to compress this data. It lets you emit
572 // single-byte opcodes that add different numbers to both the PC and the line number
573 // at the same time.
574 try self.dbg_line.ensureCapacity(self.dbg_line.items.len + 11);
575 self.dbg_line.appendAssumeCapacity(DW.LNS_advance_pc);
576 leb128.writeULEB128(self.dbg_line.writer(), delta_pc) catch unreachable;
577 if (delta_line != 0) {
578 self.dbg_line.appendAssumeCapacity(DW.LNS_advance_line);
579 leb128.writeILEB128(self.dbg_line.writer(), delta_line) catch unreachable;
580 }
581 self.dbg_line.appendAssumeCapacity(DW.LNS_copy);
582 }
583
511 fn processDeath(self: *Self, inst: *ir.Inst) void {584 fn processDeath(self: *Self, inst: *ir.Inst) void {
512 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];585 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
513 const entry = branch.inst_table.getEntry(inst) orelse return;586 const entry = branch.inst_table.getEntry(inst) orelse return;
...@@ -543,6 +616,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -543,6 +616,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
543 .cmp_neq => return self.genCmp(inst.castTag(.cmp_neq).?, .neq),616 .cmp_neq => return self.genCmp(inst.castTag(.cmp_neq).?, .neq),
544 .condbr => return self.genCondBr(inst.castTag(.condbr).?),617 .condbr => return self.genCondBr(inst.castTag(.condbr).?),
545 .constant => unreachable, // excluded from function bodies618 .constant => unreachable, // excluded from function bodies
619 .dbg_stmt => return self.genDbgStmt(inst.castTag(.dbg_stmt).?),
546 .floatcast => return self.genFloatCast(inst.castTag(.floatcast).?),620 .floatcast => return self.genFloatCast(inst.castTag(.floatcast).?),
547 .intcast => return self.genIntCast(inst.castTag(.intcast).?),621 .intcast => return self.genIntCast(inst.castTag(.intcast).?),
548 .isnonnull => return self.genIsNonNull(inst.castTag(.isnonnull).?),622 .isnonnull => return self.genIsNonNull(inst.castTag(.isnonnull).?),
...@@ -1106,6 +1180,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1106,6 +1180,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1106 }1180 }
1107 }1181 }
11081182
1183 fn genDbgStmt(self: *Self, inst: *ir.Inst.NoOp) !MCValue {
1184 try self.dbgAdvancePCAndLine(inst.base.src);
1185 return MCValue.none;
1186 }
1187
1109 fn genCondBr(self: *Self, inst: *ir.Inst.CondBr) !MCValue {1188 fn genCondBr(self: *Self, inst: *ir.Inst.CondBr) !MCValue {
1110 switch (arch) {1189 switch (arch) {
1111 .x86_64 => {1190 .x86_64 => {
src-self-hosted/codegen/c.zig+11-5
...@@ -89,17 +89,17 @@ fn genFn(file: *C, decl: *Decl) !void {...@@ -89,17 +89,17 @@ fn genFn(file: *C, decl: *Decl) !void {
89 const func: *Module.Fn = tv.val.cast(Value.Payload.Function).?.func;89 const func: *Module.Fn = tv.val.cast(Value.Payload.Function).?.func;
90 const instructions = func.analysis.success.instructions;90 const instructions = func.analysis.success.instructions;
91 if (instructions.len > 0) {91 if (instructions.len > 0) {
92 try writer.writeAll("\n");
92 for (instructions) |inst| {93 for (instructions) |inst| {
93 try writer.writeAll("\n ");
94 switch (inst.tag) {94 switch (inst.tag) {
95 .assembly => try genAsm(file, inst.castTag(.assembly).?, decl),95 .assembly => try genAsm(file, inst.castTag(.assembly).?, decl),
96 .call => try genCall(file, inst.castTag(.call).?, decl),96 .call => try genCall(file, inst.castTag(.call).?, decl),
97 .ret => try genRet(file, inst.castTag(.ret).?, decl, tv.ty.fnReturnType()),97 .ret => try genRet(file, inst.castTag(.ret).?, decl, tv.ty.fnReturnType()),
98 .retvoid => try file.main.writer().print("return;", .{}),98 .retvoid => try file.main.writer().print(" return;\n", .{}),
99 .dbg_stmt => try genDbgStmt(file, inst.castTag(.dbg_stmt).?, decl),
99 else => |e| return file.fail(decl.src(), "TODO implement C codegen for {}", .{e}),100 else => |e| return file.fail(decl.src(), "TODO implement C codegen for {}", .{e}),
100 }101 }
101 }102 }
102 try writer.writeAll("\n");
103 }103 }
104104
105 try writer.writeAll("}\n\n");105 try writer.writeAll("}\n\n");
...@@ -112,6 +112,7 @@ fn genRet(file: *C, inst: *Inst.UnOp, decl: *Decl, expected_return_type: Type) !...@@ -112,6 +112,7 @@ fn genRet(file: *C, inst: *Inst.UnOp, decl: *Decl, expected_return_type: Type) !
112fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {112fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {
113 const writer = file.main.writer();113 const writer = file.main.writer();
114 const header = file.header.writer();114 const header = file.header.writer();
115 try writer.writeAll(" ");
115 if (inst.func.castTag(.constant)) |func_inst| {116 if (inst.func.castTag(.constant)) |func_inst| {
116 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {117 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
117 const target = func_val.func.owner_decl;118 const target = func_val.func.owner_decl;
...@@ -126,7 +127,7 @@ fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {...@@ -126,7 +127,7 @@ fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {
126 try renderFunctionSignature(file, header, target);127 try renderFunctionSignature(file, header, target);
127 try header.writeAll(";\n");128 try header.writeAll(";\n");
128 }129 }
129 try writer.print("{}();", .{tname});130 try writer.print("{}();\n", .{tname});
130 } else {131 } else {
131 return file.fail(decl.src(), "TODO non-function call target?", .{});132 return file.fail(decl.src(), "TODO non-function call target?", .{});
132 }133 }
...@@ -138,8 +139,13 @@ fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {...@@ -138,8 +139,13 @@ fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {
138 }139 }
139}140}
140141
142fn genDbgStmt(file: *C, inst: *Inst.NoOp, decl: *Decl) !void {
143 // TODO emit #line directive here with line number and filename
144}
145
141fn genAsm(file: *C, as: *Inst.Assembly, decl: *Decl) !void {146fn genAsm(file: *C, as: *Inst.Assembly, decl: *Decl) !void {
142 const writer = file.main.writer();147 const writer = file.main.writer();
148 try writer.writeAll(" ");
143 for (as.inputs) |i, index| {149 for (as.inputs) |i, index| {
144 if (i[0] == '{' and i[i.len - 1] == '}') {150 if (i[0] == '{' and i[i.len - 1] == '}') {
145 const reg = i[1 .. i.len - 1];151 const reg = i[1 .. i.len - 1];
...@@ -187,5 +193,5 @@ fn genAsm(file: *C, as: *Inst.Assembly, decl: *Decl) !void {...@@ -187,5 +193,5 @@ fn genAsm(file: *C, as: *Inst.Assembly, decl: *Decl) !void {
187 }193 }
188 }194 }
189 }195 }
190 try writer.writeAll(");");196 try writer.writeAll(");\n");
191}197}
src-self-hosted/ir.zig+2
...@@ -65,6 +65,7 @@ pub const Inst = struct {...@@ -65,6 +65,7 @@ pub const Inst = struct {
65 cmp_neq,65 cmp_neq,
66 condbr,66 condbr,
67 constant,67 constant,
68 dbg_stmt,
68 isnonnull,69 isnonnull,
69 isnull,70 isnull,
70 /// Read a value from a pointer.71 /// Read a value from a pointer.
...@@ -88,6 +89,7 @@ pub const Inst = struct {...@@ -88,6 +89,7 @@ pub const Inst = struct {
88 .unreach,89 .unreach,
89 .arg,90 .arg,
90 .breakpoint,91 .breakpoint,
92 .dbg_stmt,
91 => NoOp,93 => NoOp,
9294
93 .ref,95 .ref,
src-self-hosted/link.zig+521-66
...@@ -11,6 +11,9 @@ const c_codegen = @import("codegen/c.zig");...@@ -11,6 +11,9 @@ const c_codegen = @import("codegen/c.zig");
11const log = std.log;11const log = std.log;
12const DW = std.dwarf;12const DW = std.dwarf;
13const trace = @import("tracy.zig").trace;13const trace = @import("tracy.zig").trace;
14const leb128 = std.debug.leb;
15const Package = @import("Package.zig");
16const Value = @import("value.zig").Value;
1417
15// TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented.18// TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented.
16// zig fmt: off19// zig fmt: off
...@@ -24,7 +27,7 @@ pub const Options = struct {...@@ -24,7 +27,7 @@ pub const Options = struct {
24 object_format: std.builtin.ObjectFormat,27 object_format: std.builtin.ObjectFormat,
25 optimize_mode: std.builtin.Mode,28 optimize_mode: std.builtin.Mode,
26 root_name: []const u8,29 root_name: []const u8,
27 root_src_dir_path: []const u8,30 root_pkg: *const Package,
28 /// Used for calculating how much space to reserve for symbols in case the binary file31 /// Used for calculating how much space to reserve for symbols in case the binary file
29 /// does not already have a symbol table.32 /// does not already have a symbol table.
30 symbol_count_hint: u64 = 32,33 symbol_count_hint: u64 = 32,
...@@ -82,6 +85,13 @@ pub const File = struct {...@@ -82,6 +85,13 @@ pub const File = struct {
82 }85 }
83 }86 }
8487
88 pub fn updateDeclLineNumber(base: *File, module: *Module, decl: *Module.Decl) !void {
89 switch (base.tag) {
90 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl),
91 .c => {},
92 }
93 }
94
85 pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void {95 pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void {
86 switch (base.tag) {96 switch (base.tag) {
87 .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),97 .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),
...@@ -200,7 +210,7 @@ pub const File = struct {...@@ -200,7 +210,7 @@ pub const File = struct {
200210
201 pub fn fail(self: *C, src: usize, comptime format: []const u8, args: anytype) !void {211 pub fn fail(self: *C, src: usize, comptime format: []const u8, args: anytype) !void {
202 self.error_msg = try Module.ErrorMsg.create(self.allocator, src, format, args);212 self.error_msg = try Module.ErrorMsg.create(self.allocator, src, format, args);
203 return error.CGenFailure;213 return error.AnalysisFail;
204 }214 }
205215
206 pub fn deinit(self: *File.C) void {216 pub fn deinit(self: *File.C) void {
...@@ -214,7 +224,7 @@ pub const File = struct {...@@ -214,7 +224,7 @@ pub const File = struct {
214224
215 pub fn updateDecl(self: *File.C, module: *Module, decl: *Module.Decl) !void {225 pub fn updateDecl(self: *File.C, module: *Module, decl: *Module.Decl) !void {
216 c_codegen.generate(self, decl) catch |err| {226 c_codegen.generate(self, decl) catch |err| {
217 if (err == error.CGenFailure) {227 if (err == error.AnalysisFail) {
218 try module.failed_decls.put(module.gpa, decl, self.error_msg);228 try module.failed_decls.put(module.gpa, decl, self.error_msg);
219 }229 }
220 return err;230 return err;
...@@ -291,6 +301,7 @@ pub const File = struct {...@@ -291,6 +301,7 @@ pub const File = struct {
291 debug_abbrev_section_index: ?u16 = null,301 debug_abbrev_section_index: ?u16 = null,
292 debug_str_section_index: ?u16 = null,302 debug_str_section_index: ?u16 = null,
293 debug_aranges_section_index: ?u16 = null,303 debug_aranges_section_index: ?u16 = null,
304 debug_line_section_index: ?u16 = null,
294305
295 debug_abbrev_table_offset: ?u64 = null,306 debug_abbrev_table_offset: ?u64 = null,
296307
...@@ -318,6 +329,7 @@ pub const File = struct {...@@ -318,6 +329,7 @@ pub const File = struct {
318 debug_info_section_dirty: bool = false,329 debug_info_section_dirty: bool = false,
319 debug_abbrev_section_dirty: bool = false,330 debug_abbrev_section_dirty: bool = false,
320 debug_aranges_section_dirty: bool = false,331 debug_aranges_section_dirty: bool = false,
332 debug_line_header_dirty: bool = false,
321333
322 error_flags: ErrorFlags = ErrorFlags{},334 error_flags: ErrorFlags = ErrorFlags{},
323335
...@@ -339,6 +351,12 @@ pub const File = struct {...@@ -339,6 +351,12 @@ pub const File = struct {
339 text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = std.ArrayListUnmanaged(*TextBlock){},351 text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = std.ArrayListUnmanaged(*TextBlock){},
340 last_text_block: ?*TextBlock = null,352 last_text_block: ?*TextBlock = null,
341353
354 /// A list of `SrcFn` whose Line Number Programs have surplus capacity.
355 /// This is the same concept as `text_block_free_list`; see those doc comments.
356 dbg_line_fn_free_list: std.AutoHashMapUnmanaged(*SrcFn, void) = .{},
357 dbg_line_fn_first: ?*SrcFn = null,
358 dbg_line_fn_last: ?*SrcFn = null,
359
342 /// `alloc_num / alloc_den` is the factor of padding when allocating.360 /// `alloc_num / alloc_den` is the factor of padding when allocating.
343 const alloc_num = 4;361 const alloc_num = 4;
344 const alloc_den = 3;362 const alloc_den = 3;
...@@ -402,6 +420,26 @@ pub const File = struct {...@@ -402,6 +420,26 @@ pub const File = struct {
402 sym_index: ?u32 = null,420 sym_index: ?u32 = null,
403 };421 };
404422
423 pub const SrcFn = struct {
424 /// Offset from the beginning of the Debug Line Program header that contains this function.
425 off: u32,
426 /// Size of the line number program component belonging to this function, not
427 /// including padding.
428 len: u32,
429
430 /// Points to the previous and next neighbors, based on the offset from .debug_line.
431 /// This can be used to find, for example, the capacity of this `SrcFn`.
432 prev: ?*SrcFn,
433 next: ?*SrcFn,
434
435 pub const empty: SrcFn = .{
436 .off = 0,
437 .len = 0,
438 .prev = null,
439 .next = null,
440 };
441 };
442
405 pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: Options) !*File {443 pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: Options) !*File {
406 assert(options.object_format == .elf);444 assert(options.object_format == .elf);
407445
...@@ -514,6 +552,7 @@ pub const File = struct {...@@ -514,6 +552,7 @@ pub const File = struct {
514 self.local_symbol_free_list.deinit(self.allocator);552 self.local_symbol_free_list.deinit(self.allocator);
515 self.offset_table_free_list.deinit(self.allocator);553 self.offset_table_free_list.deinit(self.allocator);
516 self.text_block_free_list.deinit(self.allocator);554 self.text_block_free_list.deinit(self.allocator);
555 self.dbg_line_fn_free_list.deinit(self.allocator);
517 self.offset_table.deinit(self.allocator);556 self.offset_table.deinit(self.allocator);
518 if (self.owns_file_handle) {557 if (self.owns_file_handle) {
519 if (self.file) |f| f.close();558 if (self.file) |f| f.close();
...@@ -538,6 +577,14 @@ pub const File = struct {...@@ -538,6 +577,14 @@ pub const File = struct {
538 });577 });
539 }578 }
540579
580 fn getDebugLineProgramOff(self: Elf) u32 {
581 return self.dbg_line_fn_first.?.off;
582 }
583
584 fn getDebugLineProgramEnd(self: Elf) u32 {
585 return self.dbg_line_fn_last.?.off + self.dbg_line_fn_last.?.len;
586 }
587
541 /// Returns end pos of collision, if any.588 /// Returns end pos of collision, if any.
542 fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {589 fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
543 const small_ptr = self.base.options.target.cpu.arch.ptrBitWidth() == 32;590 const small_ptr = self.base.options.target.cpu.arch.ptrBitWidth() == 32;
...@@ -585,6 +632,8 @@ pub const File = struct {...@@ -585,6 +632,8 @@ pub const File = struct {
585 }632 }
586633
587 fn allocatedSize(self: *Elf, start: u64) u64 {634 fn allocatedSize(self: *Elf, start: u64) u64 {
635 if (start == 0)
636 return 0;
588 var min_pos: u64 = std.math.maxInt(u64);637 var min_pos: u64 = std.math.maxInt(u64);
589 if (self.shdr_table_offset) |off| {638 if (self.shdr_table_offset) |off| {
590 if (off > start and off < min_pos) min_pos = off;639 if (off > start and off < min_pos) min_pos = off;
...@@ -611,6 +660,7 @@ pub const File = struct {...@@ -611,6 +660,7 @@ pub const File = struct {
611 return start;660 return start;
612 }661 }
613662
663 /// TODO Improve this to use a table.
614 fn makeString(self: *Elf, bytes: []const u8) !u32 {664 fn makeString(self: *Elf, bytes: []const u8) !u32 {
615 try self.shstrtab.ensureCapacity(self.allocator, self.shstrtab.items.len + bytes.len + 1);665 try self.shstrtab.ensureCapacity(self.allocator, self.shstrtab.items.len + bytes.len + 1);
616 const result = self.shstrtab.items.len;666 const result = self.shstrtab.items.len;
...@@ -619,6 +669,7 @@ pub const File = struct {...@@ -619,6 +669,7 @@ pub const File = struct {
619 return @intCast(u32, result);669 return @intCast(u32, result);
620 }670 }
621671
672 /// TODO Improve this to use a table.
622 fn makeDebugString(self: *Elf, bytes: []const u8) !u32 {673 fn makeDebugString(self: *Elf, bytes: []const u8) !u32 {
623 try self.debug_strtab.ensureCapacity(self.allocator, self.debug_strtab.items.len + bytes.len + 1);674 try self.debug_strtab.ensureCapacity(self.allocator, self.debug_strtab.items.len + bytes.len + 1);
624 const result = self.debug_strtab.items.len;675 const result = self.debug_strtab.items.len;
...@@ -645,10 +696,7 @@ pub const File = struct {...@@ -645,10 +696,7 @@ pub const File = struct {
645 .p32 => true,696 .p32 => true,
646 .p64 => false,697 .p64 => false,
647 };698 };
648 const ptr_size: u8 = switch (self.ptr_width) {699 const ptr_size: u8 = self.ptrWidthBytes();
649 .p32 => 4,
650 .p64 => 8,
651 };
652 if (self.phdr_load_re_index == null) {700 if (self.phdr_load_re_index == null) {
653 self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len);701 self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len);
654 const file_size = self.base.options.program_code_size_hint;702 const file_size = self.base.options.program_code_size_hint;
...@@ -713,27 +761,6 @@ pub const File = struct {...@@ -713,27 +761,6 @@ pub const File = struct {
713 self.shstrtab_dirty = true;761 self.shstrtab_dirty = true;
714 self.shdr_table_dirty = true;762 self.shdr_table_dirty = true;
715 }763 }
716 if (self.debug_str_section_index == null) {
717 self.debug_str_section_index = @intCast(u16, self.sections.items.len);
718 assert(self.debug_strtab.items.len == 0);
719 try self.debug_strtab.append(self.allocator, 0); // need a 0 at position 0
720 const off = self.findFreeSpace(self.debug_strtab.items.len, 1);
721 log.debug(.link, "found debug_strtab free space 0x{x} to 0x{x}\n", .{ off, off + self.debug_strtab.items.len });
722 try self.sections.append(self.allocator, .{
723 .sh_name = try self.makeString(".debug_str"),
724 .sh_type = elf.SHT_PROGBITS,
725 .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS,
726 .sh_addr = 0,
727 .sh_offset = off,
728 .sh_size = self.debug_strtab.items.len,
729 .sh_link = 0,
730 .sh_info = 0,
731 .sh_addralign = 1,
732 .sh_entsize = 1,
733 });
734 self.debug_strtab_dirty = true;
735 self.shdr_table_dirty = true;
736 }
737 if (self.text_section_index == null) {764 if (self.text_section_index == null) {
738 self.text_section_index = @intCast(u16, self.sections.items.len);765 self.text_section_index = @intCast(u16, self.sections.items.len);
739 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];766 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
...@@ -794,6 +821,24 @@ pub const File = struct {...@@ -794,6 +821,24 @@ pub const File = struct {
794 self.shdr_table_dirty = true;821 self.shdr_table_dirty = true;
795 try self.writeSymbol(0);822 try self.writeSymbol(0);
796 }823 }
824 if (self.debug_str_section_index == null) {
825 self.debug_str_section_index = @intCast(u16, self.sections.items.len);
826 assert(self.debug_strtab.items.len == 0);
827 try self.sections.append(self.allocator, .{
828 .sh_name = try self.makeString(".debug_str"),
829 .sh_type = elf.SHT_PROGBITS,
830 .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS,
831 .sh_addr = 0,
832 .sh_offset = 0,
833 .sh_size = self.debug_strtab.items.len,
834 .sh_link = 0,
835 .sh_info = 0,
836 .sh_addralign = 1,
837 .sh_entsize = 1,
838 });
839 self.debug_strtab_dirty = true;
840 self.shdr_table_dirty = true;
841 }
797 if (self.debug_info_section_index == null) {842 if (self.debug_info_section_index == null) {
798 self.debug_info_section_index = @intCast(u16, self.sections.items.len);843 self.debug_info_section_index = @intCast(u16, self.sections.items.len);
799844
...@@ -869,6 +914,31 @@ pub const File = struct {...@@ -869,6 +914,31 @@ pub const File = struct {
869 self.shdr_table_dirty = true;914 self.shdr_table_dirty = true;
870 self.debug_aranges_section_dirty = true;915 self.debug_aranges_section_dirty = true;
871 }916 }
917 if (self.debug_line_section_index == null) {
918 self.debug_line_section_index = @intCast(u16, self.sections.items.len);
919
920 const file_size_hint = 250;
921 const p_align = 1;
922 const off = self.findFreeSpace(file_size_hint, p_align);
923 log.debug(.link, "found .debug_line free space 0x{x} to 0x{x}\n", .{
924 off,
925 off + file_size_hint,
926 });
927 try self.sections.append(self.allocator, .{
928 .sh_name = try self.makeString(".debug_line"),
929 .sh_type = elf.SHT_PROGBITS,
930 .sh_flags = 0,
931 .sh_addr = 0,
932 .sh_offset = off,
933 .sh_size = file_size_hint,
934 .sh_link = 0,
935 .sh_info = 0,
936 .sh_addralign = p_align,
937 .sh_entsize = 0,
938 });
939 self.shdr_table_dirty = true;
940 self.debug_line_header_dirty = true;
941 }
872 const shsize: u64 = switch (self.ptr_width) {942 const shsize: u64 = switch (self.ptr_width) {
873 .p32 => @sizeOf(elf.Elf32_Shdr),943 .p32 => @sizeOf(elf.Elf32_Shdr),
874 .p64 => @sizeOf(elf.Elf64_Shdr),944 .p64 => @sizeOf(elf.Elf64_Shdr),
...@@ -906,9 +976,10 @@ pub const File = struct {...@@ -906,9 +976,10 @@ pub const File = struct {
906 pub fn flush(self: *Elf) !void {976 pub fn flush(self: *Elf) !void {
907 const target_endian = self.base.options.target.cpu.arch.endian();977 const target_endian = self.base.options.target.cpu.arch.endian();
908 const foreign_endian = target_endian != std.Target.current.cpu.arch.endian();978 const foreign_endian = target_endian != std.Target.current.cpu.arch.endian();
909 const ptr_width_bytes: u8 = switch (self.ptr_width) {979 const ptr_width_bytes: u8 = self.ptrWidthBytes();
980 const init_len_size: usize = switch (self.ptr_width) {
910 .p32 => 4,981 .p32 => 4,
911 .p64 => 8,982 .p64 => 12,
912 };983 };
913984
914 // Unfortunately these have to be buffered and done at the end because ELF does not allow985 // Unfortunately these have to be buffered and done at the end because ELF does not allow
...@@ -922,7 +993,7 @@ pub const File = struct {...@@ -922,7 +993,7 @@ pub const File = struct {
922 // we can simply append these bytes.993 // we can simply append these bytes.
923 const abbrev_buf = [_]u8{994 const abbrev_buf = [_]u8{
924 1, DW.TAG_compile_unit, DW.CHILDREN_no, // header995 1, DW.TAG_compile_unit, DW.CHILDREN_no, // header
925 //DW.AT_stmt_list, DW.FORM_data4, TODO996 DW.AT_stmt_list, DW.FORM_sec_offset,
926 DW.AT_low_pc , DW.FORM_addr,997 DW.AT_low_pc , DW.FORM_addr,
927 DW.AT_high_pc , DW.FORM_addr,998 DW.AT_high_pc , DW.FORM_addr,
928 DW.AT_name , DW.FORM_strp,999 DW.AT_name , DW.FORM_strp,
...@@ -969,27 +1040,23 @@ pub const File = struct {...@@ -969,27 +1040,23 @@ pub const File = struct {
969 // not including the initial length itself.1040 // not including the initial length itself.
970 // We have to come back and write it later after we know the size.1041 // We have to come back and write it later after we know the size.
971 const init_len_index = di_buf.items.len;1042 const init_len_index = di_buf.items.len;
972 switch (self.ptr_width) {1043 di_buf.items.len += init_len_size;
973 .p32 => di_buf.items.len += 4,
974 .p64 => di_buf.items.len += 12,
975 }
976 const after_init_len = di_buf.items.len;1044 const after_init_len = di_buf.items.len;
977 mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 5, target_endian); // DWARF version1045 mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 4, target_endian); // DWARF version
978 di_buf.appendAssumeCapacity(DW.UT_compile);
979 const abbrev_offset = self.debug_abbrev_table_offset.?;1046 const abbrev_offset = self.debug_abbrev_table_offset.?;
980 switch (self.ptr_width) {1047 switch (self.ptr_width) {
981 .p32 => {1048 .p32 => {
982 di_buf.appendAssumeCapacity(4); // address size
983 mem.writeInt(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, abbrev_offset), target_endian);1049 mem.writeInt(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, abbrev_offset), target_endian);
1050 di_buf.appendAssumeCapacity(4); // address size
984 },1051 },
985 .p64 => {1052 .p64 => {
986 di_buf.appendAssumeCapacity(8); // address size
987 mem.writeInt(u64, di_buf.addManyAsArrayAssumeCapacity(8), abbrev_offset, target_endian);1053 mem.writeInt(u64, di_buf.addManyAsArrayAssumeCapacity(8), abbrev_offset, target_endian);
1054 di_buf.appendAssumeCapacity(8); // address size
988 },1055 },
989 }1056 }
990 // Write the form for the compile unit, which must match the abbrev table above.1057 // Write the form for the compile unit, which must match the abbrev table above.
991 const name_strp = try self.makeDebugString(self.base.options.root_name);1058 const name_strp = try self.makeDebugString(self.base.options.root_pkg.root_src_path);
992 const comp_dir_strp = try self.makeDebugString(self.base.options.root_src_dir_path);1059 const comp_dir_strp = try self.makeDebugString(self.base.options.root_pkg.root_src_dir_path);
993 const producer_strp = try self.makeDebugString("zig (TODO version here)");1060 const producer_strp = try self.makeDebugString("zig (TODO version here)");
994 // Currently only one compilation unit is supported, so the address range is simply1061 // Currently only one compilation unit is supported, so the address range is simply
995 // identical to the main program header virtual address and memory size.1062 // identical to the main program header virtual address and memory size.
...@@ -998,7 +1065,7 @@ pub const File = struct {...@@ -998,7 +1065,7 @@ pub const File = struct {
998 const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz;1065 const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz;
9991066
1000 di_buf.appendAssumeCapacity(1); // abbrev tag, matching the value from the abbrev table header1067 di_buf.appendAssumeCapacity(1); // abbrev tag, matching the value from the abbrev table header
1001 //DW.AT_stmt_list, DW.FORM_data4, TODO line information1068 self.writeDwarfAddrAssumeCapacity(&di_buf, 0); // DW.AT_stmt_list, DW.FORM_sec_offset
1002 self.writeDwarfAddrAssumeCapacity(&di_buf, low_pc);1069 self.writeDwarfAddrAssumeCapacity(&di_buf, low_pc);
1003 self.writeDwarfAddrAssumeCapacity(&di_buf, high_pc);1070 self.writeDwarfAddrAssumeCapacity(&di_buf, high_pc);
1004 self.writeDwarfAddrAssumeCapacity(&di_buf, name_strp);1071 self.writeDwarfAddrAssumeCapacity(&di_buf, name_strp);
...@@ -1056,10 +1123,7 @@ pub const File = struct {...@@ -1056,10 +1123,7 @@ pub const File = struct {
1056 // not including the initial length itself.1123 // not including the initial length itself.
1057 // We have to come back and write it later after we know the size.1124 // We have to come back and write it later after we know the size.
1058 const init_len_index = di_buf.items.len;1125 const init_len_index = di_buf.items.len;
1059 switch (self.ptr_width) {1126 di_buf.items.len += init_len_size;
1060 .p32 => di_buf.items.len += 4,
1061 .p64 => di_buf.items.len += 12,
1062 }
1063 const after_init_len = di_buf.items.len;1127 const after_init_len = di_buf.items.len;
1064 mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 2, target_endian); // version1128 mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 2, target_endian); // version
1065 // When more than one compilation unit is supported, this will be the offset to it.1129 // When more than one compilation unit is supported, this will be the offset to it.
...@@ -1116,6 +1180,100 @@ pub const File = struct {...@@ -1116,6 +1180,100 @@ pub const File = struct {
11161180
1117 self.debug_aranges_section_dirty = false;1181 self.debug_aranges_section_dirty = false;
1118 }1182 }
1183 if (self.debug_line_header_dirty) {
1184 const dbg_line_prg_off = self.getDebugLineProgramOff();
1185 const dbg_line_prg_end = self.getDebugLineProgramEnd();
1186 assert(dbg_line_prg_end != 0);
1187
1188 const debug_line_sect = &self.sections.items[self.debug_line_section_index.?];
1189
1190 var di_buf = std.ArrayList(u8).init(self.allocator);
1191 defer di_buf.deinit();
1192
1193 // The size of this header is variable, depending on the number of directories,
1194 // files, and padding. We have a function to compute the upper bound size, however,
1195 // because it's needed for determining where to put the offset of the first `SrcFn`.
1196 try di_buf.ensureCapacity(self.dbgLineNeededHeaderBytes());
1197
1198 // initial length - length of the .debug_line contribution for this compilation unit,
1199 // not including the initial length itself.
1200 const after_init_len = di_buf.items.len + init_len_size;
1201 const init_len = dbg_line_prg_end - after_init_len;
1202 switch (self.ptr_width) {
1203 .p32 => {
1204 mem.writeInt(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, init_len), target_endian);
1205 },
1206 .p64 => {
1207 di_buf.appendNTimesAssumeCapacity(0xff, 4);
1208 mem.writeInt(u64, di_buf.addManyAsArrayAssumeCapacity(8), init_len, target_endian);
1209 },
1210 }
1211
1212 mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 4, target_endian); // version
1213
1214 // Empirically, debug info consumers do not respect this field, or otherwise
1215 // consider it to be an error when it does not point exactly to the end of the header.
1216 // Therefore we rely on the NOP jump at the beginning of the Line Number Program for
1217 // padding rather than this field.
1218 const before_header_len = di_buf.items.len;
1219 di_buf.items.len += ptr_width_bytes; // We will come back and write this.
1220 const after_header_len = di_buf.items.len;
1221
1222 const opcode_base = DW.LNS_set_isa + 1;
1223 di_buf.appendSliceAssumeCapacity(&[_]u8{
1224 1, // minimum_instruction_length
1225 1, // maximum_operations_per_instruction
1226 1, // default_is_stmt
1227 1, // line_base (signed)
1228 1, // line_range
1229 opcode_base,
1230
1231 // Standard opcode lengths. The number of items here is based on `opcode_base`.
1232 // The value is the number of LEB128 operands the instruction takes.
1233 0, // `DW.LNS_copy`
1234 1, // `DW.LNS_advance_pc`
1235 1, // `DW.LNS_advance_line`
1236 1, // `DW.LNS_set_file`
1237 1, // `DW.LNS_set_column`
1238 0, // `DW.LNS_negate_stmt`
1239 0, // `DW.LNS_set_basic_block`
1240 0, // `DW.LNS_const_add_pc`
1241 1, // `DW.LNS_fixed_advance_pc`
1242 0, // `DW.LNS_set_prologue_end`
1243 0, // `DW.LNS_set_epilogue_begin`
1244 1, // `DW.LNS_set_isa`
1245
1246 0, // include_directories (none except the compilation unit cwd)
1247 });
1248 // file_names[0]
1249 di_buf.appendSliceAssumeCapacity(self.base.options.root_pkg.root_src_path); // relative path name
1250 di_buf.appendSliceAssumeCapacity(&[_]u8{
1251 0, // null byte for the relative path name
1252 0, // directory_index
1253 0, // mtime (TODO supply this)
1254 0, // file size bytes (TODO supply this)
1255 0, // file_names sentinel
1256 });
1257
1258 const header_len = di_buf.items.len - after_header_len;
1259 switch (self.ptr_width) {
1260 .p32 => {
1261 mem.writeInt(u32, di_buf.items[before_header_len..][0..4], @intCast(u32, header_len), target_endian);
1262 },
1263 .p64 => {
1264 mem.writeInt(u64, di_buf.items[before_header_len..][0..8], header_len, target_endian);
1265 },
1266 }
1267
1268 // We use NOPs because consumers empirically do not respect the header length field.
1269 if (di_buf.items.len > dbg_line_prg_off) {
1270 // Move the first N files to the end to make more padding for the header.
1271 @panic("TODO: handle .debug_line header exceeding its padding");
1272 }
1273 const jmp_amt = dbg_line_prg_off - di_buf.items.len;
1274 try self.pwriteWithNops(0, di_buf.items, jmp_amt, debug_line_sect.sh_offset);
1275 self.debug_line_header_dirty = false;
1276 }
11191277
1120 if (self.phdr_table_dirty) {1278 if (self.phdr_table_dirty) {
1121 const phsize: u64 = switch (self.ptr_width) {1279 const phsize: u64 = switch (self.ptr_width) {
...@@ -1174,7 +1332,7 @@ pub const File = struct {...@@ -1174,7 +1332,7 @@ pub const File = struct {
1174 shstrtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);1332 shstrtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);
1175 }1333 }
1176 shstrtab_sect.sh_size = needed_size;1334 shstrtab_sect.sh_size = needed_size;
1177 log.debug(.link, "shstrtab start=0x{x} end=0x{x}\n", .{ shstrtab_sect.sh_offset, shstrtab_sect.sh_offset + needed_size });1335 log.debug(.link, "writing shstrtab start=0x{x} end=0x{x}\n", .{ shstrtab_sect.sh_offset, shstrtab_sect.sh_offset + needed_size });
11781336
1179 try self.file.?.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset);1337 try self.file.?.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset);
1180 if (!self.shdr_table_dirty) {1338 if (!self.shdr_table_dirty) {
...@@ -1263,6 +1421,7 @@ pub const File = struct {...@@ -1263,6 +1421,7 @@ pub const File = struct {
1263 assert(!self.debug_info_section_dirty);1421 assert(!self.debug_info_section_dirty);
1264 assert(!self.debug_abbrev_section_dirty);1422 assert(!self.debug_abbrev_section_dirty);
1265 assert(!self.debug_aranges_section_dirty);1423 assert(!self.debug_aranges_section_dirty);
1424 assert(!self.debug_line_header_dirty);
1266 assert(!self.phdr_table_dirty);1425 assert(!self.phdr_table_dirty);
1267 assert(!self.shdr_table_dirty);1426 assert(!self.shdr_table_dirty);
1268 assert(!self.shstrtab_dirty);1427 assert(!self.shstrtab_dirty);
...@@ -1580,11 +1739,8 @@ pub const File = struct {...@@ -1580,11 +1739,8 @@ pub const File = struct {
1580 pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void {1739 pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void {
1581 if (decl.link.local_sym_index != 0) return;1740 if (decl.link.local_sym_index != 0) return;
15821741
1583 // Here we also ensure capacity for the free lists so that they can be appended to without fail.
1584 try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1);1742 try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1);
1585 try self.local_symbol_free_list.ensureCapacity(self.allocator, self.local_symbols.items.len);
1586 try self.offset_table.ensureCapacity(self.allocator, self.offset_table.items.len + 1);1743 try self.offset_table.ensureCapacity(self.allocator, self.offset_table.items.len + 1);
1587 try self.offset_table_free_list.ensureCapacity(self.allocator, self.local_symbols.items.len);
15881744
1589 if (self.local_symbol_free_list.popOrNull()) |i| {1745 if (self.local_symbol_free_list.popOrNull()) |i| {
1590 log.debug(.link, "reusing symbol index {} for {}\n", .{ i, decl.name });1746 log.debug(.link, "reusing symbol index {} for {}\n", .{ i, decl.name });
...@@ -1617,15 +1773,37 @@ pub const File = struct {...@@ -1617,15 +1773,37 @@ pub const File = struct {
1617 }1773 }
16181774
1619 pub fn freeDecl(self: *Elf, decl: *Module.Decl) void {1775 pub fn freeDecl(self: *Elf, decl: *Module.Decl) void {
1776 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
1620 self.freeTextBlock(&decl.link);1777 self.freeTextBlock(&decl.link);
1621 if (decl.link.local_sym_index != 0) {1778 if (decl.link.local_sym_index != 0) {
1622 self.local_symbol_free_list.appendAssumeCapacity(decl.link.local_sym_index);1779 self.local_symbol_free_list.append(self.allocator, decl.link.local_sym_index) catch {};
1623 self.offset_table_free_list.appendAssumeCapacity(decl.link.offset_table_index);1780 self.offset_table_free_list.append(self.allocator, decl.link.offset_table_index) catch {};
16241781
1625 self.local_symbols.items[decl.link.local_sym_index].st_info = 0;1782 self.local_symbols.items[decl.link.local_sym_index].st_info = 0;
16261783
1627 decl.link.local_sym_index = 0;1784 decl.link.local_sym_index = 0;
1628 }1785 }
1786 // TODO make this logic match freeTextBlock. Maybe abstract the logic out since the same thing
1787 // is desired for both.
1788 _ = self.dbg_line_fn_free_list.remove(&decl.fn_link);
1789 if (decl.fn_link.prev) |prev| {
1790 _ = self.dbg_line_fn_free_list.put(self.allocator, prev, {}) catch {};
1791 prev.next = decl.fn_link.next;
1792 if (decl.fn_link.next) |next| {
1793 next.prev = prev;
1794 } else {
1795 self.dbg_line_fn_last = prev;
1796 }
1797 } else if (decl.fn_link.next) |next| {
1798 self.dbg_line_fn_first = next;
1799 next.prev = null;
1800 }
1801 if (self.dbg_line_fn_first == &decl.fn_link) {
1802 self.dbg_line_fn_first = null;
1803 }
1804 if (self.dbg_line_fn_last == &decl.fn_link) {
1805 self.dbg_line_fn_last = null;
1806 }
1629 }1807 }
16301808
1631 pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {1809 pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
...@@ -1635,8 +1813,67 @@ pub const File = struct {...@@ -1635,8 +1813,67 @@ pub const File = struct {
1635 var code_buffer = std.ArrayList(u8).init(self.allocator);1813 var code_buffer = std.ArrayList(u8).init(self.allocator);
1636 defer code_buffer.deinit();1814 defer code_buffer.deinit();
16371815
1816 var dbg_line_buffer = std.ArrayList(u8).init(self.allocator);
1817 defer dbg_line_buffer.deinit();
1818
1638 const typed_value = decl.typed_value.most_recent.typed_value;1819 const typed_value = decl.typed_value.most_recent.typed_value;
1639 const code = switch (try codegen.generateSymbol(self, decl.src(), typed_value, &code_buffer)) {1820 const is_fn: bool = switch (typed_value.ty.zigTypeTag()) {
1821 .Fn => true,
1822 else => false,
1823 };
1824 if (is_fn) {
1825 // For functions we need to add a prologue to the debug line program.
1826 try dbg_line_buffer.ensureCapacity(26);
1827
1828 const line_off: u28 = blk: {
1829 if (decl.scope.cast(Module.Scope.File)) |scope_file| {
1830 const tree = scope_file.contents.tree;
1831 const file_ast_decls = tree.root_node.decls();
1832 // TODO Look into improving the performance here by adding a token-index-to-line
1833 // lookup table. Currently this involves scanning over the source code for newlines.
1834 const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?;
1835 const block = fn_proto.body().?.castTag(.Block).?;
1836 const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start);
1837 break :blk @intCast(u28, line_delta);
1838 } else if (decl.scope.cast(Module.Scope.ZIRModule)) |zir_module| {
1839 const byte_off = zir_module.contents.module.decls[decl.src_index].inst.src;
1840 const line_delta = std.zig.lineDelta(zir_module.source.bytes, 0, byte_off);
1841 break :blk @intCast(u28, line_delta);
1842 } else {
1843 unreachable;
1844 }
1845 };
1846
1847 const ptr_width_bytes = self.ptrWidthBytes();
1848 dbg_line_buffer.appendSliceAssumeCapacity(&[_]u8{
1849 DW.LNS_extended_op,
1850 ptr_width_bytes + 1,
1851 DW.LNE_set_address,
1852 });
1853 // This is the "relocatable" vaddr, corresponding to `code_buffer` index `0`.
1854 assert(dbg_line_vaddr_reloc_index == dbg_line_buffer.items.len);
1855 dbg_line_buffer.items.len += ptr_width_bytes;
1856
1857 dbg_line_buffer.appendAssumeCapacity(DW.LNS_advance_line);
1858 // This is the "relocatable" relative line offset from the previous function's end curly
1859 // to this function's begin curly.
1860 assert(self.getRelocDbgLineOff() == dbg_line_buffer.items.len);
1861 // Here we use a ULEB128-fixed-4 to make sure this field can be overwritten later.
1862 leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), line_off);
1863
1864 dbg_line_buffer.appendAssumeCapacity(DW.LNS_set_file);
1865 assert(self.getRelocDbgFileIndex() == dbg_line_buffer.items.len);
1866 // Once we support more than one source file, this will have the ability to be more
1867 // than one possible value.
1868 const file_index = 1;
1869 leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), file_index);
1870
1871 // Emit a line for the begin curly with prologue_end=false. The codegen will
1872 // do the work of setting prologue_end=true and epilogue_begin=true.
1873 dbg_line_buffer.appendAssumeCapacity(DW.LNS_copy);
1874 }
1875 const res = try codegen.generateSymbol(self, decl.src(), typed_value, &code_buffer, &dbg_line_buffer);
1876 const code = switch (res) {
1640 .externally_managed => |x| x,1877 .externally_managed => |x| x,
1641 .appended => code_buffer.items,1878 .appended => code_buffer.items,
1642 .fail => |em| {1879 .fail => |em| {
...@@ -1648,10 +1885,7 @@ pub const File = struct {...@@ -1648,10 +1885,7 @@ pub const File = struct {
16481885
1649 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);1886 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
16501887
1651 const stt_bits: u8 = switch (typed_value.ty.zigTypeTag()) {1888 const stt_bits: u8 = if (is_fn) elf.STT_FUNC else elf.STT_OBJECT;
1652 .Fn => elf.STT_FUNC,
1653 else => elf.STT_OBJECT,
1654 };
16551889
1656 assert(decl.link.local_sym_index != 0); // Caller forgot to allocateDeclIndexes()1890 assert(decl.link.local_sym_index != 0); // Caller forgot to allocateDeclIndexes()
1657 const local_sym = &self.local_symbols.items[decl.link.local_sym_index];1891 const local_sym = &self.local_symbols.items[decl.link.local_sym_index];
...@@ -1704,6 +1938,94 @@ pub const File = struct {...@@ -1704,6 +1938,94 @@ pub const File = struct {
1704 const file_offset = self.sections.items[self.text_section_index.?].sh_offset + section_offset;1938 const file_offset = self.sections.items[self.text_section_index.?].sh_offset + section_offset;
1705 try self.file.?.pwriteAll(code, file_offset);1939 try self.file.?.pwriteAll(code, file_offset);
17061940
1941 // If the Decl is a function, we need to update the .debug_line program.
1942 if (is_fn) {
1943 // Perform the relocation based on vaddr.
1944 const target_endian = self.base.options.target.cpu.arch.endian();
1945 switch (self.ptr_width) {
1946 .p32 => {
1947 const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..4];
1948 mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_value), target_endian);
1949 },
1950 .p64 => {
1951 const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..8];
1952 mem.writeInt(u64, ptr, local_sym.st_value, target_endian);
1953 },
1954 }
1955
1956 try dbg_line_buffer.appendSlice(&[_]u8{ DW.LNS_extended_op, 1, DW.LNE_end_sequence });
1957
1958 // Now we have the full contents and may allocate a region to store it.
1959
1960 const debug_line_sect = &self.sections.items[self.debug_line_section_index.?];
1961 const src_fn = &decl.fn_link;
1962 src_fn.len = @intCast(u32, dbg_line_buffer.items.len);
1963 if (self.dbg_line_fn_last) |last| {
1964 if (src_fn.next) |next| {
1965 // Update existing function - non-last item.
1966 if (src_fn.off + src_fn.len + min_nop_size > next.off) {
1967 // It grew too big, so we move it to a new location.
1968 if (src_fn.prev) |prev| {
1969 _ = self.dbg_line_fn_free_list.put(self.allocator, prev, {}) catch {};
1970 prev.next = src_fn.next;
1971 }
1972 next.prev = src_fn.prev;
1973 src_fn.next = null;
1974 // Populate where it used to be with NOPs.
1975 const file_pos = debug_line_sect.sh_offset + src_fn.off;
1976 try self.pwriteWithNops(0, &[0]u8{}, src_fn.len, file_pos);
1977 // TODO Look at the free list before appending at the end.
1978 src_fn.prev = last;
1979 last.next = src_fn;
1980 self.dbg_line_fn_last = src_fn;
1981
1982 src_fn.off = last.off + (last.len * alloc_num / alloc_den);
1983 }
1984 } else if (src_fn.prev == null) {
1985 // Append new function.
1986 // TODO Look at the free list before appending at the end.
1987 src_fn.prev = last;
1988 last.next = src_fn;
1989 self.dbg_line_fn_last = src_fn;
1990
1991 src_fn.off = last.off + (last.len * alloc_num / alloc_den);
1992 }
1993 } else {
1994 // This is the first function of the Line Number Program.
1995 self.dbg_line_fn_first = src_fn;
1996 self.dbg_line_fn_last = src_fn;
1997
1998 src_fn.off = self.dbgLineNeededHeaderBytes() * alloc_num / alloc_den;
1999 }
2000
2001 const last_src_fn = self.dbg_line_fn_last.?;
2002 const needed_size = last_src_fn.off + last_src_fn.len;
2003 if (needed_size != debug_line_sect.sh_size) {
2004 if (needed_size > self.allocatedSize(debug_line_sect.sh_offset)) {
2005 const new_offset = self.findFreeSpace(needed_size, 1);
2006 const existing_size = last_src_fn.off;
2007 log.debug(.link, "moving .debug_line section: {} bytes from 0x{x} to 0x{x}\n", .{
2008 existing_size,
2009 debug_line_sect.sh_offset,
2010 new_offset,
2011 });
2012 const amt = try self.file.?.copyRangeAll(debug_line_sect.sh_offset, self.file.?, new_offset, existing_size);
2013 if (amt != existing_size) return error.InputOutput;
2014 debug_line_sect.sh_offset = new_offset;
2015 }
2016 debug_line_sect.sh_size = needed_size;
2017 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
2018 self.debug_line_header_dirty = true;
2019 }
2020 const prev_padding_size: u32 = if (src_fn.prev) |prev| src_fn.off - (prev.off + prev.len) else 0;
2021 const next_padding_size: u32 = if (src_fn.next) |next| next.off - (src_fn.off + src_fn.len) else 0;
2022
2023 // We only have support for one compilation unit so far, so the offsets are directly
2024 // from the .debug_line section.
2025 const file_pos = debug_line_sect.sh_offset + src_fn.off;
2026 try self.pwriteWithNops(prev_padding_size, dbg_line_buffer.items, next_padding_size, file_pos);
2027 }
2028
1707 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.2029 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
1708 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};2030 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
1709 return self.updateDeclExports(module, decl, decl_exports);2031 return self.updateDeclExports(module, decl, decl_exports);
...@@ -1719,10 +2041,7 @@ pub const File = struct {...@@ -1719,10 +2041,7 @@ pub const File = struct {
1719 const tracy = trace(@src());2041 const tracy = trace(@src());
1720 defer tracy.end();2042 defer tracy.end();
17212043
1722 // In addition to ensuring capacity for global_symbols, we also ensure capacity for freeing all of
1723 // them, so that deleting exports is guaranteed to succeed.
1724 try self.global_symbols.ensureCapacity(self.allocator, self.global_symbols.items.len + exports.len);2044 try self.global_symbols.ensureCapacity(self.allocator, self.global_symbols.items.len + exports.len);
1725 try self.global_symbol_free_list.ensureCapacity(self.allocator, self.global_symbols.items.len);
1726 const typed_value = decl.typed_value.most_recent.typed_value;2045 const typed_value = decl.typed_value.most_recent.typed_value;
1727 if (decl.link.local_sym_index == 0) return;2046 if (decl.link.local_sym_index == 0) return;
1728 const decl_sym = self.local_symbols.items[decl.link.local_sym_index];2047 const decl_sym = self.local_symbols.items[decl.link.local_sym_index];
...@@ -1787,9 +2106,31 @@ pub const File = struct {...@@ -1787,9 +2106,31 @@ pub const File = struct {
1787 }2106 }
1788 }2107 }
17892108
2109 /// Must be called only after a successful call to `updateDecl`.
2110 pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Decl) !void {
2111 const tracy = trace(@src());
2112 defer tracy.end();
2113
2114 const scope_file = decl.scope.cast(Module.Scope.File).?;
2115 const tree = scope_file.contents.tree;
2116 const file_ast_decls = tree.root_node.decls();
2117 // TODO Look into improving the performance here by adding a token-index-to-line
2118 // lookup table. Currently this involves scanning over the source code for newlines.
2119 const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?;
2120 const block = fn_proto.body().?.castTag(.Block).?;
2121 const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start);
2122 const casted_line_off = @intCast(u28, line_delta);
2123
2124 const shdr = &self.sections.items[self.debug_line_section_index.?];
2125 const file_pos = shdr.sh_offset + decl.fn_link.off + self.getRelocDbgLineOff();
2126 var data: [4]u8 = undefined;
2127 leb128.writeUnsignedFixed(4, &data, casted_line_off);
2128 try self.file.?.pwriteAll(&data, file_pos);
2129 }
2130
1790 pub fn deleteExport(self: *Elf, exp: Export) void {2131 pub fn deleteExport(self: *Elf, exp: Export) void {
1791 const sym_index = exp.sym_index orelse return;2132 const sym_index = exp.sym_index orelse return;
1792 self.global_symbol_free_list.appendAssumeCapacity(sym_index);2133 self.global_symbol_free_list.append(self.allocator, sym_index) catch {};
1793 self.global_symbols.items[sym_index].st_info = 0;2134 self.global_symbols.items[sym_index].st_info = 0;
1794 }2135 }
17952136
...@@ -1817,7 +2158,6 @@ pub const File = struct {...@@ -1817,7 +2158,6 @@ pub const File = struct {
18172158
1818 fn writeSectHeader(self: *Elf, index: usize) !void {2159 fn writeSectHeader(self: *Elf, index: usize) !void {
1819 const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();2160 const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
1820 const offset = self.sections.items[index].sh_offset;
1821 switch (self.base.options.target.cpu.arch.ptrBitWidth()) {2161 switch (self.base.options.target.cpu.arch.ptrBitWidth()) {
1822 32 => {2162 32 => {
1823 var shdr: [1]elf.Elf32_Shdr = undefined;2163 var shdr: [1]elf.Elf32_Shdr = undefined;
...@@ -1825,6 +2165,7 @@ pub const File = struct {...@@ -1825,6 +2165,7 @@ pub const File = struct {
1825 if (foreign_endian) {2165 if (foreign_endian) {
1826 bswapAllFields(elf.Elf32_Shdr, &shdr[0]);2166 bswapAllFields(elf.Elf32_Shdr, &shdr[0]);
1827 }2167 }
2168 const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf32_Shdr);
1828 return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);2169 return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
1829 },2170 },
1830 64 => {2171 64 => {
...@@ -1832,6 +2173,7 @@ pub const File = struct {...@@ -1832,6 +2173,7 @@ pub const File = struct {
1832 if (foreign_endian) {2173 if (foreign_endian) {
1833 bswapAllFields(elf.Elf64_Shdr, &shdr[0]);2174 bswapAllFields(elf.Elf64_Shdr, &shdr[0]);
1834 }2175 }
2176 const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf64_Shdr);
1835 return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);2177 return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
1836 },2178 },
1837 else => return error.UnsupportedArchitecture,2179 else => return error.UnsupportedArchitecture,
...@@ -1841,10 +2183,7 @@ pub const File = struct {...@@ -1841,10 +2183,7 @@ pub const File = struct {
1841 fn writeOffsetTableEntry(self: *Elf, index: usize) !void {2183 fn writeOffsetTableEntry(self: *Elf, index: usize) !void {
1842 const shdr = &self.sections.items[self.got_section_index.?];2184 const shdr = &self.sections.items[self.got_section_index.?];
1843 const phdr = &self.program_headers.items[self.phdr_got_index.?];2185 const phdr = &self.program_headers.items[self.phdr_got_index.?];
1844 const entry_size: u16 = switch (self.ptr_width) {2186 const entry_size: u16 = self.ptrWidthBytes();
1845 .p32 => 4,
1846 .p64 => 8,
1847 };
1848 if (self.offset_table_count_dirty) {2187 if (self.offset_table_count_dirty) {
1849 // TODO Also detect virtual address collisions.2188 // TODO Also detect virtual address collisions.
1850 const allocated_size = self.allocatedSize(shdr.sh_offset);2189 const allocated_size = self.allocatedSize(shdr.sh_offset);
...@@ -1987,6 +2326,122 @@ pub const File = struct {...@@ -1987,6 +2326,122 @@ pub const File = struct {
1987 },2326 },
1988 }2327 }
1989 }2328 }
2329
2330 fn ptrWidthBytes(self: Elf) u8 {
2331 return switch (self.ptr_width) {
2332 .p32 => 4,
2333 .p64 => 8,
2334 };
2335 }
2336
2337 /// The reloc offset for the virtual address of a function in its Line Number Program.
2338 /// Size is a virtual address integer.
2339 const dbg_line_vaddr_reloc_index = 3;
2340
2341 /// The reloc offset for the line offset of a function from the previous function's line.
2342 /// It's a fixed-size 4-byte ULEB128.
2343 fn getRelocDbgLineOff(self: Elf) usize {
2344 return dbg_line_vaddr_reloc_index + self.ptrWidthBytes() + 1;
2345 }
2346
2347 fn getRelocDbgFileIndex(self: Elf) usize {
2348 return self.getRelocDbgLineOff() + 5;
2349 }
2350
2351 fn dbgLineNeededHeaderBytes(self: Elf) u32 {
2352 const directory_entry_format_count = 1;
2353 const file_name_entry_format_count = 1;
2354 const directory_count = 1;
2355 const file_name_count = 1;
2356 return @intCast(u32, 53 + directory_entry_format_count * 2 + file_name_entry_format_count * 2 +
2357 directory_count * 8 + file_name_count * 8 +
2358 // These are encoded as DW.FORM_string rather than DW.FORM_strp as we would like
2359 // because of a workaround for readelf and gdb failing to understand DWARFv5 correctly.
2360 self.base.options.root_pkg.root_src_dir_path.len +
2361 self.base.options.root_pkg.root_src_path.len);
2362
2363 }
2364
2365 /// Writes to the file a buffer, prefixed and suffixed by the specified number of
2366 /// bytes of NOPs. Asserts each padding size is at least `min_nop_size` and total padding bytes
2367 /// are less than 126,976 bytes (if this limit is ever reached, this function can be
2368 /// improved to make more than one pwritev call, or the limit can be raised by a fixed
2369 /// amount by increasing the length of `vecs`).
2370 fn pwriteWithNops(
2371 self: *Elf,
2372 prev_padding_size: usize,
2373 buf: []const u8,
2374 next_padding_size: usize,
2375 offset: usize,
2376 ) !void {
2377 const page_of_nops = [1]u8{DW.LNS_negate_stmt} ** 4096;
2378 const three_byte_nop = [3]u8{DW.LNS_advance_pc, 0b1000_0000, 0};
2379 var vecs: [32]std.os.iovec_const = undefined;
2380 var vec_index: usize = 0;
2381 {
2382 var padding_left = prev_padding_size;
2383 if (padding_left % 2 != 0) {
2384 vecs[vec_index] = .{
2385 .iov_base = &three_byte_nop,
2386 .iov_len = three_byte_nop.len,
2387 };
2388 vec_index += 1;
2389 padding_left -= three_byte_nop.len;
2390 }
2391 while (padding_left > page_of_nops.len) {
2392 vecs[vec_index] = .{
2393 .iov_base = &page_of_nops,
2394 .iov_len = page_of_nops.len,
2395 };
2396 vec_index += 1;
2397 padding_left -= page_of_nops.len;
2398 }
2399 if (padding_left > 0) {
2400 vecs[vec_index] = .{
2401 .iov_base = &page_of_nops,
2402 .iov_len = padding_left,
2403 };
2404 vec_index += 1;
2405 }
2406 }
2407
2408 vecs[vec_index] = .{
2409 .iov_base = buf.ptr,
2410 .iov_len = buf.len,
2411 };
2412 vec_index += 1;
2413
2414 {
2415 var padding_left = next_padding_size;
2416 if (padding_left % 2 != 0) {
2417 vecs[vec_index] = .{
2418 .iov_base = &three_byte_nop,
2419 .iov_len = three_byte_nop.len,
2420 };
2421 vec_index += 1;
2422 padding_left -= three_byte_nop.len;
2423 }
2424 while (padding_left > page_of_nops.len) {
2425 vecs[vec_index] = .{
2426 .iov_base = &page_of_nops,
2427 .iov_len = page_of_nops.len,
2428 };
2429 vec_index += 1;
2430 padding_left -= page_of_nops.len;
2431 }
2432 if (padding_left > 0) {
2433 vecs[vec_index] = .{
2434 .iov_base = &page_of_nops,
2435 .iov_len = padding_left,
2436 };
2437 vec_index += 1;
2438 }
2439 }
2440 try self.file.?.pwritevAll(vecs[0..vec_index], offset - prev_padding_size);
2441 }
2442
2443 const min_nop_size = 2;
2444
1990 };2445 };
1991};2446};
19922447
src-self-hosted/main.zig+9-13
...@@ -10,9 +10,7 @@ const Module = @import("Module.zig");...@@ -10,9 +10,7 @@ const Module = @import("Module.zig");
10const link = @import("link.zig");10const link = @import("link.zig");
11const Package = @import("Package.zig");11const Package = @import("Package.zig");
12const zir = @import("zir.zig");12const zir = @import("zir.zig");
1313const build_options = @import("build_options");
14// TODO Improve async I/O enough that we feel comfortable doing this.
15//pub const io_mode = .evented;
1614
17pub const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB15pub const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB
1816
...@@ -47,18 +45,16 @@ pub fn log(...@@ -47,18 +45,16 @@ pub fn log(
47 if (@enumToInt(level) > @enumToInt(std.log.level))45 if (@enumToInt(level) > @enumToInt(std.log.level))
48 return;46 return;
4947
50 const scope_prefix = "(" ++ switch (scope) {48 const scope_name = @tagName(scope);
51 // Uncomment to hide logs49 const ok = comptime for (build_options.log_scopes) |log_scope| {
52 //.compiler,50 if (mem.eql(u8, log_scope, scope_name))
53 .module,51 break true;
54 .liveness,52 } else false;
55 .link,
56 => return,
5753
58 else => @tagName(scope),54 if (!ok)
59 } ++ "): ";55 return;
6056
61 const prefix = "[" ++ @tagName(level) ++ "] " ++ scope_prefix;57 const prefix = "[" ++ @tagName(level) ++ "] " ++ "(" ++ @tagName(scope) ++ "): ";
6258
63 // Print the message to stderr, silently ignoring any errors59 // Print the message to stderr, silently ignoring any errors
64 std.debug.print(prefix ++ format, args);60 std.debug.print(prefix ++ format, args);
src-self-hosted/zir.zig+5
...@@ -107,6 +107,8 @@ pub const Inst = struct {...@@ -107,6 +107,8 @@ pub const Inst = struct {
107 condbr,107 condbr,
108 /// Special case, has no textual representation.108 /// Special case, has no textual representation.
109 @"const",109 @"const",
110 /// Declares the beginning of a statement. Used for debug info.
111 dbg_stmt,
110 /// Represents a pointer to a global decl by name.112 /// Represents a pointer to a global decl by name.
111 declref,113 declref,
112 /// Represents a pointer to a global decl by string name.114 /// Represents a pointer to a global decl by string name.
...@@ -211,6 +213,7 @@ pub const Inst = struct {...@@ -211,6 +213,7 @@ pub const Inst = struct {
211 return switch (tag) {213 return switch (tag) {
212 .arg,214 .arg,
213 .breakpoint,215 .breakpoint,
216 .dbg_stmt,
214 .returnvoid,217 .returnvoid,
215 .alloc_inferred,218 .alloc_inferred,
216 .ret_ptr,219 .ret_ptr,
...@@ -324,6 +327,7 @@ pub const Inst = struct {...@@ -324,6 +327,7 @@ pub const Inst = struct {
324 .coerce_result_block_ptr,327 .coerce_result_block_ptr,
325 .coerce_to_ptr_elem,328 .coerce_to_ptr_elem,
326 .@"const",329 .@"const",
330 .dbg_stmt,
327 .declref,331 .declref,
328 .declref_str,332 .declref_str,
329 .declval,333 .declval,
...@@ -1843,6 +1847,7 @@ const EmitZIR = struct {...@@ -1843,6 +1847,7 @@ const EmitZIR = struct {
1843 .breakpoint => try self.emitNoOp(inst.src, .breakpoint),1847 .breakpoint => try self.emitNoOp(inst.src, .breakpoint),
1844 .unreach => try self.emitNoOp(inst.src, .@"unreachable"),1848 .unreach => try self.emitNoOp(inst.src, .@"unreachable"),
1845 .retvoid => try self.emitNoOp(inst.src, .returnvoid),1849 .retvoid => try self.emitNoOp(inst.src, .returnvoid),
1850 .dbg_stmt => try self.emitNoOp(inst.src, .dbg_stmt),
18461851
1847 .not => try self.emitUnOp(inst.src, new_body, inst.castTag(.not).?, .boolnot),1852 .not => try self.emitUnOp(inst.src, new_body, inst.castTag(.not).?, .boolnot),
1848 .ret => try self.emitUnOp(inst.src, new_body, inst.castTag(.ret).?, .@"return"),1853 .ret => try self.emitUnOp(inst.src, new_body, inst.castTag(.ret).?, .@"return"),
src-self-hosted/zir_sema.zig+6
...@@ -41,6 +41,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!...@@ -41,6 +41,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
41 .coerce_to_ptr_elem => return analyzeInstCoerceToPtrElem(mod, scope, old_inst.castTag(.coerce_to_ptr_elem).?),41 .coerce_to_ptr_elem => return analyzeInstCoerceToPtrElem(mod, scope, old_inst.castTag(.coerce_to_ptr_elem).?),
42 .compileerror => return analyzeInstCompileError(mod, scope, old_inst.castTag(.compileerror).?),42 .compileerror => return analyzeInstCompileError(mod, scope, old_inst.castTag(.compileerror).?),
43 .@"const" => return analyzeInstConst(mod, scope, old_inst.castTag(.@"const").?),43 .@"const" => return analyzeInstConst(mod, scope, old_inst.castTag(.@"const").?),
44 .dbg_stmt => return analyzeInstDbgStmt(mod, scope, old_inst.castTag(.dbg_stmt).?),
44 .declref => return analyzeInstDeclRef(mod, scope, old_inst.castTag(.declref).?),45 .declref => return analyzeInstDeclRef(mod, scope, old_inst.castTag(.declref).?),
45 .declref_str => return analyzeInstDeclRefStr(mod, scope, old_inst.castTag(.declref_str).?),46 .declref_str => return analyzeInstDeclRefStr(mod, scope, old_inst.castTag(.declref_str).?),
46 .declval => return analyzeInstDeclVal(mod, scope, old_inst.castTag(.declval).?),47 .declval => return analyzeInstDeclVal(mod, scope, old_inst.castTag(.declval).?),
...@@ -487,6 +488,11 @@ fn analyzeInstBreakVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.BreakVoid)...@@ -487,6 +488,11 @@ fn analyzeInstBreakVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.BreakVoid)
487 return analyzeBreak(mod, scope, inst.base.src, block, void_inst);488 return analyzeBreak(mod, scope, inst.base.src, block, void_inst);
488}489}
489490
491fn analyzeInstDbgStmt(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
492 const b = try mod.requireRuntimeBlock(scope, inst.base.src);
493 return mod.addNoOp(b, inst.base.src, Type.initTag(.void), .dbg_stmt);
494}
495
490fn analyzeInstDeclRefStr(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclRefStr) InnerError!*Inst {496fn analyzeInstDeclRefStr(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclRefStr) InnerError!*Inst {
491 const decl_name = try resolveConstString(mod, scope, inst.positionals.name);497 const decl_name = try resolveConstString(mod, scope, inst.positionals.name);
492 return mod.analyzeDeclRefByName(scope, inst.base.src, decl_name);498 return mod.analyzeDeclRefByName(scope, inst.base.src, decl_name);