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 {
7575 alignment: InternPool.Alignment,
7676}),
7777relocs: std.ArrayList(Reloc),
78first_free_reloc: Reloc.Index,
79last_free_reloc: Reloc.Index,
7880const_prog_node: std.Progress.Node,
7981synth_prog_node: std.Progress.Node,
8082symbol_prog_node: std.Progress.Node,
......@@ -1147,10 +1149,14 @@ pub const Reloc = extern struct {
11471149 loc: Symbol.Index,
11481150 target: Symbol.Index,
11491151 flags: packed struct(u8) {
1150 // 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.
1152 /// Indicates the addend is not known and should be recovered from the location itself.
1153 /// COFF relocation tables don't encode the addend, only the location.
11521154 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,
11541160 },
11551161
11561162 pub const Type = extern union {
......@@ -1169,6 +1175,10 @@ pub const Reloc = extern struct {
11691175 none = std.math.maxInt(u32),
11701176 _,
11711177
1178 pub fn wrap(i: ?u32) Reloc.Index {
1179 return @enumFromInt((i orelse return .none) + 1);
1180 }
1181
11721182 pub fn get(ri: Reloc.Index, coff: *Coff) *Reloc {
11731183 return &coff.relocs.items[@intFromEnum(ri)];
11741184 }
......@@ -1490,7 +1500,24 @@ pub const Reloc = extern struct {
14901500 .none => {},
14911501 else => |next| next.get(coff).prev = reloc.prev,
14921502 }
1503
14931504 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 }
14941521 }
14951522
14961523 comptime {
......@@ -1630,6 +1657,8 @@ fn create(
16301657 }),
16311658 .pending_uavs = .empty,
16321659 .relocs = .empty,
1660 .first_free_reloc = .none,
1661 .last_free_reloc = .none,
16331662 .const_prog_node = .none,
16341663 .synth_prog_node = .none,
16351664 .symbol_prog_node = .none,
......@@ -3624,6 +3653,9 @@ const RelocAddend = union(enum) {
36243653 pending: void,
36253654};
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.
36273659pub fn addReloc(
36283660 coff: *Coff,
36293661 loc_si: Symbol.Index,
......@@ -3747,6 +3779,7 @@ fn addRelocAssumeCapacity(
37473779 .addend = if (addend == .pending) 0 else addend.known,
37483780 .flags = .{
37493781 .recover_addend = addend == .pending,
3782 .free = false,
37503783 },
37513784 };
37523785 switch (target.target_relocs) {
......@@ -3756,17 +3789,6 @@ fn addRelocAssumeCapacity(
37563789 target.target_relocs = ri;
37573790}
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
37703792fn failLoadInput(
37713793 coff: *Coff,
37723794 err: LoadInputError,
......@@ -5673,6 +5695,7 @@ fn reportUndefs(coff: *Coff, tid: Zcu.PerThread.Id) !void {
56735695
56745696 var undef_indices: std.ArrayListUnmanaged(u32) = .empty;
56755697 for (coff.relocs.items, 0..) |reloc, reloc_i| {
5698 if (reloc.flags.free) continue;
56765699 const target_sym = reloc.target.get(coff);
56775700 switch (target_sym.ni) {
56785701 .none => {
......@@ -7321,13 +7344,6 @@ fn updateExportsInner(
73217344 const gpa = zcu.gpa;
73227345 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 }
73317347 try coff.symbols.ensureUnusedCapacity(gpa, export_indices.len);
73327348 const exported_si: Symbol.Index = switch (exported) {
73337349 .nav => |nav| try coff.navSymbol(zcu, nav),
......@@ -7337,6 +7353,14 @@ fn updateExportsInner(
73377353 Type.fromInterned(ip.typeOf(uav)).abiAlignment(zcu),
73387354 ))),
73397355 };
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 }
73407364 while (try coff.resolve(pt.tid)) {}
73417365 while (try coff.idle(pt.tid)) {}
73427366
......@@ -7349,7 +7373,7 @@ fn updateExportsInner(
73497373 const @"export" = export_index.ptr(zcu);
73507374 const name = @"export".opts.name.toSlice(ip);
73517375
7352 // TODO: add an errMsg if this conflicts with an existing global
7376 // TODO: add an errMsg if this conflicts with an existing symbol
73537377 const export_si = try coff.globalSymbol(.{
73547378 .name = name,
73557379 .lib_name = null,
......@@ -7360,7 +7384,7 @@ fn updateExportsInner(
73607384 export_sym.section_number = exported_sym.section_number;
73617385 if (@"export".opts.linkage == .weak and !coff.isImage()) {
73627386 // exported_si needs to be ahead of export_si in the symbol table,
7363 // so that its sti is known when creating the aux entry
7387 // so that its sti is known when creating the weak external aux entry
73647388 try coff.pendingSymbolTableEntry(exported_si);
73657389 export_sym.flags.weak_external_strat = .alias;
73667390 export_sym.setValue(.{ .weak_alias_si = exported_si });
......@@ -7371,6 +7395,8 @@ fn updateExportsInner(
73717395 const prev_alias_sym = prev_alias_si.get(coff);
73727396 switch (prev_alias_sym.flags.extra_tag) {
73737397 .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),
73747400 else => unreachable,
73757401 }
73767402
......@@ -7474,10 +7500,21 @@ fn updateExportsInner(
74747500 }
74757501}
74767502
7477pub fn deleteExport(coff: *Coff, exported: Zcu.Exported, name: InternPool.NullTerminatedString) void {
7478 _ = coff;
7479 _ = exported;
7480 _ = name;
7503pub fn deleteExport(
7504 coff: *Coff,
7505 exported: Zcu.Exported,
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
74827519 // TODO: Delete from first / second linker member table
74837520 // TODO: Delete from symbol table inside section