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
signaturebadge-question-mark 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 {
7777 const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse false;
7878 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);
8083 exe.addBuildOption(bool, "enable_tracy", tracy != null);
8184 if (tracy) |tracy_path| {
8285 const client_cpp = fs.path.join(
lib/std/build.zig+31-19
......@@ -430,9 +430,9 @@ pub const Builder = struct {
430430 const entry = self.user_input_options.getEntry(name) orelse return null;
431431 entry.value.used = true;
432432 switch (type_id) {
433 TypeId.Bool => switch (entry.value.value) {
434 UserValue.Flag => return true,
435 UserValue.Scalar => |s| {
433 .Bool => switch (entry.value.value) {
434 .Flag => return true,
435 .Scalar => |s| {
436436 if (mem.eql(u8, s, "true")) {
437437 return true;
438438 } else if (mem.eql(u8, s, "false")) {
......@@ -443,21 +443,21 @@ pub const Builder = struct {
443443 return null;
444444 }
445445 },
446 UserValue.List => {
446 .List => {
447447 warn("Expected -D{} to be a boolean, but received a list.\n", .{name});
448448 self.markInvalidUserInput();
449449 return null;
450450 },
451451 },
452 TypeId.Int => panic("TODO integer options to build script", .{}),
453 TypeId.Float => panic("TODO float options to build script", .{}),
454 TypeId.Enum => switch (entry.value.value) {
455 UserValue.Flag => {
452 .Int => panic("TODO integer options to build script", .{}),
453 .Float => panic("TODO float options to build script", .{}),
454 .Enum => switch (entry.value.value) {
455 .Flag => {
456456 warn("Expected -D{} to be a string, but received a boolean.\n", .{name});
457457 self.markInvalidUserInput();
458458 return null;
459459 },
460 UserValue.Scalar => |s| {
460 .Scalar => |s| {
461461 if (std.meta.stringToEnum(T, s)) |enum_lit| {
462462 return enum_lit;
463463 } else {
......@@ -466,33 +466,35 @@ pub const Builder = struct {
466466 return null;
467467 }
468468 },
469 UserValue.List => {
469 .List => {
470470 warn("Expected -D{} to be a string, but received a list.\n", .{name});
471471 self.markInvalidUserInput();
472472 return null;
473473 },
474474 },
475 TypeId.String => switch (entry.value.value) {
476 UserValue.Flag => {
475 .String => switch (entry.value.value) {
476 .Flag => {
477477 warn("Expected -D{} to be a string, but received a boolean.\n", .{name});
478478 self.markInvalidUserInput();
479479 return null;
480480 },
481 UserValue.List => {
481 .List => {
482482 warn("Expected -D{} to be a string, but received a list.\n", .{name});
483483 self.markInvalidUserInput();
484484 return null;
485485 },
486 UserValue.Scalar => |s| return s,
486 .Scalar => |s| return s,
487487 },
488 TypeId.List => switch (entry.value.value) {
489 UserValue.Flag => {
488 .List => switch (entry.value.value) {
489 .Flag => {
490490 warn("Expected -D{} to be a list, but received a boolean.\n", .{name});
491491 self.markInvalidUserInput();
492492 return null;
493493 },
494 UserValue.Scalar => |s| return &[_][]const u8{s},
495 UserValue.List => |lst| return lst.span(),
494 .Scalar => |s| {
495 return self.allocator.dupe([]const u8, &[_][]const u8{s}) catch unreachable;
496 },
497 .List => |lst| return lst.span(),
496498 },
497499 }
498500 }
......@@ -1706,9 +1708,19 @@ pub const LibExeObjStep = struct {
17061708
17071709 pub fn addBuildOption(self: *LibExeObjStep, comptime T: type, name: []const u8, value: T) void {
17081710 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 }
17091721 switch (@typeInfo(T)) {
17101722 .Enum => |enum_info| {
1711 out.print("const {} = enum {{\n", .{@typeName(T)}) catch unreachable;
1723 out.print("pub const {} = enum {{\n", .{@typeName(T)}) catch unreachable;
17121724 inline for (enum_info.fields) |field| {
17131725 out.print(" {},\n", .{field.name}) catch unreachable;
17141726 }
lib/std/hash_map.zig+13-5
......@@ -196,6 +196,10 @@ pub fn HashMap(
196196 return self.unmanaged.getEntry(key);
197197 }
198198
199 pub fn getIndex(self: Self, key: K) ?usize {
200 return self.unmanaged.getIndex(key);
201 }
202
199203 pub fn get(self: Self, key: K) ?V {
200204 return self.unmanaged.get(key);
201205 }
......@@ -479,17 +483,21 @@ pub fn HashMapUnmanaged(
479483 }
480484
481485 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 {
482491 const header = self.index_header orelse {
483492 // Linear scan.
484493 const h = if (store_hash) hash(key) else {};
485 for (self.entries.items) |*item| {
494 for (self.entries.items) |*item, i| {
486495 if (item.hash == h and eql(key, item.key)) {
487 return item;
496 return i;
488497 }
489498 }
490499 return null;
491500 };
492
493501 switch (header.capacityIndexType()) {
494502 .u8 => return self.getInternal(key, header, u8),
495503 .u16 => return self.getInternal(key, header, u16),
......@@ -711,7 +719,7 @@ pub fn HashMapUnmanaged(
711719 unreachable;
712720 }
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 {
715723 const indexes = header.indexes(I);
716724 const h = hash(key);
717725 const start_index = header.constrainIndex(h);
......@@ -725,7 +733,7 @@ pub fn HashMapUnmanaged(
725733 const entry = &self.entries.items[index.entry_index];
726734 const hash_match = if (store_hash) h == entry.hash else true;
727735 if (hash_match and eql(key, entry.key))
728 return entry;
736 return index.entry_index;
729737 }
730738 return null;
731739 }
lib/std/zig.zig+16
......@@ -43,6 +43,22 @@ pub fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usi
4343 return .{ .line = line, .column = column };
4444}
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
4662/// Returns the standard file system basename of a binary generated by the Zig compiler.
4763pub fn binNameAlloc(
4864 allocator: *std.mem.Allocator,
lib/std/zig/ast.zig+6-2
......@@ -1299,6 +1299,10 @@ pub const Node = struct {
12991299 });
13001300 }
13011301
1302 pub fn body(self: *const FnProto) ?*Node {
1303 return self.getTrailer("body_node");
1304 }
1305
13021306 pub fn getTrailer(self: *const FnProto, comptime name: []const u8) ?TrailerFlags.Field(name) {
13031307 const trailers_start = @alignCast(
13041308 @alignOf(ParamDecl),
......@@ -1381,7 +1385,7 @@ pub const Node = struct {
13811385 .Invalid => {},
13821386 }
13831387
1384 if (self.getTrailer("body_node")) |body_node| {
1388 if (self.body()) |body_node| {
13851389 if (i < 1) return body_node;
13861390 i -= 1;
13871391 }
......@@ -1397,7 +1401,7 @@ pub const Node = struct {
13971401 }
13981402
13991403 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();
14011405 switch (self.return_type) {
14021406 .Explicit, .InferErrorSet => |node| return node.lastToken(),
14031407 .Invalid => |tok| return tok,
src-self-hosted/Module.zig+63-40
......@@ -6,6 +6,7 @@ const Value = @import("value.zig").Value;
66const Type = @import("type.zig").Type;
77const TypedValue = @import("TypedValue.zig");
88const assert = std.debug.assert;
9const log = std.log;
910const BigIntConst = std.math.big.int.Const;
1011const BigIntMutable = std.math.big.int.Mutable;
1112const Target = std.Target;
......@@ -88,6 +89,9 @@ const WorkItem = union(enum) {
8889 /// It may have already be analyzed, or it may have been determined
8990 /// to be outdated; in this case perform semantic analysis again.
9091 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,
9195};
9296
9397pub const Export = struct {
......@@ -175,6 +179,13 @@ pub const Decl = struct {
175179 /// This is populated regardless of semantic analysis and code generation.
176180 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
178189 contents_hash: std.zig.SrcHash,
179190
180191 /// The shallow set of other decls whose typed_value could possibly change if this Decl's
......@@ -235,7 +246,7 @@ pub const Decl = struct {
235246
236247 pub fn dump(self: *Decl) void {
237248 const loc = std.zig.findLineColumn(self.scope.source.bytes, self.src);
238 std.debug.warn("{}:{}:{} name={} status={}", .{
249 std.debug.print("{}:{}:{} name={} status={}", .{
239250 self.scope.sub_file_path,
240251 loc.line + 1,
241252 loc.column + 1,
......@@ -243,9 +254,9 @@ pub const Decl = struct {
243254 @tagName(self.analysis),
244255 });
245256 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 });
247258 }
248 std.debug.warn("\n", .{});
259 std.debug.print("\n", .{});
249260 }
250261
251262 pub fn typedValueManaged(self: *Decl) ?*TypedValue.Managed {
......@@ -541,7 +552,7 @@ pub const Scope = struct {
541552
542553 pub fn dumpSrc(self: *File, src: usize) void {
543554 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 });
545556 }
546557
547558 pub fn getSource(self: *File, module: *Module) ![:0]const u8 {
......@@ -643,7 +654,7 @@ pub const Scope = struct {
643654
644655 pub fn dumpSrc(self: *ZIRModule, src: usize) void {
645656 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 });
647658 }
648659
649660 pub fn getSource(self: *ZIRModule, module: *Module) ![:0]const u8 {
......@@ -792,7 +803,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
792803 const bin_file_dir = options.bin_file_dir orelse std.fs.cwd();
793804 const bin_file = try link.File.openPath(gpa, bin_file_dir, options.bin_file_path, .{
794805 .root_name = root_name,
795 .root_src_dir_path = options.root_pkg.root_src_dir_path,
806 .root_pkg = options.root_pkg,
796807 .target = options.target,
797808 .output_mode = options.output_mode,
798809 .link_mode = options.link_mode orelse .Static,
......@@ -885,6 +896,7 @@ pub fn deinit(self: *Module) void {
885896
886897fn freeExportList(gpa: *Allocator, export_list: []*Export) void {
887898 for (export_list) |exp| {
899 gpa.free(exp.options.name);
888900 gpa.destroy(exp);
889901 }
890902 gpa.free(export_list);
......@@ -943,7 +955,6 @@ pub fn update(self: *Module) !void {
943955 }
944956
945957 self.link_error_flags = self.bin_file.errorFlags();
946 std.log.debug(.module, "link_error_flags: {}\n", .{self.link_error_flags});
947958
948959 // If there are any errors, we anticipate the source files being loaded
949960 // 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 {
10571068 error.AnalysisFail => {
10581069 decl.analysis = .dependency_failure;
10591070 },
1060 error.CGenFailure => {
1061 // Error is handled by CBE, don't try adding it again
1062 },
10631071 else => {
10641072 try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1);
1065 const result = self.failed_decls.getOrPutAssumeCapacity(decl);
1066 if (result.found_existing) {
1067 std.debug.panic("Internal error: attempted to override error '{}' with 'unable to codegen: {}'", .{ result.entry.value.msg, @errorName(err) });
1068 } else {
1069 result.entry.value = try ErrorMsg.create(
1070 self.gpa,
1071 decl.src(),
1072 "unable to codegen: {}",
1073 .{@errorName(err)},
1074 );
1075 }
1073 self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
1074 self.gpa,
1075 decl.src(),
1076 "unable to codegen: {}",
1077 .{@errorName(err)},
1078 ));
10761079 decl.analysis = .codegen_failure_retryable;
10771080 },
10781081 };
......@@ -1084,6 +1087,18 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
10841087 error.AnalysisFail => continue,
10851088 };
10861089 },
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 },
10871102 };
10881103}
10891104
......@@ -1101,12 +1116,10 @@ pub fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {
11011116 .codegen_failure_retryable,
11021117 => return error.AnalysisFail,
11031118
1104 .complete, .outdated => blk: {
1105 if (decl.generation == self.generation) {
1106 assert(decl.analysis == .complete);
1107 return;
1108 }
1109 //std.debug.warn("re-analyzing {}\n", .{decl.name});
1119 .complete => return,
1120
1121 .outdated => blk: {
1122 log.debug(.module, "re-analyzing {}\n", .{decl.name});
11101123
11111124 // The exports this Decl performs will be re-discovered, so we remove them here
11121125 // prior to re-analysis.
......@@ -1481,6 +1494,9 @@ fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {
14811494}
14821495
14831496fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
1497 const tracy = trace(@src());
1498 defer tracy.end();
1499
14841500 // We may be analyzing it for the first time, or this may be
14851501 // an incremental update. This code handles both cases.
14861502 const tree = try self.getAstTree(root_scope);
......@@ -1522,6 +1538,10 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
15221538 if (!srcHashEql(decl.contents_hash, contents_hash)) {
15231539 try self.markOutdatedDecl(decl);
15241540 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 });
15251545 }
15261546 }
15271547 } else {
......@@ -1540,7 +1560,7 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void {
15401560 // Handle explicitly deleted decls from the source code. Not to be confused
15411561 // with when we delete decls because they are no longer referenced.
15421562 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});
15441564 try self.deleteDecl(entry.key);
15451565 }
15461566}
......@@ -1569,7 +1589,6 @@ fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void {
15691589 const name_hash = root_scope.fullyQualifiedNameHash(src_decl.name);
15701590 if (self.decl_table.get(name_hash)) |decl| {
15711591 deleted_decls.removeAssertDiscard(decl);
1572 //std.debug.warn("'{}' contents: '{}'\n", .{ src_decl.name, src_decl.contents });
15731592 if (!srcHashEql(src_decl.contents_hash, decl.contents_hash)) {
15741593 try self.markOutdatedDecl(decl);
15751594 decl.contents_hash = src_decl.contents_hash;
......@@ -1594,7 +1613,7 @@ fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void {
15941613 // Handle explicitly deleted decls from the source code. Not to be confused
15951614 // with when we delete decls because they are no longer referenced.
15961615 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});
15981617 try self.deleteDecl(entry.key);
15991618 }
16001619}
......@@ -1606,7 +1625,7 @@ fn deleteDecl(self: *Module, decl: *Decl) !void {
16061625 // not be present in the set, and this does nothing.
16071626 decl.scope.removeDecl(decl);
16081627
1609 //std.debug.warn("deleting decl '{}'\n", .{decl.name});
1628 log.debug(.module, "deleting decl '{}'\n", .{decl.name});
16101629 const name_hash = decl.fullyQualifiedNameHash();
16111630 self.decl_table.removeAssertDiscard(name_hash);
16121631 // 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 {
16681687 entry.value.destroy(self.gpa);
16691688 }
16701689 _ = self.symbol_exports.remove(exp.options.name);
1690 self.gpa.free(exp.options.name);
16711691 self.gpa.destroy(exp);
16721692 }
16731693 self.gpa.free(kv.value);
......@@ -1692,17 +1712,17 @@ fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
16921712 const fn_zir = func.analysis.queued;
16931713 defer fn_zir.arena.promote(self.gpa).deinit();
16941714 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
16971717 try zir_sema.analyzeBody(self, &inner_block.base, fn_zir.body);
16981718
16991719 const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items);
17001720 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});
17021722}
17031723
17041724fn markOutdatedDecl(self: *Module, decl: *Decl) !void {
1705 //std.debug.warn("mark {} outdated\n", .{decl.name});
1725 log.debug(.module, "mark {} outdated\n", .{decl.name});
17061726 try self.work_queue.writeItem(.{ .analyze_decl = decl });
17071727 if (self.failed_decls.remove(decl)) |entry| {
17081728 entry.value.destroy(self.gpa);
......@@ -1768,7 +1788,7 @@ pub fn resolveDefinedValue(self: *Module, scope: *Scope, base: *Inst) !?Value {
17681788 return null;
17691789}
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 {
17721792 try self.ensureDeclAnalyzed(exported_decl);
17731793 const typed_value = exported_decl.typed_value.most_recent.typed_value;
17741794 switch (typed_value.ty.zigTypeTag()) {
......@@ -1782,6 +1802,9 @@ pub fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []co
17821802 const new_export = try self.gpa.create(Export);
17831803 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
17851808 const owner_decl = scope.decl().?;
17861809
17871810 new_export.* = .{
......@@ -1794,7 +1817,7 @@ pub fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []co
17941817 };
17951818
17961819 // 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);
17981821 if (!eo_gop.found_existing) {
17991822 eo_gop.entry.value = &[0]*Export{};
18001823 }
......@@ -1803,7 +1826,7 @@ pub fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []co
18031826 errdefer eo_gop.entry.value = self.gpa.shrink(eo_gop.entry.value, eo_gop.entry.value.len - 1);
18041827
18051828 // 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);
18071830 if (!de_gop.found_existing) {
18081831 de_gop.entry.value = &[0]*Export{};
18091832 }
......@@ -2811,7 +2834,7 @@ pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void {
28112834 const source = zir_module.getSource(self) catch @panic("dumpInst failed to get source");
28122835 const loc = std.zig.findLineColumn(source, inst.src);
28132836 if (inst.tag == .constant) {
2814 std.debug.warn("constant ty={} val={} src={}:{}:{}\n", .{
2837 std.debug.print("constant ty={} val={} src={}:{}:{}\n", .{
28152838 inst.ty,
28162839 inst.castTag(.constant).?.val,
28172840 zir_module.subFilePath(),
......@@ -2819,7 +2842,7 @@ pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void {
28192842 loc.column + 1,
28202843 });
28212844 } else if (inst.deaths == 0) {
2822 std.debug.warn("{} ty={} src={}:{}:{}\n", .{
2845 std.debug.print("{} ty={} src={}:{}:{}\n", .{
28232846 @tagName(inst.tag),
28242847 inst.ty,
28252848 zir_module.subFilePath(),
......@@ -2827,7 +2850,7 @@ pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void {
28272850 loc.column + 1,
28282851 });
28292852 } else {
2830 std.debug.warn("{} ty={} deaths={b} src={}:{}:{}\n", .{
2853 std.debug.print("{} ty={} deaths={b} src={}:{}:{}\n", .{
28312854 @tagName(inst.tag),
28322855 inst.ty,
28332856 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
120120
121121 var scope = parent_scope;
122122 for (block_node.statements()) |statement| {
123 const src = scope.tree().token_locs[statement.firstToken()].start;
124 _ = try addZIRNoOp(mod, scope, src, .dbg_stmt);
123125 switch (statement.tag) {
124126 .VarDecl => {
125127 const var_decl_node = statement.castTag(.VarDecl).?;
......@@ -146,7 +148,6 @@ pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block
146148 else => {
147149 const possibly_unused_result = try expr(mod, scope, .none, statement);
148150 if (!possibly_unused_result.tag.isNoReturn()) {
149 const src = scope.tree().token_locs[statement.firstToken()].start;
150151 _ = try addZIRUnOp(mod, scope, src, .ensure_result_used, possibly_unused_result);
151152 }
152153 },
src-self-hosted/codegen.zig+141-62
......@@ -12,6 +12,11 @@ const ErrorMsg = Module.ErrorMsg;
1212const Target = std.Target;
1313const Allocator = mem.Allocator;
1414const 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
1621/// The codegen-related data that is stored in `ir.Inst.Block` instructions.
1722pub const BlockData = struct {
......@@ -44,6 +49,7 @@ pub fn generateSymbol(
4449 src: usize,
4550 typed_value: TypedValue,
4651 code: *std.ArrayList(u8),
52 dbg_line: *std.ArrayList(u8),
4753) GenerateSymbolError!Result {
4854 const tracy = trace(@src());
4955 defer tracy.end();
......@@ -51,57 +57,57 @@ pub fn generateSymbol(
5157 switch (typed_value.ty.zigTypeTag()) {
5258 .Fn => {
5359 switch (bin_file.base.options.target.cpu.arch) {
54 //.arm => return Function(.arm).generateSymbol(bin_file, src, typed_value, code),
55 //.armeb => return Function(.armeb).generateSymbol(bin_file, src, typed_value, code),
56 //.aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code),
57 //.aarch64_be => return Function(.aarch64_be).generateSymbol(bin_file, src, typed_value, code),
58 //.aarch64_32 => return Function(.aarch64_32).generateSymbol(bin_file, src, typed_value, code),
59 //.arc => return Function(.arc).generateSymbol(bin_file, src, typed_value, code),
60 //.avr => return Function(.avr).generateSymbol(bin_file, src, typed_value, code),
61 //.bpfel => return Function(.bpfel).generateSymbol(bin_file, src, typed_value, code),
62 //.bpfeb => return Function(.bpfeb).generateSymbol(bin_file, src, typed_value, code),
63 //.hexagon => return Function(.hexagon).generateSymbol(bin_file, src, typed_value, code),
64 //.mips => return Function(.mips).generateSymbol(bin_file, src, typed_value, code),
65 //.mipsel => return Function(.mipsel).generateSymbol(bin_file, src, typed_value, code),
66 //.mips64 => return Function(.mips64).generateSymbol(bin_file, src, typed_value, code),
67 //.mips64el => return Function(.mips64el).generateSymbol(bin_file, src, typed_value, code),
68 //.msp430 => return Function(.msp430).generateSymbol(bin_file, src, typed_value, code),
69 //.powerpc => return Function(.powerpc).generateSymbol(bin_file, src, typed_value, code),
70 //.powerpc64 => return Function(.powerpc64).generateSymbol(bin_file, src, typed_value, code),
71 //.powerpc64le => return Function(.powerpc64le).generateSymbol(bin_file, src, typed_value, code),
72 //.r600 => return Function(.r600).generateSymbol(bin_file, src, typed_value, code),
73 //.amdgcn => return Function(.amdgcn).generateSymbol(bin_file, src, typed_value, code),
74 //.riscv32 => return Function(.riscv32).generateSymbol(bin_file, src, typed_value, code),
75 //.riscv64 => return Function(.riscv64).generateSymbol(bin_file, src, typed_value, code),
76 //.sparc => return Function(.sparc).generateSymbol(bin_file, src, typed_value, code),
77 //.sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code),
78 //.sparcel => return Function(.sparcel).generateSymbol(bin_file, src, typed_value, code),
79 //.s390x => return Function(.s390x).generateSymbol(bin_file, src, typed_value, code),
80 //.tce => return Function(.tce).generateSymbol(bin_file, src, typed_value, code),
81 //.tcele => return Function(.tcele).generateSymbol(bin_file, src, typed_value, code),
82 //.thumb => return Function(.thumb).generateSymbol(bin_file, src, typed_value, code),
83 //.thumbeb => return Function(.thumbeb).generateSymbol(bin_file, src, typed_value, code),
84 //.i386 => return Function(.i386).generateSymbol(bin_file, src, typed_value, code),
85 .x86_64 => return Function(.x86_64).generateSymbol(bin_file, src, typed_value, code),
86 //.xcore => return Function(.xcore).generateSymbol(bin_file, src, typed_value, code),
87 //.nvptx => return Function(.nvptx).generateSymbol(bin_file, src, typed_value, code),
88 //.nvptx64 => return Function(.nvptx64).generateSymbol(bin_file, src, typed_value, code),
89 //.le32 => return Function(.le32).generateSymbol(bin_file, src, typed_value, code),
90 //.le64 => return Function(.le64).generateSymbol(bin_file, src, typed_value, code),
91 //.amdil => return Function(.amdil).generateSymbol(bin_file, src, typed_value, code),
92 //.amdil64 => return Function(.amdil64).generateSymbol(bin_file, src, typed_value, code),
93 //.hsail => return Function(.hsail).generateSymbol(bin_file, src, typed_value, code),
94 //.hsail64 => return Function(.hsail64).generateSymbol(bin_file, src, typed_value, code),
95 //.spir => return Function(.spir).generateSymbol(bin_file, src, typed_value, code),
96 //.spir64 => return Function(.spir64).generateSymbol(bin_file, src, typed_value, code),
97 //.kalimba => return Function(.kalimba).generateSymbol(bin_file, src, typed_value, code),
98 //.shave => return Function(.shave).generateSymbol(bin_file, src, typed_value, code),
99 //.lanai => return Function(.lanai).generateSymbol(bin_file, src, typed_value, code),
100 //.wasm32 => return Function(.wasm32).generateSymbol(bin_file, src, typed_value, code),
101 //.wasm64 => return Function(.wasm64).generateSymbol(bin_file, src, typed_value, code),
102 //.renderscript32 => return Function(.renderscript32).generateSymbol(bin_file, src, typed_value, code),
103 //.renderscript64 => return Function(.renderscript64).generateSymbol(bin_file, src, typed_value, code),
104 //.ve => return Function(.ve).generateSymbol(bin_file, src, typed_value, code),
60 //.arm => return Function(.arm).generateSymbol(bin_file, src, typed_value, code, dbg_line),
61 //.armeb => return Function(.armeb).generateSymbol(bin_file, src, typed_value, code, dbg_line),
62 //.aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
63 //.aarch64_be => return Function(.aarch64_be).generateSymbol(bin_file, src, typed_value, code, dbg_line),
64 //.aarch64_32 => return Function(.aarch64_32).generateSymbol(bin_file, src, typed_value, code, dbg_line),
65 //.arc => return Function(.arc).generateSymbol(bin_file, src, typed_value, code, dbg_line),
66 //.avr => return Function(.avr).generateSymbol(bin_file, src, typed_value, code, dbg_line),
67 //.bpfel => return Function(.bpfel).generateSymbol(bin_file, src, typed_value, code, dbg_line),
68 //.bpfeb => return Function(.bpfeb).generateSymbol(bin_file, src, typed_value, code, dbg_line),
69 //.hexagon => return Function(.hexagon).generateSymbol(bin_file, src, typed_value, code, dbg_line),
70 //.mips => return Function(.mips).generateSymbol(bin_file, src, typed_value, code, dbg_line),
71 //.mipsel => return Function(.mipsel).generateSymbol(bin_file, src, typed_value, code, dbg_line),
72 //.mips64 => return Function(.mips64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
73 //.mips64el => return Function(.mips64el).generateSymbol(bin_file, src, typed_value, code, dbg_line),
74 //.msp430 => return Function(.msp430).generateSymbol(bin_file, src, typed_value, code, dbg_line),
75 //.powerpc => return Function(.powerpc).generateSymbol(bin_file, src, typed_value, code, dbg_line),
76 //.powerpc64 => return Function(.powerpc64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
77 //.powerpc64le => return Function(.powerpc64le).generateSymbol(bin_file, src, typed_value, code, dbg_line),
78 //.r600 => return Function(.r600).generateSymbol(bin_file, src, typed_value, code, dbg_line),
79 //.amdgcn => return Function(.amdgcn).generateSymbol(bin_file, src, typed_value, code, dbg_line),
80 //.riscv32 => return Function(.riscv32).generateSymbol(bin_file, src, typed_value, code, dbg_line),
81 //.riscv64 => return Function(.riscv64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
82 //.sparc => return Function(.sparc).generateSymbol(bin_file, src, typed_value, code, dbg_line),
83 //.sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code, dbg_line),
84 //.sparcel => return Function(.sparcel).generateSymbol(bin_file, src, typed_value, code, dbg_line),
85 //.s390x => return Function(.s390x).generateSymbol(bin_file, src, typed_value, code, dbg_line),
86 //.tce => return Function(.tce).generateSymbol(bin_file, src, typed_value, code, dbg_line),
87 //.tcele => return Function(.tcele).generateSymbol(bin_file, src, typed_value, code, dbg_line),
88 //.thumb => return Function(.thumb).generateSymbol(bin_file, src, typed_value, code, dbg_line),
89 //.thumbeb => return Function(.thumbeb).generateSymbol(bin_file, src, typed_value, code, dbg_line),
90 //.i386 => return Function(.i386).generateSymbol(bin_file, src, typed_value, code, dbg_line),
91 .x86_64 => return Function(.x86_64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
92 //.xcore => return Function(.xcore).generateSymbol(bin_file, src, typed_value, code, dbg_line),
93 //.nvptx => return Function(.nvptx).generateSymbol(bin_file, src, typed_value, code, dbg_line),
94 //.nvptx64 => return Function(.nvptx64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
95 //.le32 => return Function(.le32).generateSymbol(bin_file, src, typed_value, code, dbg_line),
96 //.le64 => return Function(.le64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
97 //.amdil => return Function(.amdil).generateSymbol(bin_file, src, typed_value, code, dbg_line),
98 //.amdil64 => return Function(.amdil64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
99 //.hsail => return Function(.hsail).generateSymbol(bin_file, src, typed_value, code, dbg_line),
100 //.hsail64 => return Function(.hsail64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
101 //.spir => return Function(.spir).generateSymbol(bin_file, src, typed_value, code, dbg_line),
102 //.spir64 => return Function(.spir64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
103 //.kalimba => return Function(.kalimba).generateSymbol(bin_file, src, typed_value, code, dbg_line),
104 //.shave => return Function(.shave).generateSymbol(bin_file, src, typed_value, code, dbg_line),
105 //.lanai => return Function(.lanai).generateSymbol(bin_file, src, typed_value, code, dbg_line),
106 //.wasm32 => return Function(.wasm32).generateSymbol(bin_file, src, typed_value, code, dbg_line),
107 //.wasm64 => return Function(.wasm64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
108 //.renderscript32 => return Function(.renderscript32).generateSymbol(bin_file, src, typed_value, code, dbg_line),
109 //.renderscript64 => return Function(.renderscript64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
110 //.ve => return Function(.ve).generateSymbol(bin_file, src, typed_value, code, dbg_line),
105111 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."),
106112 }
107113 },
......@@ -114,7 +120,7 @@ pub fn generateSymbol(
114120 switch (try generateSymbol(bin_file, src, .{
115121 .ty = typed_value.ty.elemType(),
116122 .val = sentinel,
117 }, code)) {
123 }, code, dbg_line)) {
118124 .appended => return Result{ .appended = {} },
119125 .externally_managed => |slice| {
120126 code.appendSliceAssumeCapacity(slice);
......@@ -206,6 +212,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
206212 target: *const std.Target,
207213 mod_fn: *const Module.Fn,
208214 code: *std.ArrayList(u8),
215 dbg_line: *std.ArrayList(u8),
209216 err_msg: ?*ErrorMsg,
210217 args: []MCValue,
211218 ret_mcv: MCValue,
......@@ -214,6 +221,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
214221 src: usize,
215222 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
217233 /// The value is an offset into the `Function` `code` from the beginning.
218234 /// To perform the reloc, write 32-bit signed little-endian integer
219235 /// 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 {
365381 src: usize,
366382 typed_value: TypedValue,
367383 code: *std.ArrayList(u8),
384 dbg_line: *std.ArrayList(u8),
368385 ) GenerateSymbolError!Result {
369386 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 {
379396 const branch = try branch_stack.addOne();
380397 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
382415 var function = Self{
383416 .gpa = bin_file.allocator,
384417 .target = &bin_file.base.options.target,
385418 .bin_file = bin_file,
386419 .mod_fn = module_fn,
387420 .code = code,
421 .dbg_line = dbg_line,
388422 .err_msg = null,
389423 .args = undefined, // populated after `resolveCallingConventionValues`
390424 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`
......@@ -393,6 +427,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
393427 .branch_stack = &branch_stack,
394428 .src = src,
395429 .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,
396434 };
397435 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
398436
......@@ -431,20 +469,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
431469 // TODO During semantic analysis, check if there are no function calls. If there
432470 // are none, here we can omit the part where we subtract and then add rsp.
433471 self.code.appendSliceAssumeCapacity(&[_]u8{
434 // push rbp
435 0x55,
436 // mov rbp, rsp
437 0x48,
438 0x89,
439 0xe5,
440 // sub rsp, imm32 (with reloc)
441 0x48,
442 0x81,
443 0xec,
472 0x55, // push rbp
473 0x48, 0x89, 0xe5, // mov rbp, rsp
474 0x48, 0x81, 0xec, // sub rsp, imm32 (with reloc)
444475 });
445476 const reloc_index = self.code.items.len;
446477 self.code.items.len += 4;
447478
479 try self.dbgSetPrologueEnd();
448480 try self.genBody(self.mod_fn.analysis.success);
449481
450482 const stack_end = self.branch_stack.items[0].max_end_stack;
......@@ -467,6 +499,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
467499 mem.writeIntLittle(i32, self.code.items[jmp_reloc..][0..4], s32_amt);
468500 }
469501
502 // Important to be after the possible self.code.items.len -= 5 above.
503 try self.dbgSetEpilogueBegin();
504
470505 try self.code.ensureCapacity(self.code.items.len + 9);
471506 // add rsp, x
472507 if (aligned_stack_end > math.maxInt(i8)) {
......@@ -485,13 +520,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
485520 0xc3, // ret
486521 });
487522 } else {
523 try self.dbgSetPrologueEnd();
488524 try self.genBody(self.mod_fn.analysis.success);
525 try self.dbgSetEpilogueBegin();
489526 }
490527 },
491528 else => {
529 try self.dbgSetPrologueEnd();
492530 try self.genBody(self.mod_fn.analysis.success);
531 try self.dbgSetEpilogueBegin();
493532 },
494533 }
534 // Drop them off at the rbrace.
535 try self.dbgAdvancePCAndLine(self.rbrace_src);
495536 }
496537
497538 fn genBody(self: *Self, body: ir.Body) InnerError!void {
......@@ -508,6 +549,38 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
508549 }
509550 }
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
511584 fn processDeath(self: *Self, inst: *ir.Inst) void {
512585 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
513586 const entry = branch.inst_table.getEntry(inst) orelse return;
......@@ -543,6 +616,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
543616 .cmp_neq => return self.genCmp(inst.castTag(.cmp_neq).?, .neq),
544617 .condbr => return self.genCondBr(inst.castTag(.condbr).?),
545618 .constant => unreachable, // excluded from function bodies
619 .dbg_stmt => return self.genDbgStmt(inst.castTag(.dbg_stmt).?),
546620 .floatcast => return self.genFloatCast(inst.castTag(.floatcast).?),
547621 .intcast => return self.genIntCast(inst.castTag(.intcast).?),
548622 .isnonnull => return self.genIsNonNull(inst.castTag(.isnonnull).?),
......@@ -1106,6 +1180,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
11061180 }
11071181 }
11081182
1183 fn genDbgStmt(self: *Self, inst: *ir.Inst.NoOp) !MCValue {
1184 try self.dbgAdvancePCAndLine(inst.base.src);
1185 return MCValue.none;
1186 }
1187
11091188 fn genCondBr(self: *Self, inst: *ir.Inst.CondBr) !MCValue {
11101189 switch (arch) {
11111190 .x86_64 => {
src-self-hosted/codegen/c.zig+11-5
......@@ -89,17 +89,17 @@ fn genFn(file: *C, decl: *Decl) !void {
8989 const func: *Module.Fn = tv.val.cast(Value.Payload.Function).?.func;
9090 const instructions = func.analysis.success.instructions;
9191 if (instructions.len > 0) {
92 try writer.writeAll("\n");
9293 for (instructions) |inst| {
93 try writer.writeAll("\n ");
9494 switch (inst.tag) {
9595 .assembly => try genAsm(file, inst.castTag(.assembly).?, decl),
9696 .call => try genCall(file, inst.castTag(.call).?, decl),
9797 .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),
99100 else => |e| return file.fail(decl.src(), "TODO implement C codegen for {}", .{e}),
100101 }
101102 }
102 try writer.writeAll("\n");
103103 }
104104
105105 try writer.writeAll("}\n\n");
......@@ -112,6 +112,7 @@ fn genRet(file: *C, inst: *Inst.UnOp, decl: *Decl, expected_return_type: Type) !
112112fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {
113113 const writer = file.main.writer();
114114 const header = file.header.writer();
115 try writer.writeAll(" ");
115116 if (inst.func.castTag(.constant)) |func_inst| {
116117 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
117118 const target = func_val.func.owner_decl;
......@@ -126,7 +127,7 @@ fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {
126127 try renderFunctionSignature(file, header, target);
127128 try header.writeAll(";\n");
128129 }
129 try writer.print("{}();", .{tname});
130 try writer.print("{}();\n", .{tname});
130131 } else {
131132 return file.fail(decl.src(), "TODO non-function call target?", .{});
132133 }
......@@ -138,8 +139,13 @@ fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {
138139 }
139140}
140141
142fn genDbgStmt(file: *C, inst: *Inst.NoOp, decl: *Decl) !void {
143 // TODO emit #line directive here with line number and filename
144}
145
141146fn genAsm(file: *C, as: *Inst.Assembly, decl: *Decl) !void {
142147 const writer = file.main.writer();
148 try writer.writeAll(" ");
143149 for (as.inputs) |i, index| {
144150 if (i[0] == '{' and i[i.len - 1] == '}') {
145151 const reg = i[1 .. i.len - 1];
......@@ -187,5 +193,5 @@ fn genAsm(file: *C, as: *Inst.Assembly, decl: *Decl) !void {
187193 }
188194 }
189195 }
190 try writer.writeAll(");");
196 try writer.writeAll(");\n");
191197}
src-self-hosted/ir.zig+2
......@@ -65,6 +65,7 @@ pub const Inst = struct {
6565 cmp_neq,
6666 condbr,
6767 constant,
68 dbg_stmt,
6869 isnonnull,
6970 isnull,
7071 /// Read a value from a pointer.
......@@ -88,6 +89,7 @@ pub const Inst = struct {
8889 .unreach,
8990 .arg,
9091 .breakpoint,
92 .dbg_stmt,
9193 => NoOp,
9294
9395 .ref,
src-self-hosted/link.zig+521-66
......@@ -11,6 +11,9 @@ const c_codegen = @import("codegen/c.zig");
1111const log = std.log;
1212const DW = std.dwarf;
1313const trace = @import("tracy.zig").trace;
14const leb128 = std.debug.leb;
15const Package = @import("Package.zig");
16const Value = @import("value.zig").Value;
1417
1518// TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented.
1619// zig fmt: off
......@@ -24,7 +27,7 @@ pub const Options = struct {
2427 object_format: std.builtin.ObjectFormat,
2528 optimize_mode: std.builtin.Mode,
2629 root_name: []const u8,
27 root_src_dir_path: []const u8,
30 root_pkg: *const Package,
2831 /// Used for calculating how much space to reserve for symbols in case the binary file
2932 /// does not already have a symbol table.
3033 symbol_count_hint: u64 = 32,
......@@ -82,6 +85,13 @@ pub const File = struct {
8285 }
8386 }
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
8595 pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void {
8696 switch (base.tag) {
8797 .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),
......@@ -200,7 +210,7 @@ pub const File = struct {
200210
201211 pub fn fail(self: *C, src: usize, comptime format: []const u8, args: anytype) !void {
202212 self.error_msg = try Module.ErrorMsg.create(self.allocator, src, format, args);
203 return error.CGenFailure;
213 return error.AnalysisFail;
204214 }
205215
206216 pub fn deinit(self: *File.C) void {
......@@ -214,7 +224,7 @@ pub const File = struct {
214224
215225 pub fn updateDecl(self: *File.C, module: *Module, decl: *Module.Decl) !void {
216226 c_codegen.generate(self, decl) catch |err| {
217 if (err == error.CGenFailure) {
227 if (err == error.AnalysisFail) {
218228 try module.failed_decls.put(module.gpa, decl, self.error_msg);
219229 }
220230 return err;
......@@ -291,6 +301,7 @@ pub const File = struct {
291301 debug_abbrev_section_index: ?u16 = null,
292302 debug_str_section_index: ?u16 = null,
293303 debug_aranges_section_index: ?u16 = null,
304 debug_line_section_index: ?u16 = null,
294305
295306 debug_abbrev_table_offset: ?u64 = null,
296307
......@@ -318,6 +329,7 @@ pub const File = struct {
318329 debug_info_section_dirty: bool = false,
319330 debug_abbrev_section_dirty: bool = false,
320331 debug_aranges_section_dirty: bool = false,
332 debug_line_header_dirty: bool = false,
321333
322334 error_flags: ErrorFlags = ErrorFlags{},
323335
......@@ -339,6 +351,12 @@ pub const File = struct {
339351 text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = std.ArrayListUnmanaged(*TextBlock){},
340352 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
342360 /// `alloc_num / alloc_den` is the factor of padding when allocating.
343361 const alloc_num = 4;
344362 const alloc_den = 3;
......@@ -402,6 +420,26 @@ pub const File = struct {
402420 sym_index: ?u32 = null,
403421 };
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
405443 pub fn openPath(allocator: *Allocator, dir: fs.Dir, sub_path: []const u8, options: Options) !*File {
406444 assert(options.object_format == .elf);
407445
......@@ -514,6 +552,7 @@ pub const File = struct {
514552 self.local_symbol_free_list.deinit(self.allocator);
515553 self.offset_table_free_list.deinit(self.allocator);
516554 self.text_block_free_list.deinit(self.allocator);
555 self.dbg_line_fn_free_list.deinit(self.allocator);
517556 self.offset_table.deinit(self.allocator);
518557 if (self.owns_file_handle) {
519558 if (self.file) |f| f.close();
......@@ -538,6 +577,14 @@ pub const File = struct {
538577 });
539578 }
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
541588 /// Returns end pos of collision, if any.
542589 fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
543590 const small_ptr = self.base.options.target.cpu.arch.ptrBitWidth() == 32;
......@@ -585,6 +632,8 @@ pub const File = struct {
585632 }
586633
587634 fn allocatedSize(self: *Elf, start: u64) u64 {
635 if (start == 0)
636 return 0;
588637 var min_pos: u64 = std.math.maxInt(u64);
589638 if (self.shdr_table_offset) |off| {
590639 if (off > start and off < min_pos) min_pos = off;
......@@ -611,6 +660,7 @@ pub const File = struct {
611660 return start;
612661 }
613662
663 /// TODO Improve this to use a table.
614664 fn makeString(self: *Elf, bytes: []const u8) !u32 {
615665 try self.shstrtab.ensureCapacity(self.allocator, self.shstrtab.items.len + bytes.len + 1);
616666 const result = self.shstrtab.items.len;
......@@ -619,6 +669,7 @@ pub const File = struct {
619669 return @intCast(u32, result);
620670 }
621671
672 /// TODO Improve this to use a table.
622673 fn makeDebugString(self: *Elf, bytes: []const u8) !u32 {
623674 try self.debug_strtab.ensureCapacity(self.allocator, self.debug_strtab.items.len + bytes.len + 1);
624675 const result = self.debug_strtab.items.len;
......@@ -645,10 +696,7 @@ pub const File = struct {
645696 .p32 => true,
646697 .p64 => false,
647698 };
648 const ptr_size: u8 = switch (self.ptr_width) {
649 .p32 => 4,
650 .p64 => 8,
651 };
699 const ptr_size: u8 = self.ptrWidthBytes();
652700 if (self.phdr_load_re_index == null) {
653701 self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len);
654702 const file_size = self.base.options.program_code_size_hint;
......@@ -713,27 +761,6 @@ pub const File = struct {
713761 self.shstrtab_dirty = true;
714762 self.shdr_table_dirty = true;
715763 }
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 }
737764 if (self.text_section_index == null) {
738765 self.text_section_index = @intCast(u16, self.sections.items.len);
739766 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
......@@ -794,6 +821,24 @@ pub const File = struct {
794821 self.shdr_table_dirty = true;
795822 try self.writeSymbol(0);
796823 }
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 }
797842 if (self.debug_info_section_index == null) {
798843 self.debug_info_section_index = @intCast(u16, self.sections.items.len);
799844
......@@ -869,6 +914,31 @@ pub const File = struct {
869914 self.shdr_table_dirty = true;
870915 self.debug_aranges_section_dirty = true;
871916 }
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 }
872942 const shsize: u64 = switch (self.ptr_width) {
873943 .p32 => @sizeOf(elf.Elf32_Shdr),
874944 .p64 => @sizeOf(elf.Elf64_Shdr),
......@@ -906,9 +976,10 @@ pub const File = struct {
906976 pub fn flush(self: *Elf) !void {
907977 const target_endian = self.base.options.target.cpu.arch.endian();
908978 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) {
910981 .p32 => 4,
911 .p64 => 8,
982 .p64 => 12,
912983 };
913984
914985 // Unfortunately these have to be buffered and done at the end because ELF does not allow
......@@ -922,7 +993,7 @@ pub const File = struct {
922993 // we can simply append these bytes.
923994 const abbrev_buf = [_]u8{
924995 1, DW.TAG_compile_unit, DW.CHILDREN_no, // header
925 //DW.AT_stmt_list, DW.FORM_data4, TODO
996 DW.AT_stmt_list, DW.FORM_sec_offset,
926997 DW.AT_low_pc , DW.FORM_addr,
927998 DW.AT_high_pc , DW.FORM_addr,
928999 DW.AT_name , DW.FORM_strp,
......@@ -969,27 +1040,23 @@ pub const File = struct {
9691040 // not including the initial length itself.
9701041 // We have to come back and write it later after we know the size.
9711042 const init_len_index = di_buf.items.len;
972 switch (self.ptr_width) {
973 .p32 => di_buf.items.len += 4,
974 .p64 => di_buf.items.len += 12,
975 }
1043 di_buf.items.len += init_len_size;
9761044 const after_init_len = di_buf.items.len;
977 mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 5, target_endian); // DWARF version
978 di_buf.appendAssumeCapacity(DW.UT_compile);
1045 mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 4, target_endian); // DWARF version
9791046 const abbrev_offset = self.debug_abbrev_table_offset.?;
9801047 switch (self.ptr_width) {
9811048 .p32 => {
982 di_buf.appendAssumeCapacity(4); // address size
9831049 mem.writeInt(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, abbrev_offset), target_endian);
1050 di_buf.appendAssumeCapacity(4); // address size
9841051 },
9851052 .p64 => {
986 di_buf.appendAssumeCapacity(8); // address size
9871053 mem.writeInt(u64, di_buf.addManyAsArrayAssumeCapacity(8), abbrev_offset, target_endian);
1054 di_buf.appendAssumeCapacity(8); // address size
9881055 },
9891056 }
9901057 // 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);
992 const comp_dir_strp = try self.makeDebugString(self.base.options.root_src_dir_path);
1058 const name_strp = try self.makeDebugString(self.base.options.root_pkg.root_src_path);
1059 const comp_dir_strp = try self.makeDebugString(self.base.options.root_pkg.root_src_dir_path);
9931060 const producer_strp = try self.makeDebugString("zig (TODO version here)");
9941061 // Currently only one compilation unit is supported, so the address range is simply
9951062 // identical to the main program header virtual address and memory size.
......@@ -998,7 +1065,7 @@ pub const File = struct {
9981065 const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz;
9991066
10001067 di_buf.appendAssumeCapacity(1); // abbrev tag, matching the value from the abbrev table header
1001 //DW.AT_stmt_list, DW.FORM_data4, TODO line information
1068 self.writeDwarfAddrAssumeCapacity(&di_buf, 0); // DW.AT_stmt_list, DW.FORM_sec_offset
10021069 self.writeDwarfAddrAssumeCapacity(&di_buf, low_pc);
10031070 self.writeDwarfAddrAssumeCapacity(&di_buf, high_pc);
10041071 self.writeDwarfAddrAssumeCapacity(&di_buf, name_strp);
......@@ -1056,10 +1123,7 @@ pub const File = struct {
10561123 // not including the initial length itself.
10571124 // We have to come back and write it later after we know the size.
10581125 const init_len_index = di_buf.items.len;
1059 switch (self.ptr_width) {
1060 .p32 => di_buf.items.len += 4,
1061 .p64 => di_buf.items.len += 12,
1062 }
1126 di_buf.items.len += init_len_size;
10631127 const after_init_len = di_buf.items.len;
10641128 mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 2, target_endian); // version
10651129 // When more than one compilation unit is supported, this will be the offset to it.
......@@ -1116,6 +1180,100 @@ pub const File = struct {
11161180
11171181 self.debug_aranges_section_dirty = false;
11181182 }
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
11201278 if (self.phdr_table_dirty) {
11211279 const phsize: u64 = switch (self.ptr_width) {
......@@ -1174,7 +1332,7 @@ pub const File = struct {
11741332 shstrtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);
11751333 }
11761334 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
11791337 try self.file.?.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset);
11801338 if (!self.shdr_table_dirty) {
......@@ -1263,6 +1421,7 @@ pub const File = struct {
12631421 assert(!self.debug_info_section_dirty);
12641422 assert(!self.debug_abbrev_section_dirty);
12651423 assert(!self.debug_aranges_section_dirty);
1424 assert(!self.debug_line_header_dirty);
12661425 assert(!self.phdr_table_dirty);
12671426 assert(!self.shdr_table_dirty);
12681427 assert(!self.shstrtab_dirty);
......@@ -1580,11 +1739,8 @@ pub const File = struct {
15801739 pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void {
15811740 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.
15841742 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);
15861743 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
15891745 if (self.local_symbol_free_list.popOrNull()) |i| {
15901746 log.debug(.link, "reusing symbol index {} for {}\n", .{ i, decl.name });
......@@ -1617,15 +1773,37 @@ pub const File = struct {
16171773 }
16181774
16191775 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.
16201777 self.freeTextBlock(&decl.link);
16211778 if (decl.link.local_sym_index != 0) {
1622 self.local_symbol_free_list.appendAssumeCapacity(decl.link.local_sym_index);
1623 self.offset_table_free_list.appendAssumeCapacity(decl.link.offset_table_index);
1779 self.local_symbol_free_list.append(self.allocator, decl.link.local_sym_index) catch {};
1780 self.offset_table_free_list.append(self.allocator, decl.link.offset_table_index) catch {};
16241781
16251782 self.local_symbols.items[decl.link.local_sym_index].st_info = 0;
16261783
16271784 decl.link.local_sym_index = 0;
16281785 }
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 }
16291807 }
16301808
16311809 pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
......@@ -1635,8 +1813,67 @@ pub const File = struct {
16351813 var code_buffer = std.ArrayList(u8).init(self.allocator);
16361814 defer code_buffer.deinit();
16371815
1816 var dbg_line_buffer = std.ArrayList(u8).init(self.allocator);
1817 defer dbg_line_buffer.deinit();
1818
16381819 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) {
16401877 .externally_managed => |x| x,
16411878 .appended => code_buffer.items,
16421879 .fail => |em| {
......@@ -1648,10 +1885,7 @@ pub const File = struct {
16481885
16491886 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
16501887
1651 const stt_bits: u8 = switch (typed_value.ty.zigTypeTag()) {
1652 .Fn => elf.STT_FUNC,
1653 else => elf.STT_OBJECT,
1654 };
1888 const stt_bits: u8 = if (is_fn) elf.STT_FUNC else elf.STT_OBJECT;
16551889
16561890 assert(decl.link.local_sym_index != 0); // Caller forgot to allocateDeclIndexes()
16571891 const local_sym = &self.local_symbols.items[decl.link.local_sym_index];
......@@ -1704,6 +1938,94 @@ pub const File = struct {
17041938 const file_offset = self.sections.items[self.text_section_index.?].sh_offset + section_offset;
17051939 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
17072029 // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
17082030 const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
17092031 return self.updateDeclExports(module, decl, decl_exports);
......@@ -1719,10 +2041,7 @@ pub const File = struct {
17192041 const tracy = trace(@src());
17202042 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.
17242044 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);
17262045 const typed_value = decl.typed_value.most_recent.typed_value;
17272046 if (decl.link.local_sym_index == 0) return;
17282047 const decl_sym = self.local_symbols.items[decl.link.local_sym_index];
......@@ -1787,9 +2106,31 @@ pub const File = struct {
17872106 }
17882107 }
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
17902131 pub fn deleteExport(self: *Elf, exp: Export) void {
17912132 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 {};
17932134 self.global_symbols.items[sym_index].st_info = 0;
17942135 }
17952136
......@@ -1817,7 +2158,6 @@ pub const File = struct {
18172158
18182159 fn writeSectHeader(self: *Elf, index: usize) !void {
18192160 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;
18212161 switch (self.base.options.target.cpu.arch.ptrBitWidth()) {
18222162 32 => {
18232163 var shdr: [1]elf.Elf32_Shdr = undefined;
......@@ -1825,6 +2165,7 @@ pub const File = struct {
18252165 if (foreign_endian) {
18262166 bswapAllFields(elf.Elf32_Shdr, &shdr[0]);
18272167 }
2168 const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf32_Shdr);
18282169 return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
18292170 },
18302171 64 => {
......@@ -1832,6 +2173,7 @@ pub const File = struct {
18322173 if (foreign_endian) {
18332174 bswapAllFields(elf.Elf64_Shdr, &shdr[0]);
18342175 }
2176 const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf64_Shdr);
18352177 return self.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
18362178 },
18372179 else => return error.UnsupportedArchitecture,
......@@ -1841,10 +2183,7 @@ pub const File = struct {
18412183 fn writeOffsetTableEntry(self: *Elf, index: usize) !void {
18422184 const shdr = &self.sections.items[self.got_section_index.?];
18432185 const phdr = &self.program_headers.items[self.phdr_got_index.?];
1844 const entry_size: u16 = switch (self.ptr_width) {
1845 .p32 => 4,
1846 .p64 => 8,
1847 };
2186 const entry_size: u16 = self.ptrWidthBytes();
18482187 if (self.offset_table_count_dirty) {
18492188 // TODO Also detect virtual address collisions.
18502189 const allocated_size = self.allocatedSize(shdr.sh_offset);
......@@ -1987,6 +2326,122 @@ pub const File = struct {
19872326 },
19882327 }
19892328 }
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
19902445 };
19912446};
19922447
src-self-hosted/main.zig+9-13
......@@ -10,9 +10,7 @@ const Module = @import("Module.zig");
1010const link = @import("link.zig");
1111const Package = @import("Package.zig");
1212const zir = @import("zir.zig");
13
14// TODO Improve async I/O enough that we feel comfortable doing this.
15//pub const io_mode = .evented;
13const build_options = @import("build_options");
1614
1715pub const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB
1816
......@@ -47,18 +45,16 @@ pub fn log(
4745 if (@enumToInt(level) > @enumToInt(std.log.level))
4846 return;
4947
50 const scope_prefix = "(" ++ switch (scope) {
51 // Uncomment to hide logs
52 //.compiler,
53 .module,
54 .liveness,
55 .link,
56 => return,
48 const scope_name = @tagName(scope);
49 const ok = comptime for (build_options.log_scopes) |log_scope| {
50 if (mem.eql(u8, log_scope, scope_name))
51 break true;
52 } else false;
5753
58 else => @tagName(scope),
59 } ++ "): ";
54 if (!ok)
55 return;
6056
61 const prefix = "[" ++ @tagName(level) ++ "] " ++ scope_prefix;
57 const prefix = "[" ++ @tagName(level) ++ "] " ++ "(" ++ @tagName(scope) ++ "): ";
6258
6359 // Print the message to stderr, silently ignoring any errors
6460 std.debug.print(prefix ++ format, args);
src-self-hosted/zir.zig+5
......@@ -107,6 +107,8 @@ pub const Inst = struct {
107107 condbr,
108108 /// Special case, has no textual representation.
109109 @"const",
110 /// Declares the beginning of a statement. Used for debug info.
111 dbg_stmt,
110112 /// Represents a pointer to a global decl by name.
111113 declref,
112114 /// Represents a pointer to a global decl by string name.
......@@ -211,6 +213,7 @@ pub const Inst = struct {
211213 return switch (tag) {
212214 .arg,
213215 .breakpoint,
216 .dbg_stmt,
214217 .returnvoid,
215218 .alloc_inferred,
216219 .ret_ptr,
......@@ -324,6 +327,7 @@ pub const Inst = struct {
324327 .coerce_result_block_ptr,
325328 .coerce_to_ptr_elem,
326329 .@"const",
330 .dbg_stmt,
327331 .declref,
328332 .declref_str,
329333 .declval,
......@@ -1843,6 +1847,7 @@ const EmitZIR = struct {
18431847 .breakpoint => try self.emitNoOp(inst.src, .breakpoint),
18441848 .unreach => try self.emitNoOp(inst.src, .@"unreachable"),
18451849 .retvoid => try self.emitNoOp(inst.src, .returnvoid),
1850 .dbg_stmt => try self.emitNoOp(inst.src, .dbg_stmt),
18461851
18471852 .not => try self.emitUnOp(inst.src, new_body, inst.castTag(.not).?, .boolnot),
18481853 .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!
4141 .coerce_to_ptr_elem => return analyzeInstCoerceToPtrElem(mod, scope, old_inst.castTag(.coerce_to_ptr_elem).?),
4242 .compileerror => return analyzeInstCompileError(mod, scope, old_inst.castTag(.compileerror).?),
4343 .@"const" => return analyzeInstConst(mod, scope, old_inst.castTag(.@"const").?),
44 .dbg_stmt => return analyzeInstDbgStmt(mod, scope, old_inst.castTag(.dbg_stmt).?),
4445 .declref => return analyzeInstDeclRef(mod, scope, old_inst.castTag(.declref).?),
4546 .declref_str => return analyzeInstDeclRefStr(mod, scope, old_inst.castTag(.declref_str).?),
4647 .declval => return analyzeInstDeclVal(mod, scope, old_inst.castTag(.declval).?),
......@@ -487,6 +488,11 @@ fn analyzeInstBreakVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.BreakVoid)
487488 return analyzeBreak(mod, scope, inst.base.src, block, void_inst);
488489}
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
490496fn analyzeInstDeclRefStr(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclRefStr) InnerError!*Inst {
491497 const decl_name = try resolveConstString(mod, scope, inst.positionals.name);
492498 return mod.analyzeDeclRefByName(scope, inst.base.src, decl_name);