authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2026-06-16 00:22:17-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2026-06-23 00:27:18-04:00
log411e5099e5fb8a8e1b39e0bcd0098e7a2c162aec
treeaf62de07a424afd191244361f556ea1d9bc8944b
parentc9601664c6914a8bdbf778f494a0a4fe6d84fa53

link: remove debug_link_snapshots in favour of enable_debug_extensions

MappedFile: use opts struct for realign Coff: fixup not reserving enough string capacity in loadObject

7 files changed, 40 insertions(+), 38 deletions(-)

build.zig-3
...@@ -256,7 +256,6 @@ pub fn build(b: *std.Build) !void {...@@ -256,7 +256,6 @@ pub fn build(b: *std.Build) !void {
256 const is_debug = optimize == .Debug;256 const is_debug = optimize == .Debug;
257 const enable_debug_extensions = b.option(bool, "debug-extensions", "Enable commands and options useful for debugging the compiler") orelse is_debug;257 const enable_debug_extensions = b.option(bool, "debug-extensions", "Enable commands and options useful for debugging the compiler") orelse is_debug;
258 const enable_logging = b.option(bool, "log", "Enable debug logging with --debug-log") orelse is_debug;258 const enable_logging = b.option(bool, "log", "Enable debug logging with --debug-log") orelse is_debug;
259 const enable_link_snapshots = b.option(bool, "link-snapshot", "Whether to enable linker state snapshots") orelse false;
260259
261 const opt_version_string = b.option([]const u8, "version-string", "Override Zig version string. Default is to find out with git.");260 const opt_version_string = b.option([]const u8, "version-string", "Override Zig version string. Default is to find out with git.");
262 const version_slice = if (opt_version_string) |version| version else v: {261 const version_slice = if (opt_version_string) |version| version else v: {
...@@ -372,7 +371,6 @@ pub fn build(b: *std.Build) !void {...@@ -372,7 +371,6 @@ pub fn build(b: *std.Build) !void {
372371
373 exe_options.addOption(bool, "enable_debug_extensions", enable_debug_extensions);372 exe_options.addOption(bool, "enable_debug_extensions", enable_debug_extensions);
374 exe_options.addOption(bool, "enable_logging", enable_logging);373 exe_options.addOption(bool, "enable_logging", enable_logging);
375 exe_options.addOption(bool, "enable_link_snapshots", enable_link_snapshots);
376 exe_options.addOption(bool, "enable_tracy", tracy != null);374 exe_options.addOption(bool, "enable_tracy", tracy != null);
377 exe_options.addOption(bool, "enable_tracy_callstack", tracy_callstack);375 exe_options.addOption(bool, "enable_tracy_callstack", tracy_callstack);
378 exe_options.addOption(bool, "enable_tracy_allocation", tracy_allocation);376 exe_options.addOption(bool, "enable_tracy_allocation", tracy_allocation);
...@@ -733,7 +731,6 @@ fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void {...@@ -733,7 +731,6 @@ fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void {
733 exe_options.addOption(std.SemanticVersion, "semver", semver);731 exe_options.addOption(std.SemanticVersion, "semver", semver);
734 exe_options.addOption(bool, "enable_debug_extensions", false);732 exe_options.addOption(bool, "enable_debug_extensions", false);
735 exe_options.addOption(bool, "enable_logging", false);733 exe_options.addOption(bool, "enable_logging", false);
736 exe_options.addOption(bool, "enable_link_snapshots", false);
737 exe_options.addOption(bool, "enable_tracy", false);734 exe_options.addOption(bool, "enable_tracy", false);
738 exe_options.addOption(bool, "enable_tracy_callstack", false);735 exe_options.addOption(bool, "enable_tracy_callstack", false);
739 exe_options.addOption(bool, "enable_tracy_allocation", false);736 exe_options.addOption(bool, "enable_tracy_allocation", false);
src/crash_report.zig+5-8
...@@ -132,14 +132,11 @@ fn dumpCrashContext() Io.Writer.Error!void {...@@ -132,14 +132,11 @@ fn dumpCrashContext() Io.Writer.Error!void {
132 try dumpCrashContextSema(anal, w, &S.crash_heap);132 try dumpCrashContextSema(anal, w, &S.crash_heap);
133 } else if (LinkerOp.current) |linker_op| {133 } else if (LinkerOp.current) |linker_op| {
134 try w.writeAll("Linker snapshot:\n");134 try w.writeAll("Linker snapshot:\n");
135 if (build_options.enable_link_snapshots) {135 switch (try linker_op.lf.dump(w, linker_op.tid)) {
136 switch (try linker_op.lf.dump(w, linker_op.tid)) {136 .unimplemented => try w.writeAll("(backend does not support link snapshots)"),
137 .unsupported => try w.writeAll("(backend does not support link snapshots)"),137 .needs_extensions => try w.writeAll("(build with -Ddebug-extensions to dump linker state)"),
138 .disabled => try w.writeAll("(run with --debug-link-snapshot to dump linker state)"),138 .disabled => try w.writeAll("(run with --debug-link-snapshot to dump linker state)"),
139 .enabled => {},139 .enabled => {},
140 }
141 } else {
142 try w.writeAll("(build with -Dlink-snapshot to dump linker state)");
143 }140 }
144 try w.writeAll("\n\n");141 try w.writeAll("\n\n");
145 } else {142 } else {
src/link.zig+4-3
...@@ -1091,13 +1091,14 @@ pub const File = struct {...@@ -1091,13 +1091,14 @@ pub const File = struct {
1091 }1091 }
10921092
1093 pub const DumpResult = enum {1093 pub const DumpResult = enum {
1094 unsupported,1094 unimplemented,
1095 needs_extensions,
1095 disabled,1096 disabled,
1096 enabled,1097 enabled,
1097 };1098 };
10981099
1099 pub fn dump(base: *File, w: *Io.Writer, tid: Zcu.PerThread.Id) !DumpResult {1100 pub fn dump(base: *File, w: *Io.Writer, tid: Zcu.PerThread.Id) !DumpResult {
1100 if (!build_options.enable_link_snapshots) unreachable;1101 if (!build_options.enable_debug_extensions) return .not_built;
1101 switch (base.tag) {1102 switch (base.tag) {
1102 .elf,1103 .elf,
1103 .macho,1104 .macho,
...@@ -1106,7 +1107,7 @@ pub const File = struct {...@@ -1106,7 +1107,7 @@ pub const File = struct {
1106 .spirv,1107 .spirv,
1107 .plan9,1108 .plan9,
1108 .lld,1109 .lld,
1109 => return .unsupported,1110 => return .unimplemented,
1110 inline else => |tag| {1111 inline else => |tag| {
1111 dev.check(tag.devFeature());1112 dev.check(tag.devFeature());
1112 return @as(*tag.Type(), @fieldParentPtr("base", base)).dump(w, tid);1113 return @as(*tag.Type(), @fieldParentPtr("base", base)).dump(w, tid);
src/link/Coff.zig+9-7
...@@ -22,6 +22,7 @@ const implib = @import("../libs/mingw/implib.zig");...@@ -22,6 +22,7 @@ const implib = @import("../libs/mingw/implib.zig");
22const Path = std.Build.Cache.Path;22const Path = std.Build.Cache.Path;
2323
24base: link.File,24base: link.File,
25options: link.File.OpenOptions,
25mf: MappedFile,26mf: MappedFile,
26nodes: std.MultiArrayList(Node),27nodes: std.MultiArrayList(Node),
27members: std.ArrayList(Member),28members: std.ArrayList(Member),
...@@ -82,7 +83,6 @@ synth_prog_node: std.Progress.Node,...@@ -82,7 +83,6 @@ synth_prog_node: std.Progress.Node,
82symbol_prog_node: std.Progress.Node,83symbol_prog_node: std.Progress.Node,
83member_prog_node: std.Progress.Node,84member_prog_node: std.Progress.Node,
84input_prog_node: std.Progress.Node,85input_prog_node: std.Progress.Node,
85dump_snapshot: bool,
8686
87pub const default_file_alignment: u16 = 0x200;87pub const default_file_alignment: u16 = 0x200;
88pub const default_size_of_stack_reserve: u32 = 0x1000000;88pub const default_size_of_stack_reserve: u32 = 0x1000000;
...@@ -1593,6 +1593,7 @@ fn create(...@@ -1593,6 +1593,7 @@ fn create(
1593 .allow_shlib_undefined = false,1593 .allow_shlib_undefined = false,
1594 .stack_size = 0,1594 .stack_size = 0,
1595 },1595 },
1596 .options = options,
1596 .mf = try .init(file, comp.gpa, io),1597 .mf = try .init(file, comp.gpa, io),
1597 .nodes = .empty,1598 .nodes = .empty,
1598 .members = .empty,1599 .members = .empty,
...@@ -1664,7 +1665,6 @@ fn create(...@@ -1664,7 +1665,6 @@ fn create(
1664 .symbol_prog_node = .none,1665 .symbol_prog_node = .none,
1665 .member_prog_node = .none,1666 .member_prog_node = .none,
1666 .input_prog_node = .none,1667 .input_prog_node = .none,
1667 .dump_snapshot = options.enable_link_snapshots,
1668 };1668 };
1669 errdefer coff.deinit();1669 errdefer coff.deinit();
16701670
...@@ -3579,13 +3579,13 @@ fn objectSectionMapIndex(...@@ -3579,13 +3579,13 @@ fn objectSectionMapIndex(
3579 const parent_alignment = parent_ni.alignment(&coff.mf);3579 const parent_alignment = parent_ni.alignment(&coff.mf);
3580 if (alignment.compare(.gt, parent_alignment)) {3580 if (alignment.compare(.gt, parent_alignment)) {
3581 log.debug("realignParent({s}, {d}) {d}->{d}", .{ name.toSlice(coff), parent_ni, parent_alignment, alignment });3581 log.debug("realignParent({s}, {d}) {d}->{d}", .{ name.toSlice(coff), parent_ni, parent_alignment, alignment });
3582 try parent_ni.realign(&coff.mf, gpa, alignment, true);3582 try parent_ni.realign(&coff.mf, gpa, alignment, .{ .set_alignment = true });
3583 }3583 }
35843584
3585 const old_alignment = sym.ni.alignment(&coff.mf);3585 const old_alignment = sym.ni.alignment(&coff.mf);
3586 if (alignment.compare(.gt, old_alignment)) {3586 if (alignment.compare(.gt, old_alignment)) {
3587 log.debug("realignObject({s}) {d}->{d}", .{ name.toSlice(coff), old_alignment, alignment });3587 log.debug("realignObject({s}) {d}->{d}", .{ name.toSlice(coff), old_alignment, alignment });
3588 try sym.ni.realign(&coff.mf, gpa, alignment, true);3588 try sym.ni.realign(&coff.mf, gpa, alignment, .{ .set_alignment = true });
3589 }3589 }
35903590
3591 try coff.verifyParentSectionAttributes(3591 try coff.verifyParentSectionAttributes(
...@@ -3980,7 +3980,9 @@ fn loadObject(...@@ -3980,7 +3980,9 @@ fn loadObject(
39803980
3981 try coff.ensureManyUnusedStringCapacity(3981 try coff.ensureManyUnusedStringCapacity(
3982 header.number_of_sections + header.number_of_symbols,3982 header.number_of_sections + header.number_of_symbols,
3983 string_table_len - @sizeOf(u32),3983 header.number_of_sections * 9 +
3984 header.number_of_symbols * 9 +
3985 string_table_len - @sizeOf(u32),
3984 );3986 );
39853987
3986 const PendingSymbolIndex = enum(u32) {3988 const PendingSymbolIndex = enum(u32) {
...@@ -5886,7 +5888,7 @@ pub fn flush(...@@ -5886,7 +5888,7 @@ pub fn flush(
5886 else => |e| return comp.link_diags.fail("flush write failed: {t}", .{e}),5888 else => |e| return comp.link_diags.fail("flush write failed: {t}", .{e}),
5887 };5889 };
58885890
5889 if (coff.dump_snapshot)5891 if (coff.options.enable_link_snapshots)
5890 coff.dumpStderr(tid) catch |err|5892 coff.dumpStderr(tid) catch |err|
5891 return comp.link_diags.fail("dumping link snapshot failed: {t}", .{err});5893 return comp.link_diags.fail("dumping link snapshot failed: {t}", .{err});
5892}5894}
...@@ -7534,7 +7536,7 @@ fn dumpStderr(coff: *Coff, tid: Zcu.PerThread.Id) !void {...@@ -7534,7 +7536,7 @@ fn dumpStderr(coff: *Coff, tid: Zcu.PerThread.Id) !void {
7534}7536}
75357537
7536pub fn dump(coff: *Coff, w: *Io.Writer, tid: Zcu.PerThread.Id) !link.File.DumpResult {7538pub fn dump(coff: *Coff, w: *Io.Writer, tid: Zcu.PerThread.Id) !link.File.DumpResult {
7537 if (coff.dump_snapshot) {7539 if (coff.options.enable_link_snapshots) {
7538 try coff.printNode(tid, w, .root, 0);7540 try coff.printNode(tid, w, .root, 0);
7539 try w.writeAll("Section table:\n");7541 try w.writeAll("Section table:\n");
7540 for (coff.section_table.keys(), coff.section_table.values()) |name, sec|7542 for (coff.section_table.keys(), coff.section_table.values()) |name, sec|
src/link/Elf2.zig+4-6
...@@ -161,7 +161,6 @@ textrel_count: u32,...@@ -161,7 +161,6 @@ textrel_count: u32,
161const_prog_node: std.Progress.Node,161const_prog_node: std.Progress.Node,
162synth_prog_node: std.Progress.Node,162synth_prog_node: std.Progress.Node,
163input_prog_node: std.Progress.Node,163input_prog_node: std.Progress.Node,
164dump_snapshot: bool,
165164
166const Error = link.Error || error{MappedFileIo};165const Error = link.Error || error{MappedFileIo};
167166
...@@ -2625,7 +2624,6 @@ fn create(...@@ -2625,7 +2624,6 @@ fn create(
2625 .synth_prog_node = .none,2624 .synth_prog_node = .none,
2626 .input_prog_node = .none,2625 .input_prog_node = .none,
2627 .textrel_count = 0,2626 .textrel_count = 0,
2628 .dump_snapshot = options.enable_link_snapshots,
2629 };2627 };
2630 errdefer elf.deinit();2628 errdefer elf.deinit();
26312629
...@@ -3787,7 +3785,7 @@ fn mapInputSection(elf: *Elf, opts: struct {...@@ -3787,7 +3785,7 @@ fn mapInputSection(elf: *Elf, opts: struct {
3787 const new_alignment: std.mem.Alignment = .fromByteUnits(3785 const new_alignment: std.mem.Alignment = .fromByteUnits(
3788 std.math.ceilPowerOfTwoAssert(usize, @intCast(opts.addralign)),3786 std.math.ceilPowerOfTwoAssert(usize, @intCast(opts.addralign)),
3789 );3787 );
3790 try existing_shndx.get(elf).ni.realign(&elf.mf, gpa, new_alignment, true);3788 try existing_shndx.get(elf).ni.realign(&elf.mf, gpa, new_alignment, .{ .set_alignment = true });
3791 }3789 }
3792 // ...and update the shdr as needed.3790 // ...and update the shdr as needed.
3793 switch (elf.shdrPtr(existing_shndx)) {3791 switch (elf.shdrPtr(existing_shndx)) {
...@@ -3950,7 +3948,7 @@ fn uavMapIndex(...@@ -3950,7 +3948,7 @@ fn uavMapIndex(
3950 } else {3948 } else {
3951 const node = uav_gop.value_ptr.lsi.index().ptr(elf).node;3949 const node = uav_gop.value_ptr.lsi.index().ptr(elf).node;
3952 if (resolved_align.toStdMem().order(node.alignment(&elf.mf)).compare(.gt)) {3950 if (resolved_align.toStdMem().order(node.alignment(&elf.mf)).compare(.gt)) {
3953 try node.realign(&elf.mf, gpa, resolved_align.toStdMem(), true);3951 try node.realign(&elf.mf, gpa, resolved_align.toStdMem(), .{ .set_alignment = true });
3954 }3952 }
3955 }3953 }
3956 return umi;3954 return umi;
...@@ -4679,7 +4677,7 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadPars...@@ -4679,7 +4677,7 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadPars
4679 // We have a copy relocation for this global, but the amount of space we4677 // We have a copy relocation for this global, but the amount of space we
4680 // reserved for it could be too small or underaligned!4678 // reserved for it could be too small or underaligned!
4681 try copied_global.node.resize(&elf.mf, gpa, gop.value_ptr.size);4679 try copied_global.node.resize(&elf.mf, gpa, gop.value_ptr.size);
4682 try copied_global.node.realign(&elf.mf, gpa, gop.value_ptr.alignment, true);4680 try copied_global.node.realign(&elf.mf, gpa, gop.value_ptr.alignment, .{ .set_alignment = true });
4683 const global_ptr = elf.globalByName(name).?;4681 const global_ptr = elf.globalByName(name).?;
4684 switch (elf.symPtr(global_ptr.symtab_index)) {4682 switch (elf.symPtr(global_ptr.symtab_index)) {
4685 inline else => |sym_ptr| elf.targetStore(&sym_ptr.size, @intCast(gop.value_ptr.size)),4683 inline else => |sym_ptr| elf.targetStore(&sym_ptr.size, @intCast(gop.value_ptr.size)),
...@@ -6714,7 +6712,7 @@ pub fn deleteExport(elf: *Elf, exported: Zcu.Exported, name: InternPool.NullTerm...@@ -6714,7 +6712,7 @@ pub fn deleteExport(elf: *Elf, exported: Zcu.Exported, name: InternPool.NullTerm
6714}6712}
67156713
6716pub fn dump(elf: *Elf, w: *Io.Writer, tid: Zcu.PerThread.Id) !link.File.DumpResult {6714pub fn dump(elf: *Elf, w: *Io.Writer, tid: Zcu.PerThread.Id) !link.File.DumpResult {
6717 if (elf.dump_snapshot) {6715 if (elf.options.enable_link_snapshots) {
6718 try elf.printNode(tid, w, .root, 0);6716 try elf.printNode(tid, w, .root, 0);
6719 return .enabled;6717 return .enabled;
6720 }6718 }
src/link/MappedFile.zig+16-9
...@@ -354,18 +354,23 @@ pub const Node = extern struct {...@@ -354,18 +354,23 @@ pub const Node = extern struct {
354 }354 }
355 }355 }
356356
357 pub const RealignNodeOptions = struct {
358 /// Shift the node backwards if possible
359 try_backwards: bool = true,
360 /// If `set, persists `new_alignment` as the node's alignment for future operations.
361 set_alignment: bool = true,
362 };
363
357 /// Moves and expands a node such that its offset and size are aligned to `new_alignment`.364 /// Moves and expands a node such that its offset and size are aligned to `new_alignment`.
358 /// If it is possible to move the node backwards, this will be done instead of moving it forward.
359 /// If `set_alignment` is set, persists `new_alignment` as the node's alignment for future operations.
360 /// Asserts that `ni` is not `Node.Index.root`.365 /// Asserts that `ni` is not `Node.Index.root`.
361 pub fn realign(366 pub fn realign(
362 ni: Node.Index,367 ni: Node.Index,
363 mf: *MappedFile,368 mf: *MappedFile,
364 gpa: std.mem.Allocator,369 gpa: std.mem.Allocator,
365 new_alignment: std.mem.Alignment,370 new_alignment: std.mem.Alignment,
366 set_alignment: bool,371 opts: RealignNodeOptions,
367 ) Error!void {372 ) Error!void {
368 mf.realignNode(gpa, ni, new_alignment, true, set_alignment) catch |err| switch (err) {373 mf.realignNode(gpa, ni, new_alignment, opts) catch |err| switch (err) {
369 error.OutOfMemory,374 error.OutOfMemory,
370 error.Canceled,375 error.Canceled,
371 => |e| return e,376 => |e| return e,
...@@ -573,7 +578,10 @@ fn addNode(mf: *MappedFile, gpa: std.mem.Allocator, opts: struct {...@@ -573,7 +578,10 @@ fn addNode(mf: *MappedFile, gpa: std.mem.Allocator, opts: struct {
573 else => |next_ni| {578 else => |next_ni| {
574 const next_offset, _ = next_ni.location(mf).resolve(mf);579 const next_offset, _ = next_ni.location(mf).resolve(mf);
575 if (new_end > next_offset)580 if (new_end > next_offset)
576 try next_ni.realign(mf, gpa, opts.add_node.alignment, false);581 try next_ni.realign(mf, gpa, opts.add_node.alignment, .{
582 .try_backwards = false,
583 .set_alignment = false,
584 });
577 },585 },
578 }586 }
579 }587 }
...@@ -1035,8 +1043,7 @@ fn realignNode(...@@ -1035,8 +1043,7 @@ fn realignNode(
1035 gpa: std.mem.Allocator,1043 gpa: std.mem.Allocator,
1036 ni: Node.Index,1044 ni: Node.Index,
1037 new_alignment: std.mem.Alignment,1045 new_alignment: std.mem.Alignment,
1038 try_backward: bool,1046 opts: Node.Index.RealignNodeOptions,
1039 set_alignment: bool,
1040) (Allocator.Error || Io.Cancelable || IoError)!void {1047) (Allocator.Error || Io.Cancelable || IoError)!void {
1041 assert(ni != Node.Index.root); // currently unsupported1048 assert(ni != Node.Index.root); // currently unsupported
1042 mf.nodes_lock.assertUnlocked();1049 mf.nodes_lock.assertUnlocked();
...@@ -1052,7 +1059,7 @@ fn realignNode(...@@ -1052,7 +1059,7 @@ fn realignNode(
1052 node.flags.alignment = new_alignment;1059 node.flags.alignment = new_alignment;
1053 defer {1060 defer {
1054 // alignment needs to be temporarily set for the resizes below1061 // alignment needs to be temporarily set for the resizes below
1055 if (!set_alignment) node.flags.alignment = prev_alignment;1062 if (!opts.set_alignment) node.flags.alignment = prev_alignment;
1056 }1063 }
10571064
1058 const new_size = node.flags.alignment.forward(@intCast(size));1065 const new_size = node.flags.alignment.forward(@intCast(size));
...@@ -1070,7 +1077,7 @@ fn realignNode(...@@ -1070,7 +1077,7 @@ fn realignNode(
1070 },1077 },
1071 };1078 };
10721079
1073 if (try_backward) {1080 if (opts.try_backwards) {
1074 const backward_offset = new_alignment.backward(@intCast(old_offset));1081 const backward_offset = new_alignment.backward(@intCast(old_offset));
1075 const prev_end = if (node.prev == .none) 0 else prev: {1082 const prev_end = if (node.prev == .none) 0 else prev: {
1076 const prev_offset, const prev_size = node.prev.location(mf).resolve(mf);1083 const prev_offset, const prev_size = node.prev.location(mf).resolve(mf);
src/main.zig+2-2
...@@ -1440,8 +1440,8 @@ fn buildOutputType(...@@ -1440,8 +1440,8 @@ fn buildOutputType(
1440 dev.check(.stdio_listen);1440 dev.check(.stdio_listen);
1441 listen = .stdio;1441 listen = .stdio;
1442 } else if (mem.eql(u8, arg, "--debug-link-snapshot")) {1442 } else if (mem.eql(u8, arg, "--debug-link-snapshot")) {
1443 if (!build_options.enable_link_snapshots) {1443 if (!build_options.enable_debug_extensions) {
1444 warn("Zig was compiled without linker snapshots enabled (-Dlink-snapshot). --debug-link-snapshot has no effect.", .{});1444 warn("Zig was compiled without debug extensions. --debug-link-snapshot has no effect.", .{});
1445 } else {1445 } else {
1446 enable_link_snapshots = true;1446 enable_link_snapshots = true;
1447 }1447 }