authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2026-06-09 22:17:13-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2026-06-23 00:27:17-04:00
loga9b0999d5b8922580560fcb0f65f5860df78c8be
treecaf5a00873a434374c583e60696cbbb3e63c6050
parent38d06fdf0e9b9eb2831675b550e3ba7e41b74908

Coff: a few fixups for incremental

- Handle exports being updated - Track free relocs

1 files changed, 64 insertions(+), 27 deletions(-)

src/link/Coff.zig+64-27
...@@ -75,6 +75,8 @@ pending_uavs: std.array_hash_map.Auto(Node.UavMapIndex, struct {...@@ -75,6 +75,8 @@ pending_uavs: std.array_hash_map.Auto(Node.UavMapIndex, struct {
75 alignment: InternPool.Alignment,75 alignment: InternPool.Alignment,
76}),76}),
77relocs: std.ArrayList(Reloc),77relocs: std.ArrayList(Reloc),
78first_free_reloc: Reloc.Index,
79last_free_reloc: Reloc.Index,
78const_prog_node: std.Progress.Node,80const_prog_node: std.Progress.Node,
79synth_prog_node: std.Progress.Node,81synth_prog_node: std.Progress.Node,
80symbol_prog_node: std.Progress.Node,82symbol_prog_node: std.Progress.Node,
...@@ -1147,10 +1149,14 @@ pub const Reloc = extern struct {...@@ -1147,10 +1149,14 @@ pub const Reloc = extern struct {
1147 loc: Symbol.Index,1149 loc: Symbol.Index,
1148 target: Symbol.Index,1150 target: Symbol.Index,
1149 flags: packed struct(u8) {1151 flags: packed struct(u8) {
1150 // Indicates the addend is not known and should be recovered from the location itself.1152 /// Indicates the addend is not known and should be recovered from the location itself.
1151 // COFF relocation tables don't encode the addend, only the location.1153 /// COFF relocation tables don't encode the addend, only the location.
1152 recover_addend: bool,1154 recover_addend: bool,
1153 _: u7 = 0,1155 /// Set if this reloc is in the free list.
1156 /// When set, `prev` / `next` refer to other relocs in the free list.
1157 /// All other fields are undefined.
1158 free: bool,
1159 _: u6 = 0,
1154 },1160 },
11551161
1156 pub const Type = extern union {1162 pub const Type = extern union {
...@@ -1169,6 +1175,10 @@ pub const Reloc = extern struct {...@@ -1169,6 +1175,10 @@ pub const Reloc = extern struct {
1169 none = std.math.maxInt(u32),1175 none = std.math.maxInt(u32),
1170 _,1176 _,
11711177
1178 pub fn wrap(i: ?u32) Reloc.Index {
1179 return @enumFromInt((i orelse return .none) + 1);
1180 }
1181
1172 pub fn get(ri: Reloc.Index, coff: *Coff) *Reloc {1182 pub fn get(ri: Reloc.Index, coff: *Coff) *Reloc {
1173 return &coff.relocs.items[@intFromEnum(ri)];1183 return &coff.relocs.items[@intFromEnum(ri)];
1174 }1184 }
...@@ -1490,7 +1500,24 @@ pub const Reloc = extern struct {...@@ -1490,7 +1500,24 @@ pub const Reloc = extern struct {
1490 .none => {},1500 .none => {},
1491 else => |next| next.get(coff).prev = reloc.prev,1501 else => |next| next.get(coff).prev = reloc.prev,
1492 }1502 }
1503
1493 reloc.* = undefined;1504 reloc.* = undefined;
1505 reloc.flags = .{
1506 .recover_addend = false,
1507 .free = true,
1508 };
1509
1510 const ri: Reloc.Index = .wrap(@intCast(reloc - coff.relocs.items.ptr));
1511 if (coff.last_free_reloc == .none) {
1512 assert(coff.first_free_reloc == .none);
1513 coff.first_free_reloc = ri;
1514 coff.last_free_reloc = ri;
1515 } else {
1516 coff.last_free_reloc.get(coff).next = ri;
1517 reloc.prev = coff.last_free_reloc;
1518 reloc.next = .none;
1519 coff.last_free_reloc = ri;
1520 }
1494 }1521 }
14951522
1496 comptime {1523 comptime {
...@@ -1630,6 +1657,8 @@ fn create(...@@ -1630,6 +1657,8 @@ fn create(
1630 }),1657 }),
1631 .pending_uavs = .empty,1658 .pending_uavs = .empty,
1632 .relocs = .empty,1659 .relocs = .empty,
1660 .first_free_reloc = .none,
1661 .last_free_reloc = .none,
1633 .const_prog_node = .none,1662 .const_prog_node = .none,
1634 .synth_prog_node = .none,1663 .synth_prog_node = .none,
1635 .symbol_prog_node = .none,1664 .symbol_prog_node = .none,
...@@ -3624,6 +3653,9 @@ const RelocAddend = union(enum) {...@@ -3624,6 +3653,9 @@ const RelocAddend = union(enum) {
3624 pending: void,3653 pending: void,
3625};3654};
36263655
3656// TODO: There should be an API where the caller can indicate how many contiguous relocs they need
3657// and it should attempt to allocate these from from the free list if available. We can cache
3658// the run length of each segment on Reloc when `free` is set.
3627pub fn addReloc(3659pub fn addReloc(
3628 coff: *Coff,3660 coff: *Coff,
3629 loc_si: Symbol.Index,3661 loc_si: Symbol.Index,
...@@ -3747,6 +3779,7 @@ fn addRelocAssumeCapacity(...@@ -3747,6 +3779,7 @@ fn addRelocAssumeCapacity(
3747 .addend = if (addend == .pending) 0 else addend.known,3779 .addend = if (addend == .pending) 0 else addend.known,
3748 .flags = .{3780 .flags = .{
3749 .recover_addend = addend == .pending,3781 .recover_addend = addend == .pending,
3782 .free = false,
3750 },3783 },
3751 };3784 };
3752 switch (target.target_relocs) {3785 switch (target.target_relocs) {
...@@ -3756,17 +3789,6 @@ fn addRelocAssumeCapacity(...@@ -3756,17 +3789,6 @@ fn addRelocAssumeCapacity(
3756 target.target_relocs = ri;3789 target.target_relocs = ri;
3757}3790}
37583791
3759// pub fn loadInput(coff: *Coff, input: link.Input) link.Error!void {
3760// const diags = &coff.base.comp.link_diags;
3761// return coff.loadInputInner(input) catch |err| switch (err) {
3762// else => |e| return e,
3763// error.MappedFileIo => return diags.fail(
3764// "failed to write output file: {t}",
3765// .{coff.mf.io_err.?},
3766// ),
3767// };
3768// }
3769
3770fn failLoadInput(3792fn failLoadInput(
3771 coff: *Coff,3793 coff: *Coff,
3772 err: LoadInputError,3794 err: LoadInputError,
...@@ -5673,6 +5695,7 @@ fn reportUndefs(coff: *Coff, tid: Zcu.PerThread.Id) !void {...@@ -5673,6 +5695,7 @@ fn reportUndefs(coff: *Coff, tid: Zcu.PerThread.Id) !void {
56735695
5674 var undef_indices: std.ArrayListUnmanaged(u32) = .empty;5696 var undef_indices: std.ArrayListUnmanaged(u32) = .empty;
5675 for (coff.relocs.items, 0..) |reloc, reloc_i| {5697 for (coff.relocs.items, 0..) |reloc, reloc_i| {
5698 if (reloc.flags.free) continue;
5676 const target_sym = reloc.target.get(coff);5699 const target_sym = reloc.target.get(coff);
5677 switch (target_sym.ni) {5700 switch (target_sym.ni) {
5678 .none => {5701 .none => {
...@@ -7321,13 +7344,6 @@ fn updateExportsInner(...@@ -7321,13 +7344,6 @@ fn updateExportsInner(
7321 const gpa = zcu.gpa;7344 const gpa = zcu.gpa;
7322 const ip = &zcu.intern_pool;7345 const ip = &zcu.intern_pool;
73237346
7324 switch (exported) {
7325 .nav => |nav| log.debug("updateExports({f})", .{ip.getNav(nav).fqn.fmt(ip)}),
7326 .uav => |uav| log.debug("updateExports(@as({f}, {f}))", .{
7327 Type.fromInterned(ip.typeOf(uav)).fmt(pt),
7328 Value.fromInterned(uav).fmtValue(pt),
7329 }),
7330 }
7331 try coff.symbols.ensureUnusedCapacity(gpa, export_indices.len);7347 try coff.symbols.ensureUnusedCapacity(gpa, export_indices.len);
7332 const exported_si: Symbol.Index = switch (exported) {7348 const exported_si: Symbol.Index = switch (exported) {
7333 .nav => |nav| try coff.navSymbol(zcu, nav),7349 .nav => |nav| try coff.navSymbol(zcu, nav),
...@@ -7337,6 +7353,14 @@ fn updateExportsInner(...@@ -7337,6 +7353,14 @@ fn updateExportsInner(
7337 Type.fromInterned(ip.typeOf(uav)).abiAlignment(zcu),7353 Type.fromInterned(ip.typeOf(uav)).abiAlignment(zcu),
7338 ))),7354 ))),
7339 };7355 };
7356 switch (exported) {
7357 .nav => |nav| log.debug("updateExports({f}) = {d}", .{ ip.getNav(nav).fqn.fmt(ip), exported_si }),
7358 .uav => |uav| log.debug("updateExports(@as({f}, {f})) = {d}", .{
7359 Type.fromInterned(ip.typeOf(uav)).fmt(pt),
7360 Value.fromInterned(uav).fmtValue(pt),
7361 exported_si,
7362 }),
7363 }
7340 while (try coff.resolve(pt.tid)) {}7364 while (try coff.resolve(pt.tid)) {}
7341 while (try coff.idle(pt.tid)) {}7365 while (try coff.idle(pt.tid)) {}
73427366
...@@ -7349,7 +7373,7 @@ fn updateExportsInner(...@@ -7349,7 +7373,7 @@ fn updateExportsInner(
7349 const @"export" = export_index.ptr(zcu);7373 const @"export" = export_index.ptr(zcu);
7350 const name = @"export".opts.name.toSlice(ip);7374 const name = @"export".opts.name.toSlice(ip);
73517375
7352 // TODO: add an errMsg if this conflicts with an existing global7376 // TODO: add an errMsg if this conflicts with an existing symbol
7353 const export_si = try coff.globalSymbol(.{7377 const export_si = try coff.globalSymbol(.{
7354 .name = name,7378 .name = name,
7355 .lib_name = null,7379 .lib_name = null,
...@@ -7360,7 +7384,7 @@ fn updateExportsInner(...@@ -7360,7 +7384,7 @@ fn updateExportsInner(
7360 export_sym.section_number = exported_sym.section_number;7384 export_sym.section_number = exported_sym.section_number;
7361 if (@"export".opts.linkage == .weak and !coff.isImage()) {7385 if (@"export".opts.linkage == .weak and !coff.isImage()) {
7362 // exported_si needs to be ahead of export_si in the symbol table,7386 // exported_si needs to be ahead of export_si in the symbol table,
7363 // so that its sti is known when creating the aux entry7387 // so that its sti is known when creating the weak external aux entry
7364 try coff.pendingSymbolTableEntry(exported_si);7388 try coff.pendingSymbolTableEntry(exported_si);
7365 export_sym.flags.weak_external_strat = .alias;7389 export_sym.flags.weak_external_strat = .alias;
7366 export_sym.setValue(.{ .weak_alias_si = exported_si });7390 export_sym.setValue(.{ .weak_alias_si = exported_si });
...@@ -7371,6 +7395,8 @@ fn updateExportsInner(...@@ -7371,6 +7395,8 @@ fn updateExportsInner(
7371 const prev_alias_sym = prev_alias_si.get(coff);7395 const prev_alias_sym = prev_alias_si.get(coff);
7372 switch (prev_alias_sym.flags.extra_tag) {7396 switch (prev_alias_sym.flags.extra_tag) {
7373 .size => export_sym.setExtra(.{ .size = prev_alias_sym.extra.size }),7397 .size => export_sym.setExtra(.{ .size = prev_alias_sym.extra.size }),
7398 // This export should have been deleted
7399 .next_alias_si => assert(prev_alias_sym.extra.next_alias_si == export_si),
7374 else => unreachable,7400 else => unreachable,
7375 }7401 }
73767402
...@@ -7474,10 +7500,21 @@ fn updateExportsInner(...@@ -7474,10 +7500,21 @@ fn updateExportsInner(
7474 }7500 }
7475}7501}
74767502
7477pub fn deleteExport(coff: *Coff, exported: Zcu.Exported, name: InternPool.NullTerminatedString) void {7503pub fn deleteExport(
7478 _ = coff;7504 coff: *Coff,
7479 _ = exported;7505 exported: Zcu.Exported,
7480 _ = name;7506 name: InternPool.NullTerminatedString,
7507) void {
7508 const zcu = coff.base.comp.zcu.?;
7509 const ip = &zcu.intern_pool;
7510
7511 const exported_si: Symbol.Index = switch (exported) {
7512 .nav => |nav| coff.navs.get(nav).?,
7513 .uav => |uav| coff.uavs.get(uav).?,
7514 };
7515
7516 const name_slice = name.toSlice(ip);
7517 log.debug("deleteExport({s}, {d})", .{ name_slice, exported_si });
74817518
7482 // TODO: Delete from first / second linker member table7519 // TODO: Delete from first / second linker member table
7483 // TODO: Delete from symbol table inside section7520 // TODO: Delete from symbol table inside section