Commit 5ef8321338

Luuk de Gram <luuk@degram.dev>
2024-02-09 16:36:09
wasm: make symbol indexes a non-exhaustive enum
This introduces some type safety so we cannot accidently give an atom index as a symbol index. This also means we do not have to store any optionals and therefore allow for memory optimizations. Lastly, we can now always simply access the symbol index of an atom, rather than having to call `getSymbolIndex` as it is easy to forget.
1 parent c99ef23
src/arch/wasm/CodeGen.zig
@@ -1286,8 +1286,9 @@ fn genFunc(func: *CodeGen) InnerError!void {
         var prologue = std.ArrayList(Mir.Inst).init(func.gpa);
         defer prologue.deinit();
 
+        const sp = @intFromEnum(func.bin_file.zigObjectPtr().?.stack_pointer_sym);
         // load stack pointer
-        try prologue.append(.{ .tag = .global_get, .data = .{ .label = 0 } });
+        try prologue.append(.{ .tag = .global_get, .data = .{ .label = sp } });
         // store stack pointer so we can restore it when we return from the function
         try prologue.append(.{ .tag = .local_tee, .data = .{ .label = func.initial_stack_value.local.value } });
         // get the total stack size
@@ -1303,7 +1304,7 @@ fn genFunc(func: *CodeGen) InnerError!void {
         try prologue.append(.{ .tag = .local_tee, .data = .{ .label = func.bottom_stack_value.local.value } });
         // Store the current stack pointer value into the global stack pointer so other function calls will
         // start from this value instead and not overwrite the current stack.
-        try prologue.append(.{ .tag = .global_set, .data = .{ .label = 0 } });
+        try prologue.append(.{ .tag = .global_set, .data = .{ .label = sp } });
 
         // reserve space and insert all prologue instructions at the front of the instruction list
         // We insert them in reserve order as there is no insertSlice in multiArrayList.
@@ -1502,7 +1503,7 @@ fn restoreStackPointer(func: *CodeGen) !void {
     try func.emitWValue(func.initial_stack_value);
 
     // save its value in the global stack pointer
-    try func.addLabel(.global_set, 0);
+    try func.addLabel(.global_set, @intFromEnum(func.bin_file.zigObjectPtr().?.stack_pointer_sym));
 }
 
 /// From a given type, will create space on the virtual stack to store the value of such type.
@@ -2205,7 +2206,7 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif
             const type_index = try func.bin_file.storeDeclType(extern_func.decl, func_type);
             try func.bin_file.addOrUpdateImport(
                 mod.intern_pool.stringToSlice(ext_decl.name),
-                atom.getSymbolIndex().?,
+                atom.sym_index,
                 mod.intern_pool.stringToSliceUnwrap(ext_decl.getOwnedExternFunc(mod).?.lib_name),
                 type_index,
             );
@@ -2240,7 +2241,7 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif
 
     if (callee) |direct| {
         const atom_index = func.bin_file.zigObjectPtr().?.decls_map.get(direct).?.atom;
-        try func.addLabel(.call, func.bin_file.getAtom(atom_index).sym_index);
+        try func.addLabel(.call, @intFromEnum(func.bin_file.getAtom(atom_index).sym_index));
     } else {
         // in this case we call a function pointer
         // so load its value onto the stack
@@ -3158,7 +3159,7 @@ fn lowerAnonDeclRef(
         },
     }
     const target_atom_index = func.bin_file.zigObjectPtr().?.anon_decls.get(decl_val).?;
-    const target_sym_index = func.bin_file.getAtom(target_atom_index).getSymbolIndex().?;
+    const target_sym_index = @intFromEnum(func.bin_file.getAtom(target_atom_index).sym_index);
     if (is_fn_body) {
         return WValue{ .function_index = target_sym_index };
     } else if (offset == 0) {
@@ -3189,7 +3190,7 @@ fn lowerDeclRefValue(func: *CodeGen, tv: TypedValue, decl_index: InternPool.Decl
     const atom_index = try func.bin_file.getOrCreateAtomForDecl(decl_index);
     const atom = func.bin_file.getAtom(atom_index);
 
-    const target_sym_index = atom.sym_index;
+    const target_sym_index = @intFromEnum(atom.sym_index);
     if (decl.ty.zigTypeTag(mod) == .Fn) {
         return WValue{ .function_index = target_sym_index };
     } else if (offset == 0) {
@@ -3711,7 +3712,7 @@ fn airCmpLtErrorsLen(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
     const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
     const operand = try func.resolveInst(un_op);
     const sym_index = try func.bin_file.getGlobalSymbol("__zig_errors_len", null);
-    const errors_len = WValue{ .memory = sym_index };
+    const errors_len = WValue{ .memory = @intFromEnum(sym_index) };
 
     try func.emitWValue(operand);
     const mod = func.bin_file.base.comp.module.?;
@@ -7153,7 +7154,7 @@ fn callIntrinsic(
     args: []const WValue,
 ) InnerError!WValue {
     assert(param_types.len == args.len);
-    const symbol_index = func.bin_file.base.getGlobalSymbol(name, null) catch |err| {
+    const symbol_index = func.bin_file.getGlobalSymbol(name, null) catch |err| {
         return func.fail("Could not find or create global symbol '{s}'", .{@errorName(err)});
     };
 
@@ -7181,7 +7182,7 @@ fn callIntrinsic(
     }
 
     // Actually call our intrinsic
-    try func.addLabel(.call, symbol_index);
+    try func.addLabel(.call, @intFromEnum(symbol_index));
 
     if (!return_type.hasRuntimeBitsIgnoreComptime(mod)) {
         return WValue.none;
@@ -7224,7 +7225,7 @@ fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 {
 
     // check if we already generated code for this.
     if (func.bin_file.findGlobalSymbol(func_name)) |loc| {
-        return loc.index;
+        return @intFromEnum(loc.index);
     }
 
     const int_tag_ty = enum_ty.intTagType(mod);
@@ -7364,7 +7365,8 @@ fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 {
 
     const slice_ty = Type.slice_const_u8_sentinel_0;
     const func_type = try genFunctype(arena, .Unspecified, &.{int_tag_ty.ip_index}, slice_ty, mod);
-    return func.bin_file.createFunction(func_name, func_type, &body_list, &relocs);
+    const sym_index = try func.bin_file.createFunction(func_name, func_type, &body_list, &relocs);
+    return @intFromEnum(sym_index);
 }
 
 fn airErrorSetHasValue(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
src/link/Wasm/Atom.zig
@@ -2,7 +2,7 @@
 /// This is 'null' when the atom was generated by a synthetic linker symbol.
 file: FileIndex,
 /// symbol index of the symbol representing this atom
-sym_index: u32,
+sym_index: Symbol.Index,
 /// Size of the atom, used to calculate section sizes in the final binary
 size: u32 = 0,
 /// List of relocations belonging to this atom
@@ -17,19 +17,19 @@ offset: u32 = 0,
 /// The original offset within the object file. This value is substracted from
 /// relocation offsets to determine where in the `data` to rewrite the value
 original_offset: u32 = 0,
-/// Next atom in relation to this atom.
-/// When null, this atom is the last atom
-next: ?Atom.Index = null,
 /// Previous atom in relation to this atom.
 /// is null when this atom is the first in its order
-prev: ?Atom.Index = null,
+prev: Atom.Index = .null,
 /// Contains atoms local to a decl, all managed by this `Atom`.
 /// When the parent atom is being freed, it will also do so for all local atoms.
 locals: std.ArrayListUnmanaged(Atom.Index) = .{},
 
-/// Alias to an unsigned 32-bit integer.
-// TODO: Make this a non-exhaustive enum.
-pub const Index = u32;
+/// Represents the index of an Atom where `null` is considered
+/// an invalid atom.
+pub const Index = enum(u32) {
+    null = std.math.maxInt(u32),
+    _,
+};
 
 /// Frees all resources owned by this `Atom`.
 pub fn deinit(atom: *Atom, gpa: std.mem.Allocator) void {
@@ -50,7 +50,7 @@ pub fn format(atom: Atom, comptime fmt: []const u8, options: std.fmt.FormatOptio
     _ = fmt;
     _ = options;
     try writer.print("Atom{{ .sym_index = {d}, .alignment = {d}, .size = {d}, .offset = 0x{x:0>8} }}", .{
-        atom.sym_index,
+        @intFromEnum(atom.sym_index),
         atom.alignment,
         atom.size,
         atom.offset,
@@ -62,11 +62,6 @@ pub fn symbolLoc(atom: Atom) Wasm.SymbolLoc {
     return .{ .file = atom.file, .index = atom.sym_index };
 }
 
-pub fn getSymbolIndex(atom: Atom) ?u32 {
-    if (atom.sym_index == 0) return null;
-    return atom.sym_index;
-}
-
 /// Resolves the relocations within the atom, writing the new value
 /// at the calculated offset.
 pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void {
@@ -80,7 +75,7 @@ pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void {
     for (atom.relocs.items) |reloc| {
         const value = atom.relocationValue(reloc, wasm_bin);
         log.debug("Relocating '{s}' referenced in '{s}' offset=0x{x:0>8} value={d}", .{
-            (Wasm.SymbolLoc{ .file = atom.file, .index = reloc.index }).getName(wasm_bin),
+            (Wasm.SymbolLoc{ .file = atom.file, .index = @enumFromInt(reloc.index) }).getName(wasm_bin),
             symbol_name,
             reloc.offset,
             value,
@@ -119,7 +114,7 @@ pub fn resolveRelocs(atom: *Atom, wasm_bin: *const Wasm) void {
 /// All values will be represented as a `u64` as all values can fit within it.
 /// The final value must be casted to the correct size.
 fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wasm) u64 {
-    const target_loc = (Wasm.SymbolLoc{ .file = atom.file, .index = relocation.index }).finalLoc(wasm_bin);
+    const target_loc = (Wasm.SymbolLoc{ .file = atom.file, .index = @enumFromInt(relocation.index) }).finalLoc(wasm_bin);
     const symbol = target_loc.getSymbol(wasm_bin);
     if (relocation.relocation_type != .R_WASM_TYPE_INDEX_LEB and
         symbol.tag != .section and
@@ -135,7 +130,7 @@ fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wa
         .R_WASM_TABLE_INDEX_I64,
         .R_WASM_TABLE_INDEX_SLEB,
         .R_WASM_TABLE_INDEX_SLEB64,
-        => return wasm_bin.function_table.get(.{ .file = atom.file, .index = relocation.index }) orelse 0,
+        => return wasm_bin.function_table.get(.{ .file = atom.file, .index = @enumFromInt(relocation.index) }) orelse 0,
         .R_WASM_TYPE_INDEX_LEB => {
             const obj_file = wasm_bin.file(atom.file) orelse return relocation.index;
             const original_type = obj_file.funcTypes()[relocation.index];
@@ -195,6 +190,7 @@ fn thombstone(atom: Atom, wasm: *const Wasm) ?i64 {
     }
     return null;
 }
+
 const leb = std.leb;
 const log = std.log.scoped(.link);
 const mem = std.mem;
src/link/Wasm/file.zig
@@ -20,10 +20,10 @@ pub const File = union(enum) {
         };
     }
 
-    pub fn symbol(file: File, index: u32) *Symbol {
+    pub fn symbol(file: File, index: Symbol.Index) *Symbol {
         return switch (file) {
-            .zig_object => |obj| &obj.symbols.items[index],
-            .object => |obj| &obj.symtable[index],
+            .zig_object => |obj| &obj.symbols.items[@intFromEnum(index)],
+            .object => |obj| &obj.symtable[@intFromEnum(index)],
         };
     }
 
@@ -34,20 +34,20 @@ pub const File = union(enum) {
         };
     }
 
-    pub fn symbolName(file: File, index: u32) []const u8 {
+    pub fn symbolName(file: File, index: Symbol.Index) []const u8 {
         switch (file) {
             .zig_object => |obj| {
-                const sym = obj.symbols.items[index];
+                const sym = obj.symbols.items[@intFromEnum(index)];
                 return obj.string_table.get(sym.name).?;
             },
             .object => |obj| {
-                const sym = obj.symtable[index];
+                const sym = obj.symtable[@intFromEnum(index)];
                 return obj.string_table.get(sym.name);
             },
         }
     }
 
-    pub fn parseSymbolIntoAtom(file: File, wasm_file: *Wasm, index: u32) !AtomIndex {
+    pub fn parseSymbolIntoAtom(file: File, wasm_file: *Wasm, index: Symbol.Index) !AtomIndex {
         return switch (file) {
             inline else => |obj| obj.parseSymbolIntoAtom(wasm_file, index),
         };
@@ -55,10 +55,10 @@ pub const File = union(enum) {
 
     /// For a given symbol index, find its corresponding import.
     /// Asserts import exists.
-    pub fn import(file: File, symbol_index: u32) types.Import {
+    pub fn import(file: File, symbol_index: Symbol.Index) types.Import {
         return switch (file) {
             .zig_object => |obj| obj.imports.get(symbol_index).?,
-            .object => |obj| obj.findImport(obj.symtable[symbol_index]),
+            .object => |obj| obj.findImport(obj.symtable[@intFromEnum(symbol_index)]),
         };
     }
 
@@ -89,14 +89,14 @@ pub const File = union(enum) {
         };
     }
 
-    pub fn function(file: File, sym_index: u32) std.wasm.Func {
+    pub fn function(file: File, sym_index: Symbol.Index) std.wasm.Func {
         switch (file) {
             .zig_object => |obj| {
-                const sym = obj.symbols.items[sym_index];
+                const sym = obj.symbols.items[@intFromEnum(sym_index)];
                 return obj.functions.items[sym.index];
             },
             .object => |obj| {
-                const sym = obj.symtable[sym_index];
+                const sym = obj.symtable[@intFromEnum(sym_index)];
                 return obj.functions[sym.index - obj.imported_functions_count];
             },
         }
src/link/Wasm/Object.zig
@@ -907,10 +907,10 @@ fn assertEnd(reader: anytype) !void {
 }
 
 /// Parses an object file into atoms, for code and data sections
-pub fn parseSymbolIntoAtom(object: *Object, wasm: *Wasm, symbol_index: u32) !Atom.Index {
+pub fn parseSymbolIntoAtom(object: *Object, wasm: *Wasm, symbol_index: Symbol.Index) !Atom.Index {
     const comp = wasm.base.comp;
     const gpa = comp.gpa;
-    const symbol = &object.symtable[symbol_index];
+    const symbol = &object.symtable[@intFromEnum(symbol_index)];
     const relocatable_data: RelocatableData = switch (symbol.tag) {
         .function => object.relocatable_data.get(.code).?[symbol.index - object.imported_functions_count],
         .data => object.relocatable_data.get(.data).?[symbol.index],
@@ -953,7 +953,7 @@ pub fn parseSymbolIntoAtom(object: *Object, wasm: *Wasm, symbol_index: u32) !Ato
                 => {
                     try wasm.function_table.put(gpa, .{
                         .file = object.index,
-                        .index = reloc.index,
+                        .index = @enumFromInt(reloc.index),
                     }, 0);
                 },
                 .R_WASM_GLOBAL_INDEX_I32,
@@ -961,7 +961,7 @@ pub fn parseSymbolIntoAtom(object: *Object, wasm: *Wasm, symbol_index: u32) !Ato
                 => {
                     const sym = object.symtable[reloc.index];
                     if (sym.tag != .global) {
-                        try wasm.got_symbols.append(gpa, .{ .file = object.index, .index = reloc.index });
+                        try wasm.got_symbols.append(gpa, .{ .file = object.index, .index = @enumFromInt(reloc.index) });
                     }
                 },
                 else => {},
src/link/Wasm/Symbol.zig
@@ -1,12 +1,8 @@
-//! Represents a wasm symbol. Containing all of its properties,
+//! Represents a WebAssembly symbol. Containing all of its properties,
 //! as well as providing helper methods to determine its functionality
 //! and how it will/must be linked.
 //! The name of the symbol can be found by providing the offset, found
 //! on the `name` field, to a string table in the wasm binary or object file.
-const Symbol = @This();
-
-const std = @import("std");
-const types = @import("types.zig");
 
 /// Bitfield containings flags for a symbol
 /// Can contain any of the flags defined in `Flag`
@@ -24,6 +20,12 @@ tag: Tag,
 /// This differs from the offset of an `Atom` which is relative to the start of a segment.
 virtual_address: u32,
 
+/// Represents a symbol index where `null` represents an invalid index.
+pub const Index = enum(u32) {
+    null,
+    _,
+};
+
 pub const Tag = enum {
     function,
     data,
@@ -202,3 +204,7 @@ pub fn format(symbol: Symbol, comptime fmt: []const u8, options: std.fmt.FormatO
         .{ kind_fmt, binding, visible, symbol.index, symbol.name, undef },
     );
 }
+
+const std = @import("std");
+const types = @import("types.zig");
+const Symbol = @This();
src/link/Wasm/ZigObject.zig
@@ -17,7 +17,7 @@ functions: std.ArrayListUnmanaged(std.wasm.Func) = .{},
 /// List of indexes pointing to an entry within the `functions` list which has been removed.
 functions_free_list: std.ArrayListUnmanaged(u32) = .{},
 /// Map of symbol locations, represented by its `types.Import`.
-imports: std.AutoHashMapUnmanaged(u32, types.Import) = .{},
+imports: std.AutoHashMapUnmanaged(Symbol.Index, types.Import) = .{},
 /// List of WebAssembly globals.
 globals: std.ArrayListUnmanaged(std.wasm.Global) = .{},
 /// Mapping between an `Atom` and its type index representing the Wasm
@@ -26,9 +26,9 @@ atom_types: std.AutoHashMapUnmanaged(Atom.Index, u32) = .{},
 /// List of all symbols generated by Zig code.
 symbols: std.ArrayListUnmanaged(Symbol) = .{},
 /// Map from symbol name offset to their index into the `symbols` list.
-global_syms: std.AutoHashMapUnmanaged(u32, u32) = .{},
+global_syms: std.AutoHashMapUnmanaged(u32, Symbol.Index) = .{},
 /// List of symbol indexes which are free to be used.
-symbols_free_list: std.ArrayListUnmanaged(u32) = .{},
+symbols_free_list: std.ArrayListUnmanaged(Symbol.Index) = .{},
 /// Extra metadata about the linking section, such as alignment of segments and their name.
 segment_info: std.ArrayListUnmanaged(types.Segment) = .{},
 /// List of indexes which contain a free slot in the `segment_info` list.
@@ -42,7 +42,7 @@ anon_decls: std.AutoArrayHashMapUnmanaged(InternPool.Index, Atom.Index) = .{},
 /// During initializion, a symbol with corresponding atom will be created that is
 /// used to perform relocations to the pointer of this table.
 /// The actual table is populated during `flush`.
-error_table_symbol: ?u32 = null,
+error_table_symbol: Symbol.Index = .null,
 /// Amount of functions in the `import` sections.
 imported_functions_count: u32 = 0,
 /// Amount of globals in the `import` section.
@@ -50,7 +50,7 @@ imported_globals_count: u32 = 0,
 /// Symbol index representing the stack pointer. This will be set upon initializion
 /// of a new `ZigObject`. Codegen will make calls into this to create relocations for
 /// this symbol each time the stack pointer is moved.
-stack_pointer_sym: u32,
+stack_pointer_sym: Symbol.Index,
 /// Debug information for the Zig module.
 dwarf: ?Dwarf = null,
 // Debug section atoms. These are only set when the current compilation
@@ -83,10 +83,10 @@ debug_str_index: ?u32 = null,
 debug_abbrev_index: ?u32 = null,
 
 const DeclInfo = struct {
-    atom: Atom.Index = std.math.maxInt(Atom.Index),
-    exports: std.ArrayListUnmanaged(u32) = .{},
+    atom: Atom.Index = .null,
+    exports: std.ArrayListUnmanaged(Symbol.Index) = .{},
 
-    fn @"export"(di: DeclInfo, zig_object: *const ZigObject, name: []const u8) ?u32 {
+    fn @"export"(di: DeclInfo, zig_object: *const ZigObject, name: []const u8) ?Symbol.Index {
         for (di.exports.items) |sym_index| {
             const sym_name_index = zig_object.symbol(sym_index).name;
             const sym_name = zig_object.string_table.getAssumeExists(sym_name_index);
@@ -97,11 +97,11 @@ const DeclInfo = struct {
         return null;
     }
 
-    fn appendExport(di: *DeclInfo, gpa: std.mem.Allocator, sym_index: u32) !void {
+    fn appendExport(di: *DeclInfo, gpa: std.mem.Allocator, sym_index: Symbol.Index) !void {
         return di.exports.append(gpa, sym_index);
     }
 
-    fn deleteExport(di: *DeclInfo, sym_index: u32) void {
+    fn deleteExport(di: *DeclInfo, sym_index: Symbol.Index) void {
         for (di.exports.items, 0..) |idx, index| {
             if (idx == sym_index) {
                 _ = di.exports.swapRemove(index);
@@ -138,8 +138,8 @@ fn createStackPointer(zig_object: *ZigObject, wasm_file: *Wasm) !void {
     zig_object.stack_pointer_sym = sym_index;
 }
 
-fn symbol(zig_object: *const ZigObject, index: u32) *Symbol {
-    return &zig_object.symbols.items[index];
+fn symbol(zig_object: *const ZigObject, index: Symbol.Index) *Symbol {
+    return &zig_object.symbols.items[@intFromEnum(index)];
 }
 
 /// Frees and invalidates all memory of the incrementally compiled Zig module.
@@ -192,7 +192,7 @@ pub fn deinit(zig_object: *ZigObject, wasm_file: *Wasm) void {
 
 /// Allocates a new symbol and returns its index.
 /// Will re-use slots when a symbol was freed at an earlier stage.
-pub fn allocateSymbol(zig_object: *ZigObject, gpa: std.mem.Allocator) !u32 {
+pub fn allocateSymbol(zig_object: *ZigObject, gpa: std.mem.Allocator) !Symbol.Index {
     try zig_object.symbols.ensureUnusedCapacity(gpa, 1);
     const sym: Symbol = .{
         .name = std.math.maxInt(u32), // will be set after updateDecl as well as during atom creation for decls
@@ -202,10 +202,10 @@ pub fn allocateSymbol(zig_object: *ZigObject, gpa: std.mem.Allocator) !u32 {
         .virtual_address = std.math.maxInt(u32), // will be set during atom allocation
     };
     if (zig_object.symbols_free_list.popOrNull()) |index| {
-        zig_object.symbols.items[index] = sym;
+        zig_object.symbols.items[@intFromEnum(index)] = sym;
         return index;
     }
-    const index = @as(u32, @intCast(zig_object.symbols.items.len));
+    const index: Symbol.Index = @enumFromInt(zig_object.symbols.items.len);
     zig_object.symbols.appendAssumeCapacity(sym);
     return index;
 }
@@ -247,7 +247,7 @@ pub fn updateDecl(
         .{ .ty = decl.ty, .val = val },
         &code_writer,
         .none,
-        .{ .parent_atom_index = atom.sym_index },
+        .{ .parent_atom_index = @intFromEnum(atom.sym_index) },
     );
 
     const code = switch (res) {
@@ -464,7 +464,7 @@ pub fn lowerUnnamedConst(zig_object: *ZigObject, wasm_file: *Wasm, tv: TypedValu
     switch (try zig_object.lowerConst(wasm_file, name, tv, decl.srcLoc(mod))) {
         .ok => |atom_index| {
             try wasm_file.getAtomPtr(parent_atom_index).locals.append(gpa, atom_index);
-            return wasm_file.getAtom(atom_index).getSymbolIndex().?;
+            return @intFromEnum(wasm_file.getAtom(atom_index).sym_index);
         },
         .fail => |em| {
             decl.analysis = .codegen_failure;
@@ -494,7 +494,7 @@ fn lowerConst(zig_object: *ZigObject, wasm_file: *Wasm, name: []const u8, tv: Ty
         atom.alignment = tv.ty.abiAlignment(mod);
         const segment_name = try std.mem.concat(gpa, u8, &.{ ".rodata.", name });
         errdefer gpa.free(segment_name);
-        zig_object.symbols.items[sym_index] = .{
+        zig_object.symbol(sym_index).* = .{
             .name = try zig_object.string_table.insert(gpa, name),
             .flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
             .tag = .data,
@@ -513,7 +513,7 @@ fn lowerConst(zig_object: *ZigObject, wasm_file: *Wasm, name: []const u8, tv: Ty
             &value_bytes,
             .none,
             .{
-                .parent_atom_index = atom.sym_index,
+                .parent_atom_index = @intFromEnum(atom.sym_index),
                 .addend = null,
             },
         );
@@ -534,9 +534,9 @@ fn lowerConst(zig_object: *ZigObject, wasm_file: *Wasm, name: []const u8, tv: Ty
 /// Returns the symbol index of the error name table.
 ///
 /// When the symbol does not yet exist, it will create a new one instead.
-pub fn getErrorTableSymbol(zig_object: *ZigObject, wasm_file: *Wasm) !u32 {
-    if (zig_object.error_table_symbol) |sym| {
-        return sym;
+pub fn getErrorTableSymbol(zig_object: *ZigObject, wasm_file: *Wasm) !Symbol.Index {
+    if (zig_object.error_table_symbol != .null) {
+        return zig_object.error_table_symbol;
     }
 
     // no error was referenced yet, so create a new symbol and atom for it
@@ -561,7 +561,7 @@ pub fn getErrorTableSymbol(zig_object: *ZigObject, wasm_file: *Wasm) !u32 {
         .virtual_address = undefined,
     };
 
-    log.debug("Error name table was created with symbol index: ({d})", .{sym_index});
+    log.debug("Error name table was created with symbol index: ({d})", .{@intFromEnum(sym_index)});
     zig_object.error_table_symbol = sym_index;
     return sym_index;
 }
@@ -571,9 +571,9 @@ pub fn getErrorTableSymbol(zig_object: *ZigObject, wasm_file: *Wasm) !u32 {
 /// This creates a table that consists of pointers and length to each error name.
 /// The table is what is being pointed to within the runtime bodies that are generated.
 fn populateErrorNameTable(zig_object: *ZigObject, wasm_file: *Wasm) !void {
-    const symbol_index = zig_object.error_table_symbol orelse return;
+    if (zig_object.error_table_symbol == .null) return;
     const gpa = wasm_file.base.comp.gpa;
-    const atom_index = wasm_file.symbol_atom.get(.{ .file = zig_object.index, .index = symbol_index }).?;
+    const atom_index = wasm_file.symbol_atom.get(.{ .file = zig_object.index, .index = zig_object.error_table_symbol }).?;
 
     // Rather than creating a symbol for each individual error name,
     // we create a symbol for the entire region of error names. We then calculate
@@ -584,7 +584,7 @@ fn populateErrorNameTable(zig_object: *ZigObject, wasm_file: *Wasm) !void {
     names_atom.alignment = .@"1";
     const sym_name = try zig_object.string_table.insert(gpa, "__zig_err_names");
     const segment_name = try gpa.dupe(u8, ".rodata.__zig_err_names");
-    const names_symbol = &zig_object.symbols.items[names_sym_index];
+    const names_symbol = zig_object.symbol(names_sym_index);
     names_symbol.* = .{
         .name = sym_name,
         .tag = .data,
@@ -611,7 +611,7 @@ fn populateErrorNameTable(zig_object: *ZigObject, wasm_file: *Wasm) !void {
         try atom.code.writer(gpa).writeInt(u32, len - 1, .little);
         // create relocation to the error name
         try atom.relocs.append(gpa, .{
-            .index = names_atom.sym_index,
+            .index = @intFromEnum(names_atom.sym_index),
             .relocation_type = .R_WASM_MEMORY_ADDR_I32,
             .offset = offset,
             .addend = @as(i32, @intCast(addend)),
@@ -638,7 +638,7 @@ pub fn addOrUpdateImport(
     /// Name of the import
     name: []const u8,
     /// Symbol index that is external
-    symbol_index: u32,
+    symbol_index: Symbol.Index,
     /// Optional library name (i.e. `extern "c" fn foo() void`
     lib_name: ?[:0]const u8,
     /// The index of the type that represents the function signature
@@ -647,7 +647,7 @@ pub fn addOrUpdateImport(
     type_index: ?u32,
 ) !void {
     const gpa = wasm_file.base.comp.gpa;
-    std.debug.assert(symbol_index != 0);
+    std.debug.assert(symbol_index != .null);
     // For the import name, we use the decl's name, rather than the fully qualified name
     // Also mangle the name when the lib name is set and not equal to "C" so imports with the same
     // name but different module can be resolved correctly.
@@ -659,7 +659,7 @@ pub fn addOrUpdateImport(
     defer if (mangle_name) gpa.free(full_name);
 
     const decl_name_index = try zig_object.string_table.insert(gpa, full_name);
-    const sym: *Symbol = &zig_object.symbols.items[symbol_index];
+    const sym: *Symbol = &zig_object.symbols.items[@intFromEnum(symbol_index)];
     sym.setUndefined(true);
     sym.setGlobal(true);
     sym.name = decl_name_index;
@@ -689,7 +689,7 @@ pub fn addOrUpdateImport(
 /// such as an exported or imported symbol.
 /// If the symbol does not yet exist, creates a new one symbol instead
 /// and then returns the index to it.
-pub fn getGlobalSymbol(zig_object: *ZigObject, gpa: std.mem.Allocator, name: []const u8) !u32 {
+pub fn getGlobalSymbol(zig_object: *ZigObject, gpa: std.mem.Allocator, name: []const u8) !Symbol.Index {
     const name_index = try zig_object.string_table.insert(gpa, name);
     const gop = try zig_object.global_syms.getOrPut(gpa, name_index);
     if (gop.found_existing) {
@@ -707,12 +707,12 @@ pub fn getGlobalSymbol(zig_object: *ZigObject, gpa: std.mem.Allocator, name: []c
     sym.setUndefined(true);
 
     const sym_index = if (zig_object.symbols_free_list.popOrNull()) |index| index else blk: {
-        const index: u32 = @intCast(zig_object.symbols.items.len);
+        const index: Symbol.Index = @enumFromInt(zig_object.symbols.items.len);
         try zig_object.symbols.ensureUnusedCapacity(gpa, 1);
         zig_object.symbols.items.len += 1;
         break :blk index;
     };
-    zig_object.symbols.items[sym_index] = sym;
+    zig_object.symbol(sym_index).* = sym;
     gop.value_ptr.* = sym_index;
     return sym_index;
 }
@@ -731,10 +731,10 @@ pub fn getDeclVAddr(
     const decl = mod.declPtr(decl_index);
 
     const target_atom_index = try zig_object.getOrCreateAtomForDecl(wasm_file, decl_index);
-    const target_symbol_index = wasm_file.getAtom(target_atom_index).sym_index;
+    const target_symbol_index = @intFromEnum(wasm_file.getAtom(target_atom_index).sym_index);
 
     std.debug.assert(reloc_info.parent_atom_index != 0);
-    const atom_index = wasm_file.symbol_atom.get(.{ .file = zig_object.index, .index = reloc_info.parent_atom_index }).?;
+    const atom_index = wasm_file.symbol_atom.get(.{ .file = zig_object.index, .index = @enumFromInt(reloc_info.parent_atom_index) }).?;
     const atom = wasm_file.getAtomPtr(atom_index);
     const is_wasm32 = target.cpu.arch == .wasm32;
     if (decl.ty.zigTypeTag(mod) == .Fn) {
@@ -769,9 +769,9 @@ pub fn getAnonDeclVAddr(
     const gpa = wasm_file.base.comp.gpa;
     const target = wasm_file.base.comp.root_mod.resolved_target.result;
     const atom_index = zig_object.anon_decls.get(decl_val).?;
-    const target_symbol_index = wasm_file.getAtom(atom_index).getSymbolIndex().?;
+    const target_symbol_index = @intFromEnum(wasm_file.getAtom(atom_index).sym_index);
 
-    const parent_atom_index = wasm_file.symbol_atom.get(.{ .file = zig_object.index, .index = reloc_info.parent_atom_index }).?;
+    const parent_atom_index = wasm_file.symbol_atom.get(.{ .file = zig_object.index, .index = @enumFromInt(reloc_info.parent_atom_index) }).?;
     const parent_atom = wasm_file.getAtomPtr(parent_atom_index);
     const is_wasm32 = target.cpu.arch == .wasm32;
     const mod = wasm_file.base.comp.module.?;
@@ -930,17 +930,7 @@ pub fn freeDecl(zig_object: *ZigObject, wasm_file: *Wasm, decl_index: InternPool
     //     dwarf.freeDecl(decl_index);
     // }
 
-    if (atom.next) |next_atom_index| {
-        const next_atom = wasm_file.getAtomPtr(next_atom_index);
-        next_atom.prev = atom.prev;
-        atom.next = null;
-    }
-    if (atom.prev) |prev_index| {
-        const prev_atom = wasm_file.getAtomPtr(prev_index);
-        prev_atom.next = atom.next;
-        atom.prev = null;
-    }
-
+    atom.prev = null;
     sym.tag = .dead;
     if (sym.isGlobal()) {
         std.debug.assert(zig_object.global_syms.remove(atom.sym_index));
@@ -998,7 +988,7 @@ fn setupErrorsLen(zig_object: *ZigObject, wasm_file: *Wasm) !void {
     // if not, allcoate a new atom.
     const atom_index = if (wasm_file.symbol_atom.get(.{ .file = zig_object.index, .index = sym_index })) |index| blk: {
         const atom = wasm_file.getAtomPtr(index);
-        atom.prev = null;
+        atom.prev = .null;
         atom.deinit(gpa);
         break :blk index;
     } else idx: {
@@ -1022,7 +1012,7 @@ fn setupErrorsLen(zig_object: *ZigObject, wasm_file: *Wasm) !void {
     try atom.code.writer(gpa).writeInt(u16, @intCast(errors_len), .little);
 }
 
-fn findGlobalSymbol(zig_object: *ZigObject, name: []const u8) ?u32 {
+fn findGlobalSymbol(zig_object: *ZigObject, name: []const u8) ?Symbol.Index {
     const offset = zig_object.string_table.getOffset(name) orelse return null;
     return zig_object.global_syms.get(offset);
 }
@@ -1121,7 +1111,7 @@ pub fn storeDeclType(zig_object: *ZigObject, gpa: std.mem.Allocator, decl_index:
 /// The symbols in ZigObject are already represented by an atom as we need to store its data.
 /// So rather than creating a new Atom and returning its index, we use this oppertunity to scan
 /// its relocations and create any GOT symbols or function table indexes it may require.
-pub fn parseSymbolIntoAtom(zig_object: *ZigObject, wasm_file: *Wasm, index: u32) !Atom.Index {
+pub fn parseSymbolIntoAtom(zig_object: *ZigObject, wasm_file: *Wasm, index: Symbol.Index) !Atom.Index {
     const gpa = wasm_file.base.comp.gpa;
     const loc: Wasm.SymbolLoc = .{ .file = zig_object.index, .index = index };
     const atom_index = wasm_file.symbol_atom.get(loc).?;
@@ -1129,6 +1119,7 @@ pub fn parseSymbolIntoAtom(zig_object: *ZigObject, wasm_file: *Wasm, index: u32)
     try wasm_file.appendAtomAtIndex(final_index, atom_index);
     const atom = wasm_file.getAtom(atom_index);
     for (atom.relocs.items) |reloc| {
+        const reloc_index: Symbol.Index = @enumFromInt(reloc.index);
         switch (reloc.relocation_type) {
             .R_WASM_TABLE_INDEX_I32,
             .R_WASM_TABLE_INDEX_I64,
@@ -1137,17 +1128,17 @@ pub fn parseSymbolIntoAtom(zig_object: *ZigObject, wasm_file: *Wasm, index: u32)
             => {
                 try wasm_file.function_table.put(gpa, .{
                     .file = zig_object.index,
-                    .index = reloc.index,
+                    .index = reloc_index,
                 }, 0);
             },
             .R_WASM_GLOBAL_INDEX_I32,
             .R_WASM_GLOBAL_INDEX_LEB,
             => {
-                const sym = zig_object.symbol(reloc.index);
+                const sym = zig_object.symbol(reloc_index);
                 if (sym.tag != .global) {
                     try wasm_file.got_symbols.append(gpa, .{
                         .file = zig_object.index,
-                        .index = reloc.index,
+                        .index = reloc_index,
                     });
                 }
             },
@@ -1166,10 +1157,10 @@ pub fn createFunction(
     func_ty: std.wasm.Type,
     function_body: *std.ArrayList(u8),
     relocations: *std.ArrayList(types.Relocation),
-) !u32 {
+) !Symbol.Index {
     const gpa = wasm_file.base.comp.gpa;
     const sym_index = try zig_object.allocateSymbol(gpa);
-    const sym = &zig_object.symbols.items[sym_index];
+    const sym = zig_object.symbol(sym_index);
     sym.tag = .function;
     sym.name = try zig_object.string_table.insert(gpa, symbol_name);
     const type_index = try zig_object.putOrGetFuncType(gpa, func_ty);
src/link/Wasm.zig
@@ -127,7 +127,7 @@ func_types: std.ArrayListUnmanaged(std.wasm.Type) = .{},
 /// This allows us to map multiple symbols to the same function.
 functions: std.AutoArrayHashMapUnmanaged(
     struct { file: File.Index, index: u32 },
-    struct { func: std.wasm.Func, sym_index: u32 },
+    struct { func: std.wasm.Func, sym_index: Symbol.Index },
 ) = .{},
 /// Output global section
 wasm_globals: std.ArrayListUnmanaged(std.wasm.Global) = .{},
@@ -208,13 +208,9 @@ pub const Segment = struct {
     }
 };
 
-pub const Export = struct {
-    sym_index: ?u32 = null,
-};
-
 pub const SymbolLoc = struct {
     /// The index of the symbol within the specified file
-    index: u32,
+    index: Symbol.Index,
     /// The index of the object file where the symbol resides.
     file: File.Index,
 
@@ -226,7 +222,7 @@ pub const SymbolLoc = struct {
         if (wasm_file.file(loc.file)) |obj_file| {
             return obj_file.symbol(loc.index);
         }
-        return &wasm_file.synthetic_symbols.items[loc.index];
+        return &wasm_file.synthetic_symbols.items[@intFromEnum(loc.index)];
     }
 
     /// From a given location, returns the name of the symbol.
@@ -237,7 +233,8 @@ pub const SymbolLoc = struct {
         if (wasm_file.file(loc.file)) |obj_file| {
             return obj_file.symbolName(loc.index);
         }
-        return wasm_file.string_table.get(wasm_file.synthetic_symbols.items[loc.index].name);
+        const sym = wasm_file.synthetic_symbols.items[@intFromEnum(loc.index)];
+        return wasm_file.string_table.get(sym.name);
     }
 
     /// From a given symbol location, returns the final location.
@@ -272,7 +269,7 @@ pub const InitFuncLoc = struct {
 
     /// Turns the given `InitFuncLoc` into a `SymbolLoc`
     fn getSymbolLoc(loc: InitFuncLoc) SymbolLoc {
-        return .{ .file = loc.file, .index = loc.index };
+        return .{ .file = loc.file, .index = @enumFromInt(loc.index) };
     }
 
     /// Returns true when `lhs` has a higher priority (e.i. value closer to 0) than `rhs`.
@@ -566,7 +563,7 @@ pub fn createEmpty(
             var zig_object: ZigObject = .{
                 .index = index,
                 .path = try std.fmt.allocPrint(gpa, "{s}.o", .{std.fs.path.stem(zcu.main_mod.root_src_path)}),
-                .stack_pointer_sym = undefined,
+                .stack_pointer_sym = .null,
             };
             try zig_object.init(wasm);
             try wasm.files.append(gpa, .{ .zig_object = zig_object });
@@ -607,7 +604,7 @@ pub fn addOrUpdateImport(
     /// Name of the import
     name: []const u8,
     /// Symbol index that is external
-    symbol_index: u32,
+    symbol_index: Symbol.Index,
     /// Optional library name (i.e. `extern "c" fn foo() void`
     lib_name: ?[:0]const u8,
     /// The index of the type that represents the function signature
@@ -627,7 +624,7 @@ fn createSyntheticSymbol(wasm: *Wasm, name: []const u8, tag: Symbol.Tag) !Symbol
 }
 
 fn createSyntheticSymbolOffset(wasm: *Wasm, name_offset: u32, tag: Symbol.Tag) !SymbolLoc {
-    const sym_index = @as(u32, @intCast(wasm.synthetic_symbols.items.len));
+    const sym_index: Symbol.Index = @enumFromInt(wasm.synthetic_symbols.items.len);
     const loc: SymbolLoc = .{ .index = sym_index, .file = .null };
     const gpa = wasm.base.comp.gpa;
     try wasm.synthetic_symbols.append(gpa, .{
@@ -670,9 +667,9 @@ fn parseObjectFile(wasm: *Wasm, path: []const u8) !bool {
 }
 
 /// Creates a new empty `Atom` and returns its `Atom.Index`
-pub fn createAtom(wasm: *Wasm, sym_index: u32, file_index: File.Index) !Atom.Index {
+pub fn createAtom(wasm: *Wasm, sym_index: Symbol.Index, file_index: File.Index) !Atom.Index {
     const gpa = wasm.base.comp.gpa;
-    const index: Atom.Index = @intCast(wasm.managed_atoms.items.len);
+    const index: Atom.Index = @enumFromInt(wasm.managed_atoms.items.len);
     const atom = try wasm.managed_atoms.addOne(gpa);
     atom.* = .{ .file = file_index, .sym_index = sym_index };
     try wasm.symbol_atom.putNoClobber(gpa, atom.symbolLoc(), index);
@@ -681,11 +678,11 @@ pub fn createAtom(wasm: *Wasm, sym_index: u32, file_index: File.Index) !Atom.Ind
 }
 
 pub inline fn getAtom(wasm: *const Wasm, index: Atom.Index) Atom {
-    return wasm.managed_atoms.items[index];
+    return wasm.managed_atoms.items[@intFromEnum(index)];
 }
 
 pub inline fn getAtomPtr(wasm: *Wasm, index: Atom.Index) *Atom {
-    return &wasm.managed_atoms.items[index];
+    return &wasm.managed_atoms.items[@intFromEnum(index)];
 }
 
 /// Parses an archive file and will then parse each object file
@@ -757,7 +754,7 @@ fn resolveSymbolsInObject(wasm: *Wasm, file_index: File.Index) !void {
     log.debug("Resolving symbols in object: '{s}'", .{obj_file.path()});
 
     for (obj_file.symbols(), 0..) |symbol, i| {
-        const sym_index: u32 = @intCast(i);
+        const sym_index: Symbol.Index = @enumFromInt(i);
         const location: SymbolLoc = .{ .file = file_index, .index = sym_index };
         const sym_name = obj_file.string(symbol.name);
         if (mem.eql(u8, sym_name, "__indirect_function_table")) {
@@ -1489,7 +1486,7 @@ pub fn lowerUnnamedConst(wasm: *Wasm, tv: TypedValue, decl_index: InternPool.Dec
 /// such as an exported or imported symbol.
 /// If the symbol does not yet exist, creates a new one symbol instead
 /// and then returns the index to it.
-pub fn getGlobalSymbol(wasm: *Wasm, name: []const u8, lib_name: ?[]const u8) !u32 {
+pub fn getGlobalSymbol(wasm: *Wasm, name: []const u8, lib_name: ?[]const u8) !Symbol.Index {
     _ = lib_name;
     return wasm.zigObjectPtr().?.getGlobalSymbol(wasm.base.comp.gpa, name);
 }
@@ -1609,19 +1606,20 @@ fn allocateAtoms(wasm: *Wasm) !void {
             const sym = if (wasm.file(symbol_loc.file)) |obj_file|
                 obj_file.symbol(symbol_loc.index).*
             else
-                wasm.synthetic_symbols.items[symbol_loc.index];
+                wasm.synthetic_symbols.items[@intFromEnum(symbol_loc.index)];
 
             // Dead symbols must be unlinked from the linked-list to prevent them
             // from being emit into the binary.
             if (sym.isDead()) {
-                if (entry.value_ptr.* == atom_index and atom.prev != null) {
+                if (entry.value_ptr.* == atom_index and atom.prev != .null) {
                     // When the atom is dead and is also the first atom retrieved from wasm.atoms(index) we update
                     // the entry to point it to the previous atom to ensure we do not start with a dead symbol that
                     // was removed and therefore do not emit any code at all.
-                    entry.value_ptr.* = atom.prev.?;
+                    entry.value_ptr.* = atom.prev;
                 }
-                atom_index = atom.prev orelse break;
-                atom.prev = null;
+                if (atom.prev == .null) break;
+                atom_index = atom.prev;
+                atom.prev = .null;
                 continue;
             }
             offset = @intCast(atom.alignment.forward(offset));
@@ -1633,7 +1631,8 @@ fn allocateAtoms(wasm: *Wasm) !void {
                 atom.size,
             });
             offset += atom.size;
-            atom_index = atom.prev orelse break;
+            if (atom.prev == .null) break;
+            atom_index = atom.prev;
         }
         segment.size = @intCast(segment.alignment.forward(offset));
     }
@@ -1738,7 +1737,7 @@ fn setupInitFunctions(wasm: *Wasm) !void {
                 .file = file_index,
                 .priority = init_func.priority,
             });
-            try wasm.mark(.{ .index = init_func.symbol_index, .file = file_index });
+            try wasm.mark(.{ .index = @enumFromInt(init_func.symbol_index), .file = file_index });
         }
     }
 
@@ -1844,7 +1843,7 @@ pub fn createFunction(
     func_ty: std.wasm.Type,
     function_body: *std.ArrayList(u8),
     relocations: *std.ArrayList(Relocation),
-) !u32 {
+) !Symbol.Index {
     return wasm.zigObjectPtr().?.createFunction(wasm, symbol_name, func_ty, function_body, relocations);
 }
 
@@ -2324,11 +2323,11 @@ fn setupMemory(wasm: *Wasm) !void {
 /// From a given object's index and the index of the segment, returns the corresponding
 /// index of the segment within the final data section. When the segment does not yet
 /// exist, a new one will be initialized and appended. The new index will be returned in that case.
-pub fn getMatchingSegment(wasm: *Wasm, file_index: File.Index, symbol_index: u32) !u32 {
+pub fn getMatchingSegment(wasm: *Wasm, file_index: File.Index, symbol_index: Symbol.Index) !u32 {
     const comp = wasm.base.comp;
     const gpa = comp.gpa;
     const obj_file = wasm.file(file_index).?;
-    const symbol = obj_file.symbols()[symbol_index];
+    const symbol = obj_file.symbols()[@intFromEnum(symbol_index)];
     const index: u32 = @intCast(wasm.segments.items.len);
     const shared_memory = comp.config.shared_memory;
 
@@ -2889,8 +2888,8 @@ fn writeToFile(
                 try binary_writer.writeAll(atom.code.items);
 
                 current_offset += atom.size;
-                if (atom.prev) |prev| {
-                    atom_index = prev;
+                if (atom.prev != .null) {
+                    atom_index = atom.prev;
                 } else {
                     // also pad with zeroes when last atom to ensure
                     // segments are aligned.
@@ -2984,7 +2983,8 @@ fn writeToFile(
                 while (true) {
                     atom.resolveRelocs(wasm);
                     try debug_bytes.appendSlice(atom.code.items);
-                    atom = if (atom.prev) |prev| wasm.getAtomPtr(prev) else break;
+                    if (atom.prev == .null) break;
+                    atom = wasm.getAtomPtr(atom.prev);
                 }
                 try emitDebugSection(&binary_bytes, debug_bytes.items, item.name);
                 debug_bytes.clearRetainingCapacity();
@@ -3853,7 +3853,7 @@ fn emitCodeRelocations(
         size_offset += getULEB128Size(atom.size);
         for (atom.relocs.items) |relocation| {
             count += 1;
-            const sym_loc: SymbolLoc = .{ .file = atom.file, .index = relocation.index };
+            const sym_loc: SymbolLoc = .{ .file = atom.file, .index = @enumFromInt(relocation.index) };
             const symbol_index = symbol_table.get(sym_loc).?;
             try leb.writeULEB128(writer, @intFromEnum(relocation.relocation_type));
             const offset = atom.offset + relocation.offset + size_offset;
@@ -3864,7 +3864,8 @@ fn emitCodeRelocations(
             }
             log.debug("Emit relocation: {}", .{relocation});
         }
-        atom = if (atom.prev) |prev| wasm.getAtomPtr(prev) else break;
+        if (atom.prev == .null) break;
+        atom = wasm.getAtomPtr(atom.prev);
     }
     if (count == 0) return;
     var buf: [5]u8 = undefined;
@@ -3900,7 +3901,7 @@ fn emitDataRelocations(
             size_offset += getULEB128Size(atom.size);
             for (atom.relocs.items) |relocation| {
                 count += 1;
-                const sym_loc: SymbolLoc = .{ .file = atom.file, .index = relocation.index };
+                const sym_loc: SymbolLoc = .{ .file = atom.file, .index = @enumFromInt(relocation.index) };
                 const symbol_index = symbol_table.get(sym_loc).?;
                 try leb.writeULEB128(writer, @intFromEnum(relocation.relocation_type));
                 const offset = atom.offset + relocation.offset + size_offset;
@@ -3911,7 +3912,8 @@ fn emitDataRelocations(
                 }
                 log.debug("Emit relocation: {}", .{relocation});
             }
-            atom = if (atom.prev) |prev| wasm.getAtomPtr(prev) else break;
+            if (atom.prev == .null) break;
+            atom = wasm.getAtomPtr(atom.prev);
         }
     }
     if (count == 0) return;
@@ -3969,7 +3971,8 @@ pub fn storeDeclType(wasm: *Wasm, decl_index: InternPool.DeclIndex, func_type: s
 ///
 /// When the symbol does not yet exist, it will create a new one instead.
 pub fn getErrorTableSymbol(wasm_file: *Wasm) !u32 {
-    return wasm_file.zigObjectPtr().?.getErrorTableSymbol(wasm_file);
+    const sym_index = try wasm_file.zigObjectPtr().?.getErrorTableSymbol(wasm_file);
+    return @intFromEnum(sym_index);
 }
 
 /// For a given `InternPool.DeclIndex` returns its corresponding `Atom.Index`.
@@ -4029,7 +4032,7 @@ fn mark(wasm: *Wasm, loc: SymbolLoc) !void {
 
     const atom = wasm.getAtom(atom_index);
     for (atom.relocs.items) |reloc| {
-        const target_loc: SymbolLoc = .{ .index = reloc.index, .file = loc.file };
+        const target_loc: SymbolLoc = .{ .index = @enumFromInt(reloc.index), .file = loc.file };
         try wasm.mark(target_loc.finalLoc(wasm));
     }
 }