Commit cc1d7a0e31

Jakub Konka <kubkon@jakubkonka.com>
2023-01-26 14:28:44
coff: migrate to new non-allocateDeclIndexes API
1 parent e1b9800
Changed files (8)
src/arch/aarch64/CodeGen.zig
@@ -4000,7 +4000,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
                                 const owner_decl = mod.declPtr(self.mod_fn.owner_decl);
                                 const atom_index = switch (self.bin_file.tag) {
                                     .macho => owner_decl.link.macho.getSymbolIndex().?,
-                                    .coff => owner_decl.link.coff.sym_index,
+                                    .coff => owner_decl.link.coff.getSymbolIndex().?,
                                     else => unreachable, // unsupported target format
                                 };
                                 _ = try self.addInst(.{
@@ -4318,11 +4318,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
                         .sym_index = fn_owner_decl.link.macho.getSymbolIndex().?,
                     },
                 });
-            } else if (self.bin_file.cast(link.File.Coff)) |_| {
+            } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
+                try fn_owner_decl.link.coff.ensureInitialized(coff_file);
                 try self.genSetReg(Type.initTag(.u64), .x30, .{
                     .linker_load = .{
                         .type = .got,
-                        .sym_index = fn_owner_decl.link.coff.sym_index,
+                        .sym_index = fn_owner_decl.link.coff.getSymbolIndex().?,
                     },
                 });
             } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
@@ -5494,7 +5495,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
                         const owner_decl = mod.declPtr(self.mod_fn.owner_decl);
                         const atom_index = switch (self.bin_file.tag) {
                             .macho => owner_decl.link.macho.getSymbolIndex().?,
-                            .coff => owner_decl.link.coff.sym_index,
+                            .coff => owner_decl.link.coff.getSymbolIndex().?,
                             else => unreachable, // unsupported target format
                         };
                         _ = try self.addInst(.{
@@ -5608,7 +5609,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
             const owner_decl = mod.declPtr(self.mod_fn.owner_decl);
             const atom_index = switch (self.bin_file.tag) {
                 .macho => owner_decl.link.macho.getSymbolIndex().?,
-                .coff => owner_decl.link.coff.sym_index,
+                .coff => owner_decl.link.coff.getSymbolIndex().?,
                 else => unreachable, // unsupported target format
             };
             _ = try self.addInst(.{
@@ -5802,7 +5803,7 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I
                         const owner_decl = mod.declPtr(self.mod_fn.owner_decl);
                         const atom_index = switch (self.bin_file.tag) {
                             .macho => owner_decl.link.macho.getSymbolIndex().?,
-                            .coff => owner_decl.link.coff.sym_index,
+                            .coff => owner_decl.link.coff.getSymbolIndex().?,
                             else => unreachable, // unsupported target format
                         };
                         _ = try self.addInst(.{
@@ -6129,11 +6130,11 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
             .type = .got,
             .sym_index = decl.link.macho.getSymbolIndex().?,
         } };
-    } else if (self.bin_file.cast(link.File.Coff)) |_| {
-        assert(decl.link.coff.sym_index != 0);
+    } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
+        try decl.link.coff.ensureInitialized(coff_file);
         return MCValue{ .linker_load = .{
             .type = .got,
-            .sym_index = decl.link.coff.sym_index,
+            .sym_index = decl.link.coff.getSymbolIndex().?,
         } };
     } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
         try p9.seeDecl(decl_index);
src/arch/x86_64/CodeGen.zig
@@ -2673,7 +2673,7 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue
             const atom_index = if (self.bin_file.tag == link.File.MachO.base_tag)
                 fn_owner_decl.link.macho.getSymbolIndex().?
             else
-                fn_owner_decl.link.coff.sym_index;
+                fn_owner_decl.link.coff.getSymbolIndex().?;
             const flags: u2 = switch (load_struct.type) {
                 .got => 0b00,
                 .direct => 0b01,
@@ -4005,11 +4005,13 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
                     .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }),
                     .data = .{ .imm = got_addr },
                 });
-            } else if (self.bin_file.cast(link.File.Coff)) |_| {
+            } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
+                try fn_owner_decl.link.coff.ensureInitialized(coff_file);
+                const sym_index = fn_owner_decl.link.coff.getSymbolIndex().?;
                 try self.genSetReg(Type.initTag(.usize), .rax, .{
                     .linker_load = .{
                         .type = .got,
-                        .sym_index = fn_owner_decl.link.coff.sym_index,
+                        .sym_index = sym_index,
                     },
                 });
                 _ = try self.addInst(.{
@@ -6725,11 +6727,11 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
             .type = .got,
             .sym_index = decl.link.macho.getSymbolIndex().?,
         } };
-    } else if (self.bin_file.cast(link.File.Coff)) |_| {
-        assert(decl.link.coff.sym_index != 0);
+    } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
+        try decl.link.coff.ensureInitialized(coff_file);
         return MCValue{ .linker_load = .{
             .type = .got,
-            .sym_index = decl.link.coff.sym_index,
+            .sym_index = decl.link.coff.getSymbolIndex().?,
         } };
     } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
         try p9.seeDecl(decl_index);
src/link/Coff/Atom.zig
@@ -39,30 +39,45 @@ pub const empty = Atom{
     .next = null,
 };
 
+pub fn ensureInitialized(self: *Atom, coff_file: *Coff) !void {
+    if (self.getSymbolIndex() != null) return; // Already initialized
+    self.sym_index = try coff_file.allocateSymbol();
+    try coff_file.atom_by_index_table.putNoClobber(coff_file.base.allocator, self.sym_index, self);
+}
+
+pub fn getSymbolIndex(self: Atom) ?u32 {
+    if (self.sym_index == 0) return null;
+    return self.sym_index;
+}
+
 /// Returns symbol referencing this atom.
 pub fn getSymbol(self: Atom, coff_file: *const Coff) *const coff.Symbol {
+    const sym_index = self.getSymbolIndex().?;
     return coff_file.getSymbol(.{
-        .sym_index = self.sym_index,
+        .sym_index = sym_index,
         .file = self.file,
     });
 }
 
 /// Returns pointer-to-symbol referencing this atom.
 pub fn getSymbolPtr(self: Atom, coff_file: *Coff) *coff.Symbol {
+    const sym_index = self.getSymbolIndex().?;
     return coff_file.getSymbolPtr(.{
-        .sym_index = self.sym_index,
+        .sym_index = sym_index,
         .file = self.file,
     });
 }
 
 pub fn getSymbolWithLoc(self: Atom) SymbolWithLoc {
-    return .{ .sym_index = self.sym_index, .file = self.file };
+    const sym_index = self.getSymbolIndex().?;
+    return .{ .sym_index = sym_index, .file = self.file };
 }
 
 /// Returns the name of this atom.
 pub fn getName(self: Atom, coff_file: *const Coff) []const u8 {
+    const sym_index = self.getSymbolIndex().?;
     return coff_file.getSymbolName(.{
-        .sym_index = self.sym_index,
+        .sym_index = sym_index,
         .file = self.file,
     });
 }
src/link/Coff.zig
@@ -480,16 +480,6 @@ fn growSectionVM(self: *Coff, sect_id: u32, needed_size: u32) !void {
     header.virtual_size = increased_size;
 }
 
-pub fn allocateDeclIndexes(self: *Coff, decl_index: Module.Decl.Index) !void {
-    if (self.llvm_object) |_| return;
-    const decl = self.base.options.module.?.declPtr(decl_index);
-    if (decl.link.coff.sym_index != 0) return;
-    decl.link.coff.sym_index = try self.allocateSymbol();
-    const gpa = self.base.allocator;
-    try self.atom_by_index_table.putNoClobber(gpa, decl.link.coff.sym_index, &decl.link.coff);
-    try self.decls.putNoClobber(gpa, decl_index, null);
-}
-
 fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u32 {
     const tracy = trace(@src());
     defer tracy.end();
@@ -615,7 +605,7 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u
     return vaddr;
 }
 
-fn allocateSymbol(self: *Coff) !u32 {
+pub fn allocateSymbol(self: *Coff) !u32 {
     const gpa = self.base.allocator;
     try self.locals.ensureUnusedCapacity(gpa, 1);
 
@@ -716,12 +706,11 @@ fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom {
     const atom = try gpa.create(Atom);
     errdefer gpa.destroy(atom);
     atom.* = Atom.empty;
-    atom.sym_index = try self.allocateSymbol();
+    try atom.ensureInitialized(self);
     atom.size = @sizeOf(u64);
     atom.alignment = @alignOf(u64);
 
     try self.managed_atoms.append(gpa, atom);
-    try self.atom_by_index_table.putNoClobber(gpa, atom.sym_index, atom);
 
     const sym = atom.getSymbolPtr(self);
     sym.section_number = @intToEnum(coff.SectionNumber, self.got_section_index.? + 1);
@@ -754,12 +743,11 @@ fn createImportAtom(self: *Coff) !*Atom {
     const atom = try gpa.create(Atom);
     errdefer gpa.destroy(atom);
     atom.* = Atom.empty;
-    atom.sym_index = try self.allocateSymbol();
+    try atom.ensureInitialized(self);
     atom.size = @sizeOf(u64);
     atom.alignment = @alignOf(u64);
 
     try self.managed_atoms.append(gpa, atom);
-    try self.atom_by_index_table.putNoClobber(gpa, atom.sym_index, atom);
 
     const sym = atom.getSymbolPtr(self);
     sym.section_number = @intToEnum(coff.SectionNumber, self.idata_section_index.? + 1);
@@ -790,7 +778,11 @@ fn writeAtom(self: *Coff, atom: *Atom, code: []const u8) !void {
     const sym = atom.getSymbol(self);
     const section = self.sections.get(@enumToInt(sym.section_number) - 1);
     const file_offset = section.header.pointer_to_raw_data + sym.value - section.header.virtual_address;
-    log.debug("writing atom for symbol {s} at file offset 0x{x} to 0x{x}", .{ atom.getName(self), file_offset, file_offset + code.len });
+    log.debug("writing atom for symbol {s} at file offset 0x{x} to 0x{x}", .{
+        atom.getName(self),
+        file_offset,
+        file_offset + code.len,
+    });
     try self.base.file.?.pwriteAll(code, file_offset);
     try self.resolveRelocs(atom);
 }
@@ -848,6 +840,7 @@ fn freeAtom(self: *Coff, atom: *Atom) void {
     // Remove any relocs and base relocs associated with this Atom
     self.freeRelocationsForAtom(atom);
 
+    const gpa = self.base.allocator;
     const sym = atom.getSymbol(self);
     const sect_id = @enumToInt(sym.section_number) - 1;
     const free_list = &self.sections.items(.free_list)[sect_id];
@@ -885,7 +878,7 @@ fn freeAtom(self: *Coff, atom: *Atom) void {
         if (!already_have_free_list_node and prev.freeListEligible(self)) {
             // The free list is heuristics, it doesn't have to be perfect, so we can
             // ignore the OOM here.
-            free_list.append(self.base.allocator, prev) catch {};
+            free_list.append(gpa, prev) catch {};
         }
     } else {
         atom.prev = null;
@@ -896,6 +889,28 @@ fn freeAtom(self: *Coff, atom: *Atom) void {
     } else {
         atom.next = null;
     }
+
+    // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
+    const sym_index = atom.getSymbolIndex().?;
+    self.locals_free_list.append(gpa, sym_index) catch {};
+
+    // Try freeing GOT atom if this decl had one
+    const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null };
+    if (self.got_entries_table.get(got_target)) |got_index| {
+        self.got_entries_free_list.append(gpa, @intCast(u32, got_index)) catch {};
+        self.got_entries.items[got_index] = .{
+            .target = .{ .sym_index = 0, .file = null },
+            .sym_index = 0,
+        };
+        _ = self.got_entries_table.remove(got_target);
+
+        log.debug("  adding GOT index {d} to free list (target local@{d})", .{ got_index, sym_index });
+    }
+
+    self.locals.items[sym_index].section_number = .UNDEFINED;
+    _ = self.atom_by_index_table.remove(sym_index);
+    log.debug("  adding local symbol index {d} to free list", .{sym_index});
+    atom.sym_index = 0;
 }
 
 pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
@@ -912,8 +927,15 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live
 
     const decl_index = func.owner_decl;
     const decl = module.declPtr(decl_index);
-    self.freeUnnamedConsts(decl_index);
-    self.freeRelocationsForAtom(&decl.link.coff);
+    const atom = &decl.link.coff;
+    try atom.ensureInitialized(self);
+    const gop = try self.decls.getOrPut(self.base.allocator, decl_index);
+    if (gop.found_existing) {
+        self.freeUnnamedConsts(decl_index);
+        self.freeRelocationsForAtom(&decl.link.coff);
+    } else {
+        gop.value_ptr.* = null;
+    }
 
     var code_buffer = std.ArrayList(u8).init(self.base.allocator);
     defer code_buffer.deinit();
@@ -960,9 +982,9 @@ pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.In
     const atom = try gpa.create(Atom);
     errdefer gpa.destroy(atom);
     atom.* = Atom.empty;
+    try atom.ensureInitialized(self);
+    try self.managed_atoms.append(gpa, atom);
 
-    atom.sym_index = try self.allocateSymbol();
-    const sym = atom.getSymbolPtr(self);
     const sym_name = blk: {
         const decl_name = try decl.getFullyQualifiedName(mod);
         defer gpa.free(decl_name);
@@ -971,14 +993,11 @@ pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.In
         break :blk try std.fmt.allocPrint(gpa, "__unnamed_{s}_{d}", .{ decl_name, index });
     };
     defer gpa.free(sym_name);
-    try self.setSymbolName(sym, sym_name);
-    sym.section_number = @intToEnum(coff.SectionNumber, self.rdata_section_index.? + 1);
-
-    try self.managed_atoms.append(gpa, atom);
-    try self.atom_by_index_table.putNoClobber(gpa, atom.sym_index, atom);
+    try self.setSymbolName(atom.getSymbolPtr(self), sym_name);
+    atom.getSymbolPtr(self).section_number = @intToEnum(coff.SectionNumber, self.rdata_section_index.? + 1);
 
     const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), tv, &code_buffer, .none, .{
-        .parent_atom_index = atom.sym_index,
+        .parent_atom_index = atom.getSymbolIndex().?,
     });
     const code = switch (res) {
         .ok => code_buffer.items,
@@ -993,17 +1012,17 @@ pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.In
     const required_alignment = tv.ty.abiAlignment(self.base.options.target);
     atom.alignment = required_alignment;
     atom.size = @intCast(u32, code.len);
-    sym.value = try self.allocateAtom(atom, atom.size, atom.alignment);
+    atom.getSymbolPtr(self).value = try self.allocateAtom(atom, atom.size, atom.alignment);
     errdefer self.freeAtom(atom);
 
     try unnamed_consts.append(gpa, atom);
 
-    log.debug("allocated atom for {s} at 0x{x}", .{ sym_name, sym.value });
+    log.debug("allocated atom for {s} at 0x{x}", .{ sym_name, atom.getSymbol(self).value });
     log.debug("  (required alignment 0x{x})", .{required_alignment});
 
     try self.writeAtom(atom, code);
 
-    return atom.sym_index;
+    return atom.getSymbolIndex().?;
 }
 
 pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !void {
@@ -1028,7 +1047,14 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !
         }
     }
 
-    self.freeRelocationsForAtom(&decl.link.coff);
+    const atom = &decl.link.coff;
+    try atom.ensureInitialized(self);
+    const gop = try self.decls.getOrPut(self.base.allocator, decl_index);
+    if (gop.found_existing) {
+        self.freeRelocationsForAtom(atom);
+    } else {
+        gop.value_ptr.* = null;
+    }
 
     var code_buffer = std.ArrayList(u8).init(self.base.allocator);
     defer code_buffer.deinit();
@@ -1038,7 +1064,7 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !
         .ty = decl.ty,
         .val = decl_val,
     }, &code_buffer, .none, .{
-        .parent_atom_index = decl.link.coff.sym_index,
+        .parent_atom_index = decl.link.coff.getSymbolIndex().?,
     });
     const code = switch (res) {
         .ok => code_buffer.items,
@@ -1099,7 +1125,7 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8,
 
     const code_len = @intCast(u32, code.len);
     const atom = &decl.link.coff;
-    assert(atom.sym_index != 0); // Caller forgot to allocateDeclIndexes()
+
     if (atom.size != 0) {
         const sym = atom.getSymbolPtr(self);
         try self.setSymbolName(sym, decl_name);
@@ -1116,7 +1142,7 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8,
             if (vaddr != sym.value) {
                 sym.value = vaddr;
                 log.debug("  (updating GOT entry)", .{});
-                const got_target = SymbolWithLoc{ .sym_index = atom.sym_index, .file = null };
+                const got_target = SymbolWithLoc{ .sym_index = atom.getSymbolIndex().?, .file = null };
                 const got_atom = self.getGotAtomForSymbol(got_target).?;
                 self.markRelocsDirtyByTarget(got_target);
                 try self.writePtrWidthAtom(got_atom);
@@ -1137,10 +1163,10 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8,
         atom.size = code_len;
         sym.value = vaddr;
 
-        const got_target = SymbolWithLoc{ .sym_index = atom.sym_index, .file = null };
+        const got_target = SymbolWithLoc{ .sym_index = atom.getSymbolIndex().?, .file = null };
         const got_index = try self.allocateGotEntry(got_target);
         const got_atom = try self.createGotAtom(got_target);
-        self.got_entries.items[got_index].sym_index = got_atom.sym_index;
+        self.got_entries.items[got_index].sym_index = got_atom.getSymbolIndex().?;
         try self.writePtrWidthAtom(got_atom);
     }
 
@@ -1160,11 +1186,6 @@ fn freeUnnamedConsts(self: *Coff, decl_index: Module.Decl.Index) void {
     const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return;
     for (unnamed_consts.items) |atom| {
         self.freeAtom(atom);
-        self.locals_free_list.append(gpa, atom.sym_index) catch {};
-        self.locals.items[atom.sym_index].section_number = .UNDEFINED;
-        _ = self.atom_by_index_table.remove(atom.sym_index);
-        log.debug("  adding local symbol index {d} to free list", .{atom.sym_index});
-        atom.sym_index = 0;
     }
     unnamed_consts.clearAndFree(gpa);
 }
@@ -1179,35 +1200,11 @@ pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void {
 
     log.debug("freeDecl {*}", .{decl});
 
-    const kv = self.decls.fetchRemove(decl_index);
-    if (kv.?.value) |_| {
-        self.freeAtom(&decl.link.coff);
-        self.freeUnnamedConsts(decl_index);
-    }
-
-    // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
-    const gpa = self.base.allocator;
-    const sym_index = decl.link.coff.sym_index;
-    if (sym_index != 0) {
-        self.locals_free_list.append(gpa, sym_index) catch {};
-
-        // Try freeing GOT atom if this decl had one
-        const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null };
-        if (self.got_entries_table.get(got_target)) |got_index| {
-            self.got_entries_free_list.append(gpa, @intCast(u32, got_index)) catch {};
-            self.got_entries.items[got_index] = .{
-                .target = .{ .sym_index = 0, .file = null },
-                .sym_index = 0,
-            };
-            _ = self.got_entries_table.remove(got_target);
-
-            log.debug("  adding GOT index {d} to free list (target local@{d})", .{ got_index, sym_index });
+    if (self.decls.fetchRemove(decl_index)) |kv| {
+        if (kv.value) |_| {
+            self.freeAtom(&decl.link.coff);
+            self.freeUnnamedConsts(decl_index);
         }
-
-        self.locals.items[sym_index].section_number = .UNDEFINED;
-        _ = self.atom_by_index_table.remove(sym_index);
-        log.debug("  adding local symbol index {d} to free list", .{sym_index});
-        decl.link.coff.sym_index = 0;
     }
 }
 
@@ -1261,7 +1258,14 @@ pub fn updateDeclExports(
 
     const decl = module.declPtr(decl_index);
     const atom = &decl.link.coff;
-    if (atom.sym_index == 0) return;
+
+    if (atom.getSymbolIndex() == null) return;
+
+    const gop = try self.decls.getOrPut(gpa, decl_index);
+    if (!gop.found_existing) {
+        gop.value_ptr.* = self.getDeclOutputSection(decl);
+    }
+
     const decl_sym = atom.getSymbol(self);
 
     for (exports) |exp| {
@@ -1416,7 +1420,7 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
 
         const import_index = try self.allocateImportEntry(global);
         const import_atom = try self.createImportAtom();
-        self.imports.items[import_index].sym_index = import_atom.sym_index;
+        self.imports.items[import_index].sym_index = import_atom.getSymbolIndex().?;
         try self.writePtrWidthAtom(import_atom);
     }
 
@@ -1460,10 +1464,12 @@ pub fn getDeclVAddr(
     const decl = mod.declPtr(decl_index);
 
     assert(self.llvm_object == null);
-    assert(decl.link.coff.sym_index != 0);
+
+    try decl.link.coff.ensureInitialized(self);
+    const sym_index = decl.link.coff.getSymbolIndex().?;
 
     const atom = self.getAtomForSymbol(.{ .sym_index = reloc_info.parent_atom_index, .file = null }).?;
-    const target = SymbolWithLoc{ .sym_index = decl.link.coff.sym_index, .file = null };
+    const target = SymbolWithLoc{ .sym_index = sym_index, .file = null };
     try atom.addRelocation(self, .{
         .type = .direct,
         .target = target,
src/link/Elf.zig
@@ -893,7 +893,7 @@ fn growAllocSection(self: *Elf, shdr_index: u16, phdr_index: u16, needed_size: u
         // Must move the entire section.
         const new_offset = self.findFreeSpace(needed_size, self.page_size);
         const existing_size = if (self.atoms.get(phdr_index)) |last| blk: {
-            const sym = self.local_symbols.items[last.local_sym_index];
+            const sym = last.getSymbol(self);
             break :blk (sym.st_value + sym.st_size) - phdr.p_vaddr;
         } else if (shdr_index == self.got_section_index.?) blk: {
             break :blk shdr.sh_size;
@@ -1031,7 +1031,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
         while (it.next()) |entry| {
             const atom = entry.key_ptr.*;
             const relocs = entry.value_ptr.*;
-            const source_sym = self.local_symbols.items[atom.local_sym_index];
+            const source_sym = atom.getSymbol(self);
             const source_shdr = self.sections.items[source_sym.st_shndx];
 
             log.debug("relocating '{s}'", .{self.getString(source_sym.st_name)});
@@ -2034,11 +2034,13 @@ fn writeElfHeader(self: *Elf) !void {
 }
 
 fn freeTextBlock(self: *Elf, text_block: *TextBlock, phdr_index: u16) void {
-    const local_sym = self.local_symbols.items[text_block.local_sym_index];
+    const local_sym = text_block.getSymbol(self);
     const name_str_index = local_sym.st_name;
     const name = self.getString(name_str_index);
     log.debug("freeTextBlock {*} ({s})", .{ text_block, name });
 
+    self.freeRelocationsForTextBlock(text_block);
+
     const free_list = self.atom_free_lists.getPtr(phdr_index).?;
     var already_have_free_list_node = false;
     {
@@ -2107,7 +2109,7 @@ fn shrinkTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, phdr
 }
 
 fn growTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, alignment: u64, phdr_index: u16) !u64 {
-    const sym = self.local_symbols.items[text_block.local_sym_index];
+    const sym = text_block.getSymbol(self);
     const align_ok = mem.alignBackwardGeneric(u64, sym.st_value, alignment) == sym.st_value;
     const need_realloc = !align_ok or new_block_size > text_block.capacity(self);
     if (!need_realloc) return sym.st_value;
@@ -2137,7 +2139,7 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al
             const big_block = free_list.items[i];
             // We now have a pointer to a live text block that has too much capacity.
             // Is it enough that we could fit this new text block?
-            const sym = self.local_symbols.items[big_block.local_sym_index];
+            const sym = big_block.getSymbol(self);
             const capacity = big_block.capacity(self);
             const ideal_capacity = padToIdeal(capacity);
             const ideal_capacity_end_vaddr = std.math.add(u64, sym.st_value, ideal_capacity) catch ideal_capacity;
@@ -2168,7 +2170,7 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al
             }
             break :blk new_start_vaddr;
         } else if (self.atoms.get(phdr_index)) |last| {
-            const sym = self.local_symbols.items[last.local_sym_index];
+            const sym = last.getSymbol(self);
             const ideal_capacity = padToIdeal(sym.st_size);
             const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity;
             const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);
@@ -2270,6 +2272,11 @@ pub fn allocateGotOffset(self: *Elf) !u32 {
     return index;
 }
 
+fn freeRelocationsForTextBlock(self: *Elf, text_block: *TextBlock) void {
+    var removed_relocs = self.relocs.fetchRemove(text_block);
+    if (removed_relocs) |*relocs| relocs.value.deinit(self.base.allocator);
+}
+
 fn freeUnnamedConsts(self: *Elf, decl_index: Module.Decl.Index) void {
     const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return;
     for (unnamed_consts.items) |atom| {
@@ -2341,8 +2348,7 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s
     const phdr_index = decl_ptr.*.?;
     const shdr_index = self.phdr_shdr_table.get(phdr_index).?;
 
-    assert(decl.link.elf.local_sym_index != 0); // Caller forgot to allocateDeclIndexes()
-    const local_sym = &self.local_symbols.items[decl.link.elf.local_sym_index];
+    const local_sym = decl.link.elf.getSymbolPtr(self);
     if (local_sym.st_size != 0) {
         const capacity = decl.link.elf.capacity(self);
         const need_realloc = code.len > capacity or
@@ -2366,7 +2372,7 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s
         local_sym.st_other = 0;
         local_sym.st_shndx = shdr_index;
         // TODO this write could be avoided if no fields of the symbol were changed.
-        try self.writeSymbol(decl.link.elf.local_sym_index);
+        try self.writeSymbol(decl.link.elf.getSymbolIndex().?);
     } else {
         const name_str_index = try self.makeString(decl_name);
         const vaddr = try self.allocateTextBlock(&decl.link.elf, code.len, required_alignment, phdr_index);
@@ -2383,7 +2389,7 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s
         };
         self.offset_table.items[decl.link.elf.offset_table_index] = vaddr;
 
-        try self.writeSymbol(decl.link.elf.local_sym_index);
+        try self.writeSymbol(decl.link.elf.getSymbolIndex().?);
         try self.writeOffsetTableEntry(decl.link.elf.offset_table_index);
     }
 
@@ -2412,6 +2418,7 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven
     const gop = try self.decls.getOrPut(self.base.allocator, decl_index);
     if (gop.found_existing) {
         self.freeUnnamedConsts(decl_index);
+        self.freeRelocationsForTextBlock(atom);
     } else {
         gop.value_ptr.* = null;
     }
@@ -2481,7 +2488,9 @@ pub fn updateDecl(self: *Elf, module: *Module, decl_index: Module.Decl.Index) !v
     const atom = &decl.link.elf;
     try atom.ensureInitialized(self);
     const gop = try self.decls.getOrPut(self.base.allocator, decl_index);
-    if (!gop.found_existing) {
+    if (gop.found_existing) {
+        self.freeRelocationsForTextBlock(atom);
+    } else {
         gop.value_ptr.* = null;
     }
 
@@ -2500,14 +2509,14 @@ pub fn updateDecl(self: *Elf, module: *Module, decl_index: Module.Decl.Index) !v
         }, &code_buffer, .{
             .dwarf = ds,
         }, .{
-            .parent_atom_index = decl.link.elf.local_sym_index,
+            .parent_atom_index = decl.link.elf.getSymbolIndex().?,
         })
     else
         try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
             .ty = decl.ty,
             .val = decl_val,
         }, &code_buffer, .none, .{
-            .parent_atom_index = decl.link.elf.local_sym_index,
+            .parent_atom_index = decl.link.elf.getSymbolIndex().?,
         });
 
     const code = switch (res) {
@@ -2551,6 +2560,8 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module
     const atom = try self.base.allocator.create(TextBlock);
     errdefer self.base.allocator.destroy(atom);
     atom.* = TextBlock.empty;
+    // TODO for unnamed consts we don't need GOT offset/entry allocated
+    try atom.ensureInitialized(self);
     try self.managed_atoms.append(self.base.allocator, atom);
 
     const name_str_index = blk: {
@@ -2565,14 +2576,10 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module
     };
     const name = self.getString(name_str_index);
 
-    log.debug("allocating symbol indexes for {s}", .{name});
-    atom.local_sym_index = try self.allocateLocalSymbol();
-    try self.atom_by_index_table.putNoClobber(self.base.allocator, atom.local_sym_index, atom);
-
     const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .{
         .none = {},
     }, .{
-        .parent_atom_index = atom.local_sym_index,
+        .parent_atom_index = atom.getSymbolIndex().?,
     });
     const code = switch (res) {
         .ok => code_buffer.items,
@@ -2592,7 +2599,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module
 
     log.debug("allocated text block for {s} at 0x{x}", .{ name, vaddr });
 
-    const local_sym = &self.local_symbols.items[atom.local_sym_index];
+    const local_sym = atom.getSymbolPtr(self);
     local_sym.* = .{
         .st_name = name_str_index,
         .st_info = (elf.STB_LOCAL << 4) | elf.STT_OBJECT,
@@ -2602,14 +2609,14 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module
         .st_size = code.len,
     };
 
-    try self.writeSymbol(atom.local_sym_index);
+    try self.writeSymbol(atom.getSymbolIndex().?);
     try unnamed_consts.append(self.base.allocator, atom);
 
     const section_offset = local_sym.st_value - self.program_headers.items[phdr_index].p_vaddr;
     const file_offset = self.sections.items[shdr_index].sh_offset + section_offset;
     try self.base.file.?.pwriteAll(code, file_offset);
 
-    return atom.local_sym_index;
+    return atom.getSymbolIndex().?;
 }
 
 pub fn updateDeclExports(
src/link/MachO.zig
@@ -1056,19 +1056,14 @@ pub fn allocateSpecialSymbols(self: *MachO) !void {
 pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
     const gpa = self.base.allocator;
 
-    const sym_index = try self.allocateSymbol();
-    const atom = blk: {
-        const atom = try gpa.create(Atom);
-        atom.* = Atom.empty;
-        atom.sym_index = sym_index;
-        atom.size = @sizeOf(u64);
-        atom.alignment = @alignOf(u64);
-        break :blk atom;
-    };
+    const atom = try gpa.create(Atom);
+    atom.* = Atom.empty;
+    try atom.ensureInitialized(self);
+    atom.size = @sizeOf(u64);
+    atom.alignment = @alignOf(u64);
     errdefer gpa.destroy(atom);
 
     try self.managed_atoms.append(gpa, atom);
-    try self.atom_by_index_table.putNoClobber(gpa, atom.sym_index, atom);
 
     const sym = atom.getSymbolPtr(self);
     sym.n_type = macho.N_SECT;
@@ -1109,15 +1104,11 @@ pub fn createDyldPrivateAtom(self: *MachO) !void {
 
     const gpa = self.base.allocator;
 
-    const sym_index = try self.allocateSymbol();
-    const atom = blk: {
-        const atom = try gpa.create(Atom);
-        atom.* = Atom.empty;
-        atom.sym_index = sym_index;
-        atom.size = @sizeOf(u64);
-        atom.alignment = @alignOf(u64);
-        break :blk atom;
-    };
+    const atom = try gpa.create(Atom);
+    atom.* = Atom.empty;
+    try atom.ensureInitialized(self);
+    atom.size = @sizeOf(u64);
+    atom.alignment = @alignOf(u64);
     errdefer gpa.destroy(atom);
 
     const sym = atom.getSymbolPtr(self);
@@ -1126,7 +1117,6 @@ pub fn createDyldPrivateAtom(self: *MachO) !void {
     self.dyld_private_atom = atom;
 
     try self.managed_atoms.append(gpa, atom);
-    try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
 
     sym.n_value = try self.allocateAtom(atom, atom.size, @alignOf(u64));
     log.debug("allocated dyld_private atom at 0x{x}", .{sym.n_value});
@@ -1144,18 +1134,14 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {
         .aarch64 => 6 * @sizeOf(u32),
         else => unreachable,
     };
-    const sym_index = try self.allocateSymbol();
-    const atom = blk: {
-        const atom = try gpa.create(Atom);
-        atom.* = Atom.empty;
-        atom.sym_index = sym_index;
-        atom.size = size;
-        atom.alignment = switch (arch) {
-            .x86_64 => 1,
-            .aarch64 => @alignOf(u32),
-            else => unreachable,
-        };
-        break :blk atom;
+    const atom = try gpa.create(Atom);
+    atom.* = Atom.empty;
+    try atom.ensureInitialized(self);
+    atom.size = size;
+    atom.alignment = switch (arch) {
+        .x86_64 => 1,
+        .aarch64 => @alignOf(u32),
+        else => unreachable,
     };
     errdefer gpa.destroy(atom);
 
@@ -1163,7 +1149,7 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {
     sym.n_type = macho.N_SECT;
     sym.n_sect = self.stub_helper_section_index.? + 1;
 
-    const dyld_private_sym_index = self.dyld_private_atom.?.sym_index;
+    const dyld_private_sym_index = self.dyld_private_atom.?.getSymbolIndex().?;
 
     const code = try gpa.alloc(u8, size);
     defer gpa.free(code);
@@ -1258,7 +1244,6 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {
     self.stub_helper_preamble_atom = atom;
 
     try self.managed_atoms.append(gpa, atom);
-    try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
 
     sym.n_value = try self.allocateAtom(atom, size, atom.alignment);
     log.debug("allocated stub preamble atom at 0x{x}", .{sym.n_value});
@@ -1273,18 +1258,14 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
         .aarch64 => 3 * @sizeOf(u32),
         else => unreachable,
     };
-    const sym_index = try self.allocateSymbol();
-    const atom = blk: {
-        const atom = try gpa.create(Atom);
-        atom.* = Atom.empty;
-        atom.sym_index = sym_index;
-        atom.size = size;
-        atom.alignment = switch (arch) {
-            .x86_64 => 1,
-            .aarch64 => @alignOf(u32),
-            else => unreachable,
-        };
-        break :blk atom;
+    const atom = try gpa.create(Atom);
+    atom.* = Atom.empty;
+    try atom.ensureInitialized(self);
+    atom.size = size;
+    atom.alignment = switch (arch) {
+        .x86_64 => 1,
+        .aarch64 => @alignOf(u32),
+        else => unreachable,
     };
     errdefer gpa.destroy(atom);
 
@@ -1306,7 +1287,7 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
 
             try atom.addRelocation(self, .{
                 .type = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
-                .target = .{ .sym_index = self.stub_helper_preamble_atom.?.sym_index, .file = null },
+                .target = .{ .sym_index = self.stub_helper_preamble_atom.?.getSymbolIndex().?, .file = null },
                 .offset = 6,
                 .addend = 0,
                 .pcrel = true,
@@ -1329,7 +1310,7 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
 
             try atom.addRelocation(self, .{
                 .type = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_BRANCH26),
-                .target = .{ .sym_index = self.stub_helper_preamble_atom.?.sym_index, .file = null },
+                .target = .{ .sym_index = self.stub_helper_preamble_atom.?.getSymbolIndex().?, .file = null },
                 .offset = 4,
                 .addend = 0,
                 .pcrel = true,
@@ -1340,7 +1321,6 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
     }
 
     try self.managed_atoms.append(gpa, atom);
-    try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
 
     sym.n_value = try self.allocateAtom(atom, size, atom.alignment);
     log.debug("allocated stub helper atom at 0x{x}", .{sym.n_value});
@@ -1351,15 +1331,11 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
 
 pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWithLoc) !*Atom {
     const gpa = self.base.allocator;
-    const sym_index = try self.allocateSymbol();
-    const atom = blk: {
-        const atom = try gpa.create(Atom);
-        atom.* = Atom.empty;
-        atom.sym_index = sym_index;
-        atom.size = @sizeOf(u64);
-        atom.alignment = @alignOf(u64);
-        break :blk atom;
-    };
+    const atom = try gpa.create(Atom);
+    atom.* = Atom.empty;
+    try atom.ensureInitialized(self);
+    atom.size = @sizeOf(u64);
+    atom.alignment = @alignOf(u64);
     errdefer gpa.destroy(atom);
 
     const sym = atom.getSymbolPtr(self);
@@ -1385,7 +1361,6 @@ pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWi
     });
 
     try self.managed_atoms.append(gpa, atom);
-    try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
 
     sym.n_value = try self.allocateAtom(atom, atom.size, @alignOf(u64));
     log.debug("allocated lazy pointer atom at 0x{x} ({s})", .{ sym.n_value, self.getSymbolName(target) });
@@ -1402,19 +1377,15 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
         .aarch64 => 3 * @sizeOf(u32),
         else => unreachable, // unhandled architecture type
     };
-    const sym_index = try self.allocateSymbol();
-    const atom = blk: {
-        const atom = try gpa.create(Atom);
-        atom.* = Atom.empty;
-        atom.sym_index = sym_index;
-        atom.size = size;
-        atom.alignment = switch (arch) {
-            .x86_64 => 1,
-            .aarch64 => @alignOf(u32),
-            else => unreachable, // unhandled architecture type
+    const atom = try gpa.create(Atom);
+    atom.* = Atom.empty;
+    try atom.ensureInitialized(self);
+    atom.size = size;
+    atom.alignment = switch (arch) {
+        .x86_64 => 1,
+        .aarch64 => @alignOf(u32),
+        else => unreachable, // unhandled architecture type
 
-        };
-        break :blk atom;
     };
     errdefer gpa.destroy(atom);
 
@@ -1476,7 +1447,6 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
     }
 
     try self.managed_atoms.append(gpa, atom);
-    try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
 
     sym.n_value = try self.allocateAtom(atom, size, atom.alignment);
     log.debug("allocated stub atom at 0x{x}", .{sym.n_value});
@@ -1617,9 +1587,9 @@ pub fn resolveSymbolsInDylibs(self: *MachO) !void {
 
                 const stub_index = try self.allocateStubEntry(global);
                 const stub_helper_atom = try self.createStubHelperAtom();
-                const laptr_atom = try self.createLazyPointerAtom(stub_helper_atom.sym_index, global);
-                const stub_atom = try self.createStubAtom(laptr_atom.sym_index);
-                self.stubs.items[stub_index].sym_index = stub_atom.sym_index;
+                const laptr_atom = try self.createLazyPointerAtom(stub_helper_atom.getSymbolIndex().?, global);
+                const stub_atom = try self.createStubAtom(laptr_atom.getSymbolIndex().?);
+                self.stubs.items[stub_index].sym_index = stub_atom.getSymbolIndex().?;
                 self.markRelocsDirtyByTarget(global);
             }
 
@@ -1717,7 +1687,7 @@ pub fn resolveDyldStubBinder(self: *MachO) !void {
     // Add dyld_stub_binder as the final GOT entry.
     const got_index = try self.allocateGotEntry(global);
     const got_atom = try self.createGotAtom(global);
-    self.got_entries.items[got_index].sym_index = got_atom.sym_index;
+    self.got_entries.items[got_index].sym_index = got_atom.getSymbolIndex().?;
 
     try self.writePtrWidthAtom(got_atom);
 }
@@ -2098,14 +2068,11 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
     const atom = try gpa.create(Atom);
     errdefer gpa.destroy(atom);
     atom.* = Atom.empty;
-
-    atom.sym_index = try self.allocateSymbol();
-
+    try atom.ensureInitialized(self);
     try self.managed_atoms.append(gpa, atom);
-    try self.atom_by_index_table.putNoClobber(gpa, atom.sym_index, atom);
 
     const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .none, .{
-        .parent_atom_index = atom.sym_index,
+        .parent_atom_index = atom.getSymbolIndex().?,
     });
     const code = switch (res) {
         .ok => code_buffer.items,
@@ -2137,7 +2104,7 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
 
     try self.writeAtom(atom, code);
 
-    return atom.sym_index;
+    return atom.getSymbolIndex().?;
 }
 
 pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index) !void {
@@ -2188,14 +2155,14 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)
         }, &code_buffer, .{
             .dwarf = ds,
         }, .{
-            .parent_atom_index = decl.link.macho.sym_index,
+            .parent_atom_index = decl.link.macho.getSymbolIndex().?,
         })
     else
         try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
             .ty = decl.ty,
             .val = decl_val,
         }, &code_buffer, .none, .{
-            .parent_atom_index = decl.link.macho.sym_index,
+            .parent_atom_index = decl.link.macho.getSymbolIndex().?,
         });
 
     const code = switch (res) {
src/link.zig
@@ -615,12 +615,16 @@ pub const File = struct {
             return;
         }
         switch (base.tag) {
-            .coff => return @fieldParentPtr(Coff, "base", base).allocateDeclIndexes(decl_index),
-            .elf => {}, // no-op
-            .macho => {}, // no-op
             .wasm => return @fieldParentPtr(Wasm, "base", base).allocateDeclIndexes(decl_index),
             .plan9 => return @fieldParentPtr(Plan9, "base", base).allocateDeclIndexes(decl_index),
-            .c, .spirv, .nvptx => {},
+
+            .coff,
+            .elf,
+            .macho,
+            .c,
+            .spirv,
+            .nvptx,
+            => {},
         }
     }
 
src/Module.zig
@@ -5324,7 +5324,12 @@ pub fn deleteUnusedDecl(mod: *Module, decl_index: Decl.Index) void {
     // Until then, we did call `allocateDeclIndexes` on this anonymous Decl and so we
     // must call `freeDecl` in the linker backend now.
     switch (mod.comp.bin_file.tag) {
-        .elf, .macho, .c => {}, // this linker backend has already migrated to the new API
+        .coff,
+        .elf,
+        .macho,
+        .c,
+        => {}, // this linker backend has already migrated to the new API
+
         else => if (decl.has_tv) {
             if (decl.ty.isFnOrHasRuntimeBits()) {
                 mod.comp.bin_file.freeDecl(decl_index);