Commit f7596ae942

Andrew Kelley <andrew@ziglang.org>
2022-04-20 06:51:08
stage2: use indexes for Decl objects
Rather than allocating Decl objects with an Allocator, we instead allocate them with a SegmentedList. This provides four advantages: * Stable memory so that one thread can access a Decl object while another thread allocates additional Decl objects from this list. * It allows us to use u32 indexes to reference Decl objects rather than pointers, saving memory in Type, Value, and dependency sets. * Using integers to reference Decl objects rather than pointers makes serialization trivial. * It provides a unique integer to be used for anonymous symbol names, avoiding multi-threaded contention on an atomic counter.
1 parent 4f527e5
lib/std/segmented_list.zig
@@ -148,6 +148,24 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type
             return result;
         }
 
+        /// Reduce length to `new_len`.
+        /// Invalidates pointers for the elements at index new_len and beyond.
+        pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void {
+            assert(new_len <= self.len);
+            self.len = new_len;
+        }
+
+        /// Invalidates all element pointers.
+        pub fn clearRetainingCapacity(self: *Self) void {
+            self.items.len = 0;
+        }
+
+        /// Invalidates all element pointers.
+        pub fn clearAndFree(self: *Self, allocator: Allocator) void {
+            self.setCapacity(allocator, 0) catch unreachable;
+            self.items.len = 0;
+        }
+
         /// Grows or shrinks capacity to match usage.
         /// TODO update this and related methods to match the conventions set by ArrayList
         pub fn setCapacity(self: *Self, allocator: Allocator, new_capacity: usize) Allocator.Error!void {
src/arch/aarch64/CodeGen.zig
@@ -237,8 +237,10 @@ pub fn generate(
         @panic("Attempted to compile for architecture that was disabled by build configuration");
     }
 
-    assert(module_fn.owner_decl.has_tv);
-    const fn_type = module_fn.owner_decl.ty;
+    const mod = bin_file.options.module.?;
+    const fn_owner_decl = mod.declPtr(module_fn.owner_decl);
+    assert(fn_owner_decl.has_tv);
+    const fn_type = fn_owner_decl.ty;
 
     var branch_stack = std.ArrayList(Branch).init(bin_file.allocator);
     defer {
@@ -819,9 +821,9 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {
         return @as(u32, 0);
     }
 
-    const target = self.target.*;
     const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
-        return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(target)});
+        const mod = self.bin_file.options.module.?;
+        return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)});
     };
     // TODO swap this for inst.ty.ptrAlign
     const abi_align = elem_ty.abiAlignment(self.target.*);
@@ -830,9 +832,9 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {
 
 fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
     const elem_ty = self.air.typeOfIndex(inst);
-    const target = self.target.*;
     const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
-        return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(target)});
+        const mod = self.bin_file.options.module.?;
+        return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)});
     };
     const abi_align = elem_ty.abiAlignment(self.target.*);
     if (abi_align > self.stack_align)
@@ -1422,7 +1424,7 @@ fn binOp(
     lhs_ty: Type,
     rhs_ty: Type,
 ) InnerError!MCValue {
-    const target = self.target.*;
+    const mod = self.bin_file.options.module.?;
     switch (tag) {
         .add,
         .sub,
@@ -1432,7 +1434,7 @@ fn binOp(
                 .Float => return self.fail("TODO binary operations on floats", .{}),
                 .Vector => return self.fail("TODO binary operations on vectors", .{}),
                 .Int => {
-                    assert(lhs_ty.eql(rhs_ty, target));
+                    assert(lhs_ty.eql(rhs_ty, mod));
                     const int_info = lhs_ty.intInfo(self.target.*);
                     if (int_info.bits <= 64) {
                         // Only say yes if the operation is
@@ -1483,7 +1485,7 @@ fn binOp(
             switch (lhs_ty.zigTypeTag()) {
                 .Vector => return self.fail("TODO binary operations on vectors", .{}),
                 .Int => {
-                    assert(lhs_ty.eql(rhs_ty, target));
+                    assert(lhs_ty.eql(rhs_ty, mod));
                     const int_info = lhs_ty.intInfo(self.target.*);
                     if (int_info.bits <= 64) {
                         // TODO add optimisations for multiplication
@@ -1534,7 +1536,7 @@ fn binOp(
             switch (lhs_ty.zigTypeTag()) {
                 .Vector => return self.fail("TODO binary operations on vectors", .{}),
                 .Int => {
-                    assert(lhs_ty.eql(rhs_ty, target));
+                    assert(lhs_ty.eql(rhs_ty, mod));
                     const int_info = lhs_ty.intInfo(self.target.*);
                     if (int_info.bits <= 64) {
                         // TODO implement bitwise operations with immediates
@@ -2425,12 +2427,12 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
     const ty = self.air.typeOfIndex(inst);
 
     const result = self.args[arg_index];
-    const target = self.target.*;
     const mcv = switch (result) {
         // Copy registers to the stack
         .register => |reg| blk: {
+            const mod = self.bin_file.options.module.?;
             const abi_size = math.cast(u32, ty.abiSize(self.target.*)) catch {
-                return self.fail("type '{}' too big to fit into stack frame", .{ty.fmt(target)});
+                return self.fail("type '{}' too big to fit into stack frame", .{ty.fmt(mod)});
             };
             const abi_align = ty.abiAlignment(self.target.*);
             const stack_offset = try self.allocMem(inst, abi_size, abi_align);
@@ -2537,17 +2539,19 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
 
     // Due to incremental compilation, how function calls are generated depends
     // on linking.
+    const mod = self.bin_file.options.module.?;
     if (self.air.value(callee)) |func_value| {
         if (self.bin_file.tag == link.File.Elf.base_tag or self.bin_file.tag == link.File.Coff.base_tag) {
             if (func_value.castTag(.function)) |func_payload| {
                 const func = func_payload.data;
                 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
                 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
+                const fn_owner_decl = mod.declPtr(func.owner_decl);
                 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
                     const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
-                    break :blk @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
+                    break :blk @intCast(u32, got.p_vaddr + fn_owner_decl.link.elf.offset_table_index * ptr_bytes);
                 } else if (self.bin_file.cast(link.File.Coff)) |coff_file|
-                    coff_file.offset_table_virtual_address + func.owner_decl.link.coff.offset_table_index * ptr_bytes
+                    coff_file.offset_table_virtual_address + fn_owner_decl.link.coff.offset_table_index * ptr_bytes
                 else
                     unreachable;
 
@@ -2565,8 +2569,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
         } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
             if (func_value.castTag(.function)) |func_payload| {
                 const func = func_payload.data;
+                const fn_owner_decl = mod.declPtr(func.owner_decl);
                 try self.genSetReg(Type.initTag(.u64), .x30, .{
-                    .got_load = func.owner_decl.link.macho.local_sym_index,
+                    .got_load = fn_owner_decl.link.macho.local_sym_index,
                 });
                 // blr x30
                 _ = try self.addInst(.{
@@ -2575,7 +2580,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
                 });
             } else if (func_value.castTag(.extern_fn)) |func_payload| {
                 const extern_fn = func_payload.data;
-                const decl_name = extern_fn.owner_decl.name;
+                const decl_name = mod.declPtr(extern_fn.owner_decl).name;
                 if (extern_fn.lib_name) |lib_name| {
                     log.debug("TODO enforce that '{s}' is expected in '{s}' library", .{
                         decl_name,
@@ -2588,7 +2593,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
                     .tag = .call_extern,
                     .data = .{
                         .extern_fn = .{
-                            .atom_index = self.mod_fn.owner_decl.link.macho.local_sym_index,
+                            .atom_index = mod.declPtr(self.mod_fn.owner_decl).link.macho.local_sym_index,
                             .sym_name = n_strx,
                         },
                     },
@@ -2602,7 +2607,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
                 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
                 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
                 const got_addr = p9.bases.data;
-                const got_index = func_payload.data.owner_decl.link.plan9.got_index.?;
+                const got_index = mod.declPtr(func_payload.data.owner_decl).link.plan9.got_index.?;
                 const fn_got_addr = got_addr + got_index * ptr_bytes;
 
                 try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = fn_got_addr });
@@ -3478,12 +3483,13 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
                             .direct_load => .load_memory_ptr_direct,
                             else => unreachable,
                         };
+                        const mod = self.bin_file.options.module.?;
                         _ = try self.addInst(.{
                             .tag = tag,
                             .data = .{
                                 .payload = try self.addExtra(Mir.LoadMemoryPie{
                                     .register = @enumToInt(src_reg),
-                                    .atom_index = self.mod_fn.owner_decl.link.macho.local_sym_index,
+                                    .atom_index = mod.declPtr(self.mod_fn.owner_decl).link.macho.local_sym_index,
                                     .sym_index = sym_index,
                                 }),
                             },
@@ -3597,12 +3603,13 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
                 .direct_load => .load_memory_direct,
                 else => unreachable,
             };
+            const mod = self.bin_file.options.module.?;
             _ = try self.addInst(.{
                 .tag = tag,
                 .data = .{
                     .payload = try self.addExtra(Mir.LoadMemoryPie{
                         .register = @enumToInt(reg),
-                        .atom_index = self.mod_fn.owner_decl.link.macho.local_sym_index,
+                        .atom_index = mod.declPtr(self.mod_fn.owner_decl).link.macho.local_sym_index,
                         .sym_index = sym_index,
                     }),
                 },
@@ -3860,7 +3867,7 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {
     }
 }
 
-fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCValue {
+fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) InnerError!MCValue {
     const ptr_bits = self.target.cpu.arch.ptrBitWidth();
     const ptr_bytes: u64 = @divExact(ptr_bits, 8);
 
@@ -3872,7 +3879,10 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCVa
         }
     }
 
-    decl.alive = true;
+    const mod = self.bin_file.options.module.?;
+    const decl = mod.declPtr(decl_index);
+    mod.markDeclAlive(decl);
+
     if (self.bin_file.cast(link.File.Elf)) |elf_file| {
         const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
         const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;
@@ -3886,7 +3896,7 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCVa
         const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;
         return MCValue{ .memory = got_addr };
     } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
-        try p9.seeDecl(decl);
+        try p9.seeDecl(decl_index);
         const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;
         return MCValue{ .memory = got_addr };
     } else {
@@ -3922,7 +3932,7 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
         return self.lowerDeclRef(typed_value, payload.data);
     }
     if (typed_value.val.castTag(.decl_ref_mut)) |payload| {
-        return self.lowerDeclRef(typed_value, payload.data.decl);
+        return self.lowerDeclRef(typed_value, payload.data.decl_index);
     }
     const target = self.target.*;
 
src/arch/arm/CodeGen.zig
@@ -271,8 +271,10 @@ pub fn generate(
         @panic("Attempted to compile for architecture that was disabled by build configuration");
     }
 
-    assert(module_fn.owner_decl.has_tv);
-    const fn_type = module_fn.owner_decl.ty;
+    const mod = bin_file.options.module.?;
+    const fn_owner_decl = mod.declPtr(module_fn.owner_decl);
+    assert(fn_owner_decl.has_tv);
+    const fn_type = fn_owner_decl.ty;
 
     var branch_stack = std.ArrayList(Branch).init(bin_file.allocator);
     defer {
@@ -838,9 +840,9 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {
         return @as(u32, 0);
     }
 
-    const target = self.target.*;
     const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
-        return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(target)});
+        const mod = self.bin_file.options.module.?;
+        return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)});
     };
     // TODO swap this for inst.ty.ptrAlign
     const abi_align = elem_ty.abiAlignment(self.target.*);
@@ -849,9 +851,9 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {
 
 fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
     const elem_ty = self.air.typeOfIndex(inst);
-    const target = self.target.*;
     const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
-        return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(target)});
+        const mod = self.bin_file.options.module.?;
+        return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)});
     };
     const abi_align = elem_ty.abiAlignment(self.target.*);
     if (abi_align > self.stack_align)
@@ -1204,7 +1206,8 @@ fn minMax(
         .Float => return self.fail("TODO ARM min/max on floats", .{}),
         .Vector => return self.fail("TODO ARM min/max on vectors", .{}),
         .Int => {
-            assert(lhs_ty.eql(rhs_ty, self.target.*));
+            const mod = self.bin_file.options.module.?;
+            assert(lhs_ty.eql(rhs_ty, mod));
             const int_info = lhs_ty.intInfo(self.target.*);
             if (int_info.bits <= 32) {
                 const lhs_is_register = lhs == .register;
@@ -1372,7 +1375,8 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
         switch (lhs_ty.zigTypeTag()) {
             .Vector => return self.fail("TODO implement add_with_overflow/sub_with_overflow for vectors", .{}),
             .Int => {
-                assert(lhs_ty.eql(rhs_ty, self.target.*));
+                const mod = self.bin_file.options.module.?;
+                assert(lhs_ty.eql(rhs_ty, mod));
                 const int_info = lhs_ty.intInfo(self.target.*);
                 if (int_info.bits < 32) {
                     const stack_offset = try self.allocMem(inst, tuple_size, tuple_align);
@@ -1472,7 +1476,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
         switch (lhs_ty.zigTypeTag()) {
             .Vector => return self.fail("TODO implement mul_with_overflow for vectors", .{}),
             .Int => {
-                assert(lhs_ty.eql(rhs_ty, self.target.*));
+                const mod = self.bin_file.options.module.?;
+                assert(lhs_ty.eql(rhs_ty, mod));
                 const int_info = lhs_ty.intInfo(self.target.*);
                 if (int_info.bits <= 16) {
                     const stack_offset = try self.allocMem(inst, tuple_size, tuple_align);
@@ -2682,7 +2687,6 @@ fn binOp(
     lhs_ty: Type,
     rhs_ty: Type,
 ) InnerError!MCValue {
-    const target = self.target.*;
     switch (tag) {
         .add,
         .sub,
@@ -2692,7 +2696,8 @@ fn binOp(
                 .Float => return self.fail("TODO ARM binary operations on floats", .{}),
                 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
                 .Int => {
-                    assert(lhs_ty.eql(rhs_ty, target));
+                    const mod = self.bin_file.options.module.?;
+                    assert(lhs_ty.eql(rhs_ty, mod));
                     const int_info = lhs_ty.intInfo(self.target.*);
                     if (int_info.bits <= 32) {
                         // Only say yes if the operation is
@@ -2740,7 +2745,8 @@ fn binOp(
                 .Float => return self.fail("TODO ARM binary operations on floats", .{}),
                 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
                 .Int => {
-                    assert(lhs_ty.eql(rhs_ty, target));
+                    const mod = self.bin_file.options.module.?;
+                    assert(lhs_ty.eql(rhs_ty, mod));
                     const int_info = lhs_ty.intInfo(self.target.*);
                     if (int_info.bits <= 32) {
                         // TODO add optimisations for multiplication
@@ -2794,7 +2800,8 @@ fn binOp(
             switch (lhs_ty.zigTypeTag()) {
                 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
                 .Int => {
-                    assert(lhs_ty.eql(rhs_ty, target));
+                    const mod = self.bin_file.options.module.?;
+                    assert(lhs_ty.eql(rhs_ty, mod));
                     const int_info = lhs_ty.intInfo(self.target.*);
                     if (int_info.bits <= 32) {
                         const lhs_immediate_ok = lhs == .immediate and Instruction.Operand.fromU32(lhs.immediate) != null;
@@ -3100,8 +3107,9 @@ fn addDbgInfoTypeReloc(self: *Self, ty: Type) error{OutOfMemory}!void {
             const dbg_info = &dw.dbg_info;
             const index = dbg_info.items.len;
             try dbg_info.resize(index + 4); // DW.AT.type,  DW.FORM.ref4
+            const mod = self.bin_file.options.module.?;
             const atom = switch (self.bin_file.tag) {
-                .elf => &self.mod_fn.owner_decl.link.elf.dbg_info_atom,
+                .elf => &mod.declPtr(self.mod_fn.owner_decl).link.elf.dbg_info_atom,
                 .macho => unreachable,
                 else => unreachable,
             };
@@ -3318,11 +3326,13 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
                     const func = func_payload.data;
                     const ptr_bits = self.target.cpu.arch.ptrBitWidth();
                     const ptr_bytes: u64 = @divExact(ptr_bits, 8);
+                    const mod = self.bin_file.options.module.?;
+                    const fn_owner_decl = mod.declPtr(func.owner_decl);
                     const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
                         const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
-                        break :blk @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
+                        break :blk @intCast(u32, got.p_vaddr + fn_owner_decl.link.elf.offset_table_index * ptr_bytes);
                     } else if (self.bin_file.cast(link.File.Coff)) |coff_file|
-                        coff_file.offset_table_virtual_address + func.owner_decl.link.coff.offset_table_index * ptr_bytes
+                        coff_file.offset_table_virtual_address + fn_owner_decl.link.coff.offset_table_index * ptr_bytes
                     else
                         unreachable;
 
@@ -4924,11 +4934,14 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {
     }
 }
 
-fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCValue {
+fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) InnerError!MCValue {
     const ptr_bits = self.target.cpu.arch.ptrBitWidth();
     const ptr_bytes: u64 = @divExact(ptr_bits, 8);
 
-    decl.alive = true;
+    const mod = self.bin_file.options.module.?;
+    const decl = mod.declPtr(decl_index);
+    mod.markDeclAlive(decl);
+
     if (self.bin_file.cast(link.File.Elf)) |elf_file| {
         const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
         const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;
@@ -4939,7 +4952,7 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCVa
         const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;
         return MCValue{ .memory = got_addr };
     } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
-        try p9.seeDecl(decl);
+        try p9.seeDecl(decl_index);
         const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;
         return MCValue{ .memory = got_addr };
     } else {
@@ -4976,7 +4989,7 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
         return self.lowerDeclRef(typed_value, payload.data);
     }
     if (typed_value.val.castTag(.decl_ref_mut)) |payload| {
-        return self.lowerDeclRef(typed_value, payload.data.decl);
+        return self.lowerDeclRef(typed_value, payload.data.decl_index);
     }
     const target = self.target.*;
 
src/arch/riscv64/CodeGen.zig
@@ -229,8 +229,10 @@ pub fn generate(
         @panic("Attempted to compile for architecture that was disabled by build configuration");
     }
 
-    assert(module_fn.owner_decl.has_tv);
-    const fn_type = module_fn.owner_decl.ty;
+    const mod = bin_file.options.module.?;
+    const fn_owner_decl = mod.declPtr(module_fn.owner_decl);
+    assert(fn_owner_decl.has_tv);
+    const fn_type = fn_owner_decl.ty;
 
     var branch_stack = std.ArrayList(Branch).init(bin_file.allocator);
     defer {
@@ -738,8 +740,9 @@ fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void {
             const dbg_info = &dw.dbg_info;
             const index = dbg_info.items.len;
             try dbg_info.resize(index + 4); // DW.AT.type,  DW.FORM.ref4
+            const mod = self.bin_file.options.module.?;
             const atom = switch (self.bin_file.tag) {
-                .elf => &self.mod_fn.owner_decl.link.elf.dbg_info_atom,
+                .elf => &mod.declPtr(self.mod_fn.owner_decl).link.elf.dbg_info_atom,
                 .macho => unreachable,
                 else => unreachable,
             };
@@ -768,9 +771,9 @@ fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u
 /// Use a pointer instruction as the basis for allocating stack memory.
 fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {
     const elem_ty = self.air.typeOfIndex(inst).elemType();
-    const target = self.target.*;
     const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
-        return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(target)});
+        const mod = self.bin_file.options.module.?;
+        return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)});
     };
     // TODO swap this for inst.ty.ptrAlign
     const abi_align = elem_ty.abiAlignment(self.target.*);
@@ -779,9 +782,9 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {
 
 fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
     const elem_ty = self.air.typeOfIndex(inst);
-    const target = self.target.*;
     const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
-        return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(target)});
+        const mod = self.bin_file.options.module.?;
+        return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)});
     };
     const abi_align = elem_ty.abiAlignment(self.target.*);
     if (abi_align > self.stack_align)
@@ -1037,7 +1040,8 @@ fn binOp(
                 .Float => return self.fail("TODO binary operations on floats", .{}),
                 .Vector => return self.fail("TODO binary operations on vectors", .{}),
                 .Int => {
-                    assert(lhs_ty.eql(rhs_ty, self.target.*));
+                    const mod = self.bin_file.options.module.?;
+                    assert(lhs_ty.eql(rhs_ty, mod));
                     const int_info = lhs_ty.intInfo(self.target.*);
                     if (int_info.bits <= 64) {
                         // TODO immediate operands
@@ -1679,11 +1683,13 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
 
                 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
                 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
+                const mod = self.bin_file.options.module.?;
+                const fn_owner_decl = mod.declPtr(func.owner_decl);
                 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
                     const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
-                    break :blk @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
+                    break :blk @intCast(u32, got.p_vaddr + fn_owner_decl.link.elf.offset_table_index * ptr_bytes);
                 } else if (self.bin_file.cast(link.File.Coff)) |coff_file|
-                    coff_file.offset_table_virtual_address + func.owner_decl.link.coff.offset_table_index * ptr_bytes
+                    coff_file.offset_table_virtual_address + fn_owner_decl.link.coff.offset_table_index * ptr_bytes
                 else
                     unreachable;
 
@@ -1768,7 +1774,8 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
     if (self.liveness.isUnused(inst))
         return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
     const ty = self.air.typeOf(bin_op.lhs);
-    assert(ty.eql(self.air.typeOf(bin_op.rhs), self.target.*));
+    const mod = self.bin_file.options.module.?;
+    assert(ty.eql(self.air.typeOf(bin_op.rhs), mod));
     if (ty.zigTypeTag() == .ErrorSet)
         return self.fail("TODO implement cmp for errors", .{});
 
@@ -2501,10 +2508,12 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {
     }
 }
 
-fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCValue {
+fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) InnerError!MCValue {
     const ptr_bits = self.target.cpu.arch.ptrBitWidth();
     const ptr_bytes: u64 = @divExact(ptr_bits, 8);
-    decl.alive = true;
+    const mod = self.bin_file.options.module.?;
+    const decl = mod.declPtr(decl_index);
+    mod.markDeclAlive(decl);
     if (self.bin_file.cast(link.File.Elf)) |elf_file| {
         const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
         const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;
@@ -2517,7 +2526,7 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCVa
         const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;
         return MCValue{ .memory = got_addr };
     } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
-        try p9.seeDecl(decl);
+        try p9.seeDecl(decl_index);
         const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;
         return MCValue{ .memory = got_addr };
     } else {
@@ -2534,7 +2543,7 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
         return self.lowerDeclRef(typed_value, payload.data);
     }
     if (typed_value.val.castTag(.decl_ref_mut)) |payload| {
-        return self.lowerDeclRef(typed_value, payload.data.decl);
+        return self.lowerDeclRef(typed_value, payload.data.decl_index);
     }
     const target = self.target.*;
     const ptr_bits = self.target.cpu.arch.ptrBitWidth();
@@ -2544,7 +2553,8 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
                 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
                 const ptr_type = typed_value.ty.slicePtrFieldType(&buf);
                 const ptr_mcv = try self.genTypedValue(.{ .ty = ptr_type, .val = typed_value.val });
-                const slice_len = typed_value.val.sliceLen(target);
+                const mod = self.bin_file.options.module.?;
+                const slice_len = typed_value.val.sliceLen(mod);
                 // Codegen can't handle some kinds of indirection. If the wrong union field is accessed here it may mean
                 // the Sema code needs to use anonymous Decls or alloca instructions to store data.
                 const ptr_imm = ptr_mcv.memory;
src/arch/sparcv9/CodeGen.zig
@@ -243,8 +243,10 @@ pub fn generate(
         @panic("Attempted to compile for architecture that was disabled by build configuration");
     }
 
-    assert(module_fn.owner_decl.has_tv);
-    const fn_type = module_fn.owner_decl.ty;
+    const mod = bin_file.options.module.?;
+    const fn_owner_decl = mod.declPtr(module_fn.owner_decl);
+    assert(fn_owner_decl.has_tv);
+    const fn_type = fn_owner_decl.ty;
 
     var branch_stack = std.ArrayList(Branch).init(bin_file.allocator);
     defer {
@@ -871,7 +873,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
                 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
                 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
                     const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
-                    break :blk @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
+                    const mod = self.bin_file.options.module.?;
+                    break :blk @intCast(u32, got.p_vaddr + mod.declPtr(func.owner_decl).link.elf.offset_table_index * ptr_bytes);
                 } else unreachable;
 
                 try self.genSetReg(Type.initTag(.usize), .o7, .{ .memory = got_addr });
@@ -1026,9 +1029,9 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {
         return @as(u32, 0);
     }
 
-    const target = self.target.*;
     const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
-        return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(target)});
+        const mod = self.bin_file.options.module.?;
+        return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)});
     };
     // TODO swap this for inst.ty.ptrAlign
     const abi_align = elem_ty.abiAlignment(self.target.*);
@@ -1037,9 +1040,9 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {
 
 fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
     const elem_ty = self.air.typeOfIndex(inst);
-    const target = self.target.*;
     const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
-        return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(target)});
+        const mod = self.bin_file.options.module.?;
+        return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)});
     };
     const abi_align = elem_ty.abiAlignment(self.target.*);
     if (abi_align > self.stack_align)
@@ -1372,7 +1375,7 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
         return self.lowerDeclRef(typed_value, payload.data);
     }
     if (typed_value.val.castTag(.decl_ref_mut)) |payload| {
-        return self.lowerDeclRef(typed_value, payload.data.decl);
+        return self.lowerDeclRef(typed_value, payload.data.decl_index);
     }
     const target = self.target.*;
 
@@ -1422,7 +1425,7 @@ fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigT
     };
 }
 
-fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCValue {
+fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) InnerError!MCValue {
     const ptr_bits = self.target.cpu.arch.ptrBitWidth();
     const ptr_bytes: u64 = @divExact(ptr_bits, 8);
 
@@ -1434,7 +1437,10 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCVa
         }
     }
 
-    decl.alive = true;
+    const mod = self.bin_file.options.module.?;
+    const decl = mod.declPtr(decl_index);
+
+    mod.markDeclAlive(decl);
     if (self.bin_file.cast(link.File.Elf)) |elf_file| {
         const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
         const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;
src/arch/wasm/CodeGen.zig
@@ -538,6 +538,10 @@ const Self = @This();
 /// Reference to the function declaration the code
 /// section belongs to
 decl: *Decl,
+decl_index: Decl.Index,
+/// Current block depth. Used to calculate the relative difference between a break
+/// and block
+block_depth: u32 = 0,
 air: Air,
 liveness: Liveness,
 gpa: mem.Allocator,
@@ -559,9 +563,6 @@ local_index: u32 = 0,
 arg_index: u32 = 0,
 /// If codegen fails, an error messages will be allocated and saved in `err_msg`
 err_msg: *Module.ErrorMsg,
-/// Current block depth. Used to calculate the relative difference between a break
-/// and block
-block_depth: u32 = 0,
 /// List of all locals' types generated throughout this declaration
 /// used to emit locals count at start of 'code' section.
 locals: std.ArrayListUnmanaged(u8),
@@ -644,7 +645,7 @@ fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!WValue {
     // In the other cases, we will simply lower the constant to a value that fits
     // into a single local (such as a pointer, integer, bool, etc).
     const result = if (isByRef(ty, self.target)) blk: {
-        const sym_index = try self.bin_file.lowerUnnamedConst(self.decl, .{ .ty = ty, .val = val });
+        const sym_index = try self.bin_file.lowerUnnamedConst(.{ .ty = ty, .val = val }, self.decl_index);
         break :blk WValue{ .memory = sym_index };
     } else try self.lowerConstant(val, ty);
 
@@ -838,7 +839,8 @@ pub fn generate(
         .liveness = liveness,
         .values = .{},
         .code = code,
-        .decl = func.owner_decl,
+        .decl_index = func.owner_decl,
+        .decl = bin_file.options.module.?.declPtr(func.owner_decl),
         .err_msg = undefined,
         .locals = .{},
         .target = bin_file.options.target,
@@ -1022,8 +1024,9 @@ fn allocStack(self: *Self, ty: Type) !WValue {
     }
 
     const abi_size = std.math.cast(u32, ty.abiSize(self.target)) catch {
+        const module = self.bin_file.base.options.module.?;
         return self.fail("Type {} with ABI size of {d} exceeds stack frame size", .{
-            ty.fmt(self.target), ty.abiSize(self.target),
+            ty.fmt(module), ty.abiSize(self.target),
         });
     };
     const abi_align = ty.abiAlignment(self.target);
@@ -1056,8 +1059,9 @@ fn allocStackPtr(self: *Self, inst: Air.Inst.Index) !WValue {
 
     const abi_alignment = ptr_ty.ptrAlignment(self.target);
     const abi_size = std.math.cast(u32, pointee_ty.abiSize(self.target)) catch {
+        const module = self.bin_file.base.options.module.?;
         return self.fail("Type {} with ABI size of {d} exceeds stack frame size", .{
-            pointee_ty.fmt(self.target), pointee_ty.abiSize(self.target),
+            pointee_ty.fmt(module), pointee_ty.abiSize(self.target),
         });
     };
     if (abi_alignment > self.stack_alignment) {
@@ -1542,20 +1546,21 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
     const ret_ty = fn_ty.fnReturnType();
     const first_param_sret = isByRef(ret_ty, self.target);
 
-    const target: ?*Decl = blk: {
+    const callee: ?*Decl = blk: {
         const func_val = self.air.value(pl_op.operand) orelse break :blk null;
+        const module = self.bin_file.base.options.module.?;
 
         if (func_val.castTag(.function)) |func| {
-            break :blk func.data.owner_decl;
+            break :blk module.declPtr(func.data.owner_decl);
         } else if (func_val.castTag(.extern_fn)) |extern_fn| {
-            const ext_decl = extern_fn.data.owner_decl;
+            const ext_decl = module.declPtr(extern_fn.data.owner_decl);
             var func_type = try genFunctype(self.gpa, ext_decl.ty, self.target);
             defer func_type.deinit(self.gpa);
             ext_decl.fn_link.wasm.type_index = try self.bin_file.putOrGetFuncType(func_type);
             try self.bin_file.addOrUpdateImport(ext_decl);
             break :blk ext_decl;
         } else if (func_val.castTag(.decl_ref)) |decl_ref| {
-            break :blk decl_ref.data;
+            break :blk module.declPtr(decl_ref.data);
         }
         return self.fail("Expected a function, but instead found type '{s}'", .{func_val.tag()});
     };
@@ -1580,7 +1585,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
         }
     }
 
-    if (target) |direct| {
+    if (callee) |direct| {
         try self.addLabel(.call, direct.link.wasm.sym_index);
     } else {
         // in this case we call a function pointer
@@ -1837,16 +1842,16 @@ fn wrapBinOp(self: *Self, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError
 fn lowerParentPtr(self: *Self, ptr_val: Value, ptr_child_ty: Type) InnerError!WValue {
     switch (ptr_val.tag()) {
         .decl_ref_mut => {
-            const decl = ptr_val.castTag(.decl_ref_mut).?.data.decl;
-            return self.lowerParentPtrDecl(ptr_val, decl);
+            const decl_index = ptr_val.castTag(.decl_ref_mut).?.data.decl_index;
+            return self.lowerParentPtrDecl(ptr_val, decl_index);
         },
         .decl_ref => {
-            const decl = ptr_val.castTag(.decl_ref).?.data;
-            return self.lowerParentPtrDecl(ptr_val, decl);
+            const decl_index = ptr_val.castTag(.decl_ref).?.data;
+            return self.lowerParentPtrDecl(ptr_val, decl_index);
         },
         .variable => {
-            const decl = ptr_val.castTag(.variable).?.data.owner_decl;
-            return self.lowerParentPtrDecl(ptr_val, decl);
+            const decl_index = ptr_val.castTag(.variable).?.data.owner_decl;
+            return self.lowerParentPtrDecl(ptr_val, decl_index);
         },
         .field_ptr => {
             const field_ptr = ptr_val.castTag(.field_ptr).?.data;
@@ -1918,24 +1923,31 @@ fn lowerParentPtr(self: *Self, ptr_val: Value, ptr_child_ty: Type) InnerError!WV
     }
 }
 
-fn lowerParentPtrDecl(self: *Self, ptr_val: Value, decl: *Module.Decl) InnerError!WValue {
-    decl.markAlive();
+fn lowerParentPtrDecl(self: *Self, ptr_val: Value, decl_index: Module.Decl.Index) InnerError!WValue {
+    const module = self.bin_file.base.options.module.?;
+    const decl = module.declPtr(decl_index);
+    module.markDeclAlive(decl);
     var ptr_ty_payload: Type.Payload.ElemType = .{
         .base = .{ .tag = .single_mut_pointer },
         .data = decl.ty,
     };
     const ptr_ty = Type.initPayload(&ptr_ty_payload.base);
-    return self.lowerDeclRefValue(.{ .ty = ptr_ty, .val = ptr_val }, decl);
+    return self.lowerDeclRefValue(.{ .ty = ptr_ty, .val = ptr_val }, decl_index);
 }
 
-fn lowerDeclRefValue(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!WValue {
+fn lowerDeclRefValue(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) InnerError!WValue {
     if (tv.ty.isSlice()) {
-        return WValue{ .memory = try self.bin_file.lowerUnnamedConst(decl, tv) };
-    } else if (decl.ty.zigTypeTag() != .Fn and !decl.ty.hasRuntimeBitsIgnoreComptime()) {
+        return WValue{ .memory = try self.bin_file.lowerUnnamedConst(tv, decl_index) };
+    }
+
+    const module = self.bin_file.base.options.module.?;
+    const decl = module.declPtr(decl_index);
+    if (decl.ty.zigTypeTag() != .Fn and !decl.ty.hasRuntimeBitsIgnoreComptime()) {
         return WValue{ .imm32 = 0xaaaaaaaa };
     }
 
-    decl.markAlive();
+    module.markDeclAlive(decl);
+
     const target_sym_index = decl.link.wasm.sym_index;
     if (decl.ty.zigTypeTag() == .Fn) {
         try self.bin_file.addTableFunction(target_sym_index);
@@ -1946,12 +1958,12 @@ fn lowerDeclRefValue(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError
 fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
     if (val.isUndefDeep()) return self.emitUndefined(ty);
     if (val.castTag(.decl_ref)) |decl_ref| {
-        const decl = decl_ref.data;
-        return self.lowerDeclRefValue(.{ .ty = ty, .val = val }, decl);
+        const decl_index = decl_ref.data;
+        return self.lowerDeclRefValue(.{ .ty = ty, .val = val }, decl_index);
     }
-    if (val.castTag(.decl_ref_mut)) |decl_ref| {
-        const decl = decl_ref.data.decl;
-        return self.lowerDeclRefValue(.{ .ty = ty, .val = val }, decl);
+    if (val.castTag(.decl_ref_mut)) |decl_ref_mut| {
+        const decl_index = decl_ref_mut.data.decl_index;
+        return self.lowerDeclRefValue(.{ .ty = ty, .val = val }, decl_index);
     }
 
     const target = self.target;
@@ -2347,8 +2359,9 @@ fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
     const struct_ptr = try self.resolveInst(extra.data.struct_operand);
     const struct_ty = self.air.typeOf(extra.data.struct_operand).childType();
     const offset = std.math.cast(u32, struct_ty.structFieldOffset(extra.data.field_index, self.target)) catch {
+        const module = self.bin_file.base.options.module.?;
         return self.fail("Field type '{}' too big to fit into stack frame", .{
-            struct_ty.structFieldType(extra.data.field_index).fmt(self.target),
+            struct_ty.structFieldType(extra.data.field_index).fmt(module),
         });
     };
     return self.structFieldPtr(struct_ptr, offset);
@@ -2360,8 +2373,9 @@ fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerEr
     const struct_ty = self.air.typeOf(ty_op.operand).childType();
     const field_ty = struct_ty.structFieldType(index);
     const offset = std.math.cast(u32, struct_ty.structFieldOffset(index, self.target)) catch {
+        const module = self.bin_file.base.options.module.?;
         return self.fail("Field type '{}' too big to fit into stack frame", .{
-            field_ty.fmt(self.target),
+            field_ty.fmt(module),
         });
     };
     return self.structFieldPtr(struct_ptr, offset);
@@ -2387,7 +2401,8 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
     const field_ty = struct_ty.structFieldType(field_index);
     if (!field_ty.hasRuntimeBitsIgnoreComptime()) return WValue{ .none = {} };
     const offset = std.math.cast(u32, struct_ty.structFieldOffset(field_index, self.target)) catch {
-        return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty.fmt(self.target)});
+        const module = self.bin_file.base.options.module.?;
+        return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty.fmt(module)});
     };
 
     if (isByRef(field_ty, self.target)) {
@@ -2782,7 +2797,8 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue
     }
 
     const offset = std.math.cast(u32, opt_ty.abiSize(self.target) - payload_ty.abiSize(self.target)) catch {
-        return self.fail("Optional type {} too big to fit into stack frame", .{opt_ty.fmt(self.target)});
+        const module = self.bin_file.base.options.module.?;
+        return self.fail("Optional type {} too big to fit into stack frame", .{opt_ty.fmt(module)});
     };
 
     try self.emitWValue(operand);
@@ -2811,7 +2827,8 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
         return operand;
     }
     const offset = std.math.cast(u32, op_ty.abiSize(self.target) - payload_ty.abiSize(self.target)) catch {
-        return self.fail("Optional type {} too big to fit into stack frame", .{op_ty.fmt(self.target)});
+        const module = self.bin_file.base.options.module.?;
+        return self.fail("Optional type {} too big to fit into stack frame", .{op_ty.fmt(module)});
     };
 
     // Create optional type, set the non-null bit, and store the operand inside the optional type
src/arch/x86_64/CodeGen.zig
@@ -309,8 +309,10 @@ pub fn generate(
         @panic("Attempted to compile for architecture that was disabled by build configuration");
     }
 
-    assert(module_fn.owner_decl.has_tv);
-    const fn_type = module_fn.owner_decl.ty;
+    const mod = bin_file.options.module.?;
+    const fn_owner_decl = mod.declPtr(module_fn.owner_decl);
+    assert(fn_owner_decl.has_tv);
+    const fn_type = fn_owner_decl.ty;
 
     var branch_stack = std.ArrayList(Branch).init(bin_file.allocator);
     defer {
@@ -396,14 +398,14 @@ pub fn generate(
 
     if (builtin.mode == .Debug and bin_file.options.module.?.comp.verbose_mir) {
         const w = std.io.getStdErr().writer();
-        w.print("# Begin Function MIR: {s}:\n", .{module_fn.owner_decl.name}) catch {};
+        w.print("# Begin Function MIR: {s}:\n", .{fn_owner_decl.name}) catch {};
         const PrintMir = @import("PrintMir.zig");
         const print = PrintMir{
             .mir = mir,
             .bin_file = bin_file,
         };
         print.printMir(w, function.mir_to_air_map, air) catch {}; // we don't care if the debug printing fails
-        w.print("# End Function MIR: {s}\n\n", .{module_fn.owner_decl.name}) catch {};
+        w.print("# End Function MIR: {s}\n\n", .{fn_owner_decl.name}) catch {};
     }
 
     if (function.err_msg) |em| {
@@ -915,9 +917,9 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {
         return self.allocMem(inst, @sizeOf(usize), @alignOf(usize));
     }
 
-    const target = self.target.*;
     const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
-        return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(target)});
+        const mod = self.bin_file.options.module.?;
+        return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)});
     };
     // TODO swap this for inst.ty.ptrAlign
     const abi_align = ptr_ty.ptrAlignment(self.target.*);
@@ -926,9 +928,9 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {
 
 fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
     const elem_ty = self.air.typeOfIndex(inst);
-    const target = self.target.*;
     const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
-        return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(target)});
+        const mod = self.bin_file.options.module.?;
+        return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)});
     };
     const abi_align = elem_ty.abiAlignment(self.target.*);
     if (abi_align > self.stack_align)
@@ -2650,6 +2652,8 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue
                 .direct_load => 0b01,
                 else => unreachable,
             };
+            const mod = self.bin_file.options.module.?;
+            const fn_owner_decl = mod.declPtr(self.mod_fn.owner_decl);
             _ = try self.addInst(.{
                 .tag = .lea_pie,
                 .ops = (Mir.Ops{
@@ -2658,7 +2662,7 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue
                 }).encode(),
                 .data = .{
                     .load_reloc = .{
-                        .atom_index = self.mod_fn.owner_decl.link.macho.local_sym_index,
+                        .atom_index = fn_owner_decl.link.macho.local_sym_index,
                         .sym_index = sym_index,
                     },
                 },
@@ -3583,17 +3587,19 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
 
     // Due to incremental compilation, how function calls are generated depends
     // on linking.
+    const mod = self.bin_file.options.module.?;
     if (self.bin_file.tag == link.File.Elf.base_tag or self.bin_file.tag == link.File.Coff.base_tag) {
         if (self.air.value(callee)) |func_value| {
             if (func_value.castTag(.function)) |func_payload| {
                 const func = func_payload.data;
                 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
                 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
+                const fn_owner_decl = mod.declPtr(func.owner_decl);
                 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
                     const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
-                    break :blk @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
+                    break :blk @intCast(u32, got.p_vaddr + fn_owner_decl.link.elf.offset_table_index * ptr_bytes);
                 } else if (self.bin_file.cast(link.File.Coff)) |coff_file|
-                    @intCast(u32, coff_file.offset_table_virtual_address + func.owner_decl.link.coff.offset_table_index * ptr_bytes)
+                    @intCast(u32, coff_file.offset_table_virtual_address + fn_owner_decl.link.coff.offset_table_index * ptr_bytes)
                 else
                     unreachable;
                 _ = try self.addInst(.{
@@ -3625,8 +3631,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
         if (self.air.value(callee)) |func_value| {
             if (func_value.castTag(.function)) |func_payload| {
                 const func = func_payload.data;
+                const fn_owner_decl = mod.declPtr(func.owner_decl);
                 try self.genSetReg(Type.initTag(.usize), .rax, .{
-                    .got_load = func.owner_decl.link.macho.local_sym_index,
+                    .got_load = fn_owner_decl.link.macho.local_sym_index,
                 });
                 // callq *%rax
                 _ = try self.addInst(.{
@@ -3639,7 +3646,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
                 });
             } else if (func_value.castTag(.extern_fn)) |func_payload| {
                 const extern_fn = func_payload.data;
-                const decl_name = extern_fn.owner_decl.name;
+                const decl_name = mod.declPtr(extern_fn.owner_decl).name;
                 if (extern_fn.lib_name) |lib_name| {
                     log.debug("TODO enforce that '{s}' is expected in '{s}' library", .{
                         decl_name,
@@ -3652,7 +3659,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
                     .ops = undefined,
                     .data = .{
                         .extern_fn = .{
-                            .atom_index = self.mod_fn.owner_decl.link.macho.local_sym_index,
+                            .atom_index = mod.declPtr(self.mod_fn.owner_decl).link.macho.local_sym_index,
                             .sym_name = n_strx,
                         },
                     },
@@ -3680,7 +3687,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
                 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
                 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
                 const got_addr = p9.bases.data;
-                const got_index = func_payload.data.owner_decl.link.plan9.got_index.?;
+                const got_index = mod.declPtr(func_payload.data.owner_decl).link.plan9.got_index.?;
                 const fn_got_addr = got_addr + got_index * ptr_bytes;
                 _ = try self.addInst(.{
                     .tag = .call,
@@ -4012,9 +4019,11 @@ fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void {
             const dbg_info = &dw.dbg_info;
             const index = dbg_info.items.len;
             try dbg_info.resize(index + 4); // DW.AT.type,  DW.FORM.ref4
+            const mod = self.bin_file.options.module.?;
+            const fn_owner_decl = mod.declPtr(self.mod_fn.owner_decl);
             const atom = switch (self.bin_file.tag) {
-                .elf => &self.mod_fn.owner_decl.link.elf.dbg_info_atom,
-                .macho => &self.mod_fn.owner_decl.link.macho.dbg_info_atom,
+                .elf => &fn_owner_decl.link.elf.dbg_info_atom,
+                .macho => &fn_owner_decl.link.macho.dbg_info_atom,
                 else => unreachable,
             };
             try dw.addTypeReloc(atom, ty, @intCast(u32, index), null);
@@ -6124,7 +6133,7 @@ fn limitImmediateType(self: *Self, operand: Air.Inst.Ref, comptime T: type) !MCV
     return mcv;
 }
 
-fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCValue {
+fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) InnerError!MCValue {
     log.debug("lowerDeclRef: ty = {}, val = {}", .{ tv.ty.fmtDebug(), tv.val.fmtDebug() });
     const ptr_bits = self.target.cpu.arch.ptrBitWidth();
     const ptr_bytes: u64 = @divExact(ptr_bits, 8);
@@ -6137,7 +6146,9 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCVa
         }
     }
 
-    decl.markAlive();
+    const module = self.bin_file.options.module.?;
+    const decl = module.declPtr(decl_index);
+    module.markDeclAlive(decl);
 
     if (self.bin_file.cast(link.File.Elf)) |elf_file| {
         const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
@@ -6152,7 +6163,7 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCVa
         const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;
         return MCValue{ .memory = got_addr };
     } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
-        try p9.seeDecl(decl);
+        try p9.seeDecl(decl_index);
         const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;
         return MCValue{ .memory = got_addr };
     } else {
@@ -6189,7 +6200,7 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
         return self.lowerDeclRef(typed_value, payload.data);
     }
     if (typed_value.val.castTag(.decl_ref_mut)) |payload| {
-        return self.lowerDeclRef(typed_value, payload.data.decl);
+        return self.lowerDeclRef(typed_value, payload.data.decl_index);
     }
 
     const target = self.target.*;
src/codegen/c.zig
@@ -32,8 +32,8 @@ pub const CValue = union(enum) {
     /// Index into the parameters
     arg: usize,
     /// By-value
-    decl: *Decl,
-    decl_ref: *Decl,
+    decl: Decl.Index,
+    decl_ref: Decl.Index,
     /// An undefined (void *) pointer (cannot be dereferenced)
     undefined_ptr: void,
     /// Render the slice as an identifier (using fmtIdent)
@@ -58,7 +58,7 @@ pub const TypedefMap = std.ArrayHashMap(
 
 const FormatTypeAsCIdentContext = struct {
     ty: Type,
-    target: std.Target,
+    mod: *Module,
 };
 
 /// TODO make this not cut off at 128 bytes
@@ -71,14 +71,14 @@ fn formatTypeAsCIdentifier(
     _ = fmt;
     _ = options;
     var buffer = [1]u8{0} ** 128;
-    var buf = std.fmt.bufPrint(&buffer, "{}", .{data.ty.fmt(data.target)}) catch &buffer;
+    var buf = std.fmt.bufPrint(&buffer, "{}", .{data.ty.fmt(data.mod)}) catch &buffer;
     return formatIdent(buf, "", .{}, writer);
 }
 
-pub fn typeToCIdentifier(ty: Type, target: std.Target) std.fmt.Formatter(formatTypeAsCIdentifier) {
+pub fn typeToCIdentifier(ty: Type, mod: *Module) std.fmt.Formatter(formatTypeAsCIdentifier) {
     return .{ .data = .{
         .ty = ty,
-        .target = target,
+        .mod = mod,
     } };
 }
 
@@ -349,6 +349,7 @@ pub const DeclGen = struct {
     gpa: std.mem.Allocator,
     module: *Module,
     decl: *Decl,
+    decl_index: Decl.Index,
     fwd_decl: std.ArrayList(u8),
     error_msg: ?*Module.ErrorMsg,
     /// The key of this map is Type which has references to typedefs_arena.
@@ -376,10 +377,8 @@ pub const DeclGen = struct {
         writer: anytype,
         ty: Type,
         val: Value,
-        decl: *Decl,
+        decl_index: Decl.Index,
     ) error{ OutOfMemory, AnalysisFail }!void {
-        const target = dg.module.getTarget();
-
         if (ty.isSlice()) {
             try writer.writeByte('(');
             try dg.renderTypecast(writer, ty);
@@ -387,11 +386,12 @@ pub const DeclGen = struct {
             var buf: Type.SlicePtrFieldTypeBuffer = undefined;
             try dg.renderValue(writer, ty.slicePtrFieldType(&buf), val.slicePtr());
             try writer.writeAll(", ");
-            try writer.print("{d}", .{val.sliceLen(target)});
+            try writer.print("{d}", .{val.sliceLen(dg.module)});
             try writer.writeAll("}");
             return;
         }
 
+        const decl = dg.module.declPtr(decl_index);
         assert(decl.has_tv);
         // We shouldn't cast C function pointers as this is UB (when you call
         // them).  The analysis until now should ensure that the C function
@@ -399,21 +399,21 @@ pub const DeclGen = struct {
         // somewhere and we should let the C compiler tell us about it.
         if (ty.castPtrToFn() == null) {
             // Determine if we must pointer cast.
-            if (ty.eql(decl.ty, target)) {
+            if (ty.eql(decl.ty, dg.module)) {
                 try writer.writeByte('&');
-                try dg.renderDeclName(writer, decl);
+                try dg.renderDeclName(writer, decl_index);
                 return;
             }
 
             try writer.writeAll("((");
             try dg.renderTypecast(writer, ty);
             try writer.writeAll(")&");
-            try dg.renderDeclName(writer, decl);
+            try dg.renderDeclName(writer, decl_index);
             try writer.writeByte(')');
             return;
         }
 
-        try dg.renderDeclName(writer, decl);
+        try dg.renderDeclName(writer, decl_index);
     }
 
     fn renderInt128(
@@ -471,13 +471,13 @@ pub const DeclGen = struct {
         try writer.writeByte(')');
         switch (ptr_val.tag()) {
             .decl_ref_mut, .decl_ref, .variable => {
-                const decl = switch (ptr_val.tag()) {
+                const decl_index = switch (ptr_val.tag()) {
                     .decl_ref => ptr_val.castTag(.decl_ref).?.data,
-                    .decl_ref_mut => ptr_val.castTag(.decl_ref_mut).?.data.decl,
+                    .decl_ref_mut => ptr_val.castTag(.decl_ref_mut).?.data.decl_index,
                     .variable => ptr_val.castTag(.variable).?.data.owner_decl,
                     else => unreachable,
                 };
-                try dg.renderDeclValue(writer, ptr_ty, ptr_val, decl);
+                try dg.renderDeclValue(writer, ptr_ty, ptr_val, decl_index);
             },
             .field_ptr => {
                 const field_ptr = ptr_val.castTag(.field_ptr).?.data;
@@ -685,7 +685,7 @@ pub const DeclGen = struct {
                         var index: usize = 0;
                         while (index < ai.len) : (index += 1) {
                             if (index != 0) try writer.writeAll(",");
-                            const elem_val = try val.elemValue(arena_allocator, index);
+                            const elem_val = try val.elemValue(dg.module, arena_allocator, index);
                             try dg.renderValue(writer, ai.elem_type, elem_val);
                         }
                         if (ai.sentinel) |s| {
@@ -837,7 +837,7 @@ pub const DeclGen = struct {
                     try writer.writeAll(".payload = {");
                 }
 
-                const index = union_ty.tag_ty.enumTagFieldIndex(union_obj.tag, target).?;
+                const index = union_ty.tag_ty.enumTagFieldIndex(union_obj.tag, dg.module).?;
                 const field_ty = ty.unionFields().values()[index].ty;
                 const field_name = ty.unionFields().keys()[index];
                 if (field_ty.hasRuntimeBits()) {
@@ -889,7 +889,7 @@ pub const DeclGen = struct {
             try w.writeAll("void");
         }
         try w.writeAll(" ");
-        try dg.renderDeclName(w, dg.decl);
+        try dg.renderDeclName(w, dg.decl_index);
         try w.writeAll("(");
         const param_len = dg.decl.ty.fnParamLen();
 
@@ -927,8 +927,7 @@ pub const DeclGen = struct {
         try bw.writeAll(" (*");
 
         const name_start = buffer.items.len;
-        const target = dg.module.getTarget();
-        try bw.print("zig_F_{s})(", .{typeToCIdentifier(t, target)});
+        try bw.print("zig_F_{s})(", .{typeToCIdentifier(t, dg.module)});
         const name_end = buffer.items.len - 2;
 
         const param_len = fn_info.param_types.len;
@@ -982,11 +981,10 @@ pub const DeclGen = struct {
 
         try bw.writeAll("; size_t len; } ");
         const name_index = buffer.items.len;
-        const target = dg.module.getTarget();
         if (t.isConstPtr()) {
-            try bw.print("zig_L_{s}", .{typeToCIdentifier(child_type, target)});
+            try bw.print("zig_L_{s}", .{typeToCIdentifier(child_type, dg.module)});
         } else {
-            try bw.print("zig_M_{s}", .{typeToCIdentifier(child_type, target)});
+            try bw.print("zig_M_{s}", .{typeToCIdentifier(child_type, dg.module)});
         }
         if (ptr_sentinel) |s| {
             try bw.writeAll("_s_");
@@ -1009,7 +1007,7 @@ pub const DeclGen = struct {
 
     fn renderStructTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 {
         const struct_obj = t.castTag(.@"struct").?.data; // Handle 0 bit types elsewhere.
-        const fqn = try struct_obj.getFullyQualifiedName(dg.typedefs.allocator);
+        const fqn = try struct_obj.getFullyQualifiedName(dg.module);
         defer dg.typedefs.allocator.free(fqn);
 
         var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
@@ -1072,8 +1070,7 @@ pub const DeclGen = struct {
         try buffer.appendSlice("} ");
 
         const name_start = buffer.items.len;
-        const target = dg.module.getTarget();
-        try writer.print("zig_T_{};\n", .{typeToCIdentifier(t, target)});
+        try writer.print("zig_T_{};\n", .{typeToCIdentifier(t, dg.module)});
 
         const rendered = buffer.toOwnedSlice();
         errdefer dg.typedefs.allocator.free(rendered);
@@ -1090,7 +1087,7 @@ pub const DeclGen = struct {
 
     fn renderUnionTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 {
         const union_ty = t.cast(Type.Payload.Union).?.data;
-        const fqn = try union_ty.getFullyQualifiedName(dg.typedefs.allocator);
+        const fqn = try union_ty.getFullyQualifiedName(dg.module);
         defer dg.typedefs.allocator.free(fqn);
 
         const target = dg.module.getTarget();
@@ -1157,7 +1154,6 @@ pub const DeclGen = struct {
         try dg.renderTypeAndName(bw, child_type, payload_name, .Mut, 0);
         try bw.writeAll("; uint16_t error; } ");
         const name_index = buffer.items.len;
-        const target = dg.module.getTarget();
         if (err_set_type.castTag(.error_set_inferred)) |inf_err_set_payload| {
             const func = inf_err_set_payload.data.func;
             try bw.writeAll("zig_E_");
@@ -1165,7 +1161,7 @@ pub const DeclGen = struct {
             try bw.writeAll(";\n");
         } else {
             try bw.print("zig_E_{s}_{s};\n", .{
-                typeToCIdentifier(err_set_type, target), typeToCIdentifier(child_type, target),
+                typeToCIdentifier(err_set_type, dg.module), typeToCIdentifier(child_type, dg.module),
             });
         }
 
@@ -1195,8 +1191,7 @@ pub const DeclGen = struct {
         try dg.renderType(bw, elem_type);
 
         const name_start = buffer.items.len + 1;
-        const target = dg.module.getTarget();
-        try bw.print(" zig_A_{s}_{d}", .{ typeToCIdentifier(elem_type, target), c_len });
+        try bw.print(" zig_A_{s}_{d}", .{ typeToCIdentifier(elem_type, dg.module), c_len });
         const name_end = buffer.items.len;
 
         try bw.print("[{d}];\n", .{c_len});
@@ -1224,8 +1219,7 @@ pub const DeclGen = struct {
         try dg.renderTypeAndName(bw, child_type, payload_name, .Mut, 0);
         try bw.writeAll("; bool is_null; } ");
         const name_index = buffer.items.len;
-        const target = dg.module.getTarget();
-        try bw.print("zig_Q_{s};\n", .{typeToCIdentifier(child_type, target)});
+        try bw.print("zig_Q_{s};\n", .{typeToCIdentifier(child_type, dg.module)});
 
         const rendered = buffer.toOwnedSlice();
         errdefer dg.typedefs.allocator.free(rendered);
@@ -1535,16 +1529,17 @@ pub const DeclGen = struct {
         }
     }
 
-    fn renderDeclName(dg: DeclGen, writer: anytype, decl: *Decl) !void {
-        decl.markAlive();
+    fn renderDeclName(dg: DeclGen, writer: anytype, decl_index: Decl.Index) !void {
+        const decl = dg.module.declPtr(decl_index);
+        dg.module.markDeclAlive(decl);
 
-        if (dg.module.decl_exports.get(decl)) |exports| {
+        if (dg.module.decl_exports.get(decl_index)) |exports| {
             return writer.writeAll(exports[0].options.name);
         } else if (decl.val.tag() == .extern_fn) {
             return writer.writeAll(mem.sliceTo(decl.name, 0));
         } else {
             const gpa = dg.module.gpa;
-            const name = try decl.getFullyQualifiedName(gpa);
+            const name = try decl.getFullyQualifiedName(dg.module);
             defer gpa.free(name);
             return writer.print("{ }", .{fmtIdent(name)});
         }
@@ -1616,7 +1611,11 @@ pub fn genDecl(o: *Object) !void {
             try fwd_decl_writer.writeAll("zig_threadlocal ");
         }
 
-        const decl_c_value: CValue = if (is_global) .{ .bytes = mem.span(o.dg.decl.name) } else .{ .decl = o.dg.decl };
+        const decl_c_value: CValue = if (is_global) .{
+            .bytes = mem.span(o.dg.decl.name),
+        } else .{
+            .decl = o.dg.decl_index,
+        };
 
         try o.dg.renderTypeAndName(fwd_decl_writer, o.dg.decl.ty, decl_c_value, .Mut, o.dg.decl.@"align");
         try fwd_decl_writer.writeAll(";\n");
@@ -1641,7 +1640,7 @@ pub fn genDecl(o: *Object) !void {
         // TODO ask the Decl if it is const
         // https://github.com/ziglang/zig/issues/7582
 
-        const decl_c_value: CValue = .{ .decl = o.dg.decl };
+        const decl_c_value: CValue = .{ .decl = o.dg.decl_index };
         try o.dg.renderTypeAndName(writer, tv.ty, decl_c_value, .Mut, o.dg.decl.@"align");
 
         try writer.writeAll(" = ");
@@ -2234,13 +2233,12 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
     if (src_val_is_undefined)
         return try airStoreUndefined(f, dest_ptr);
 
-    const target = f.object.dg.module.getTarget();
     const writer = f.object.writer();
     if (lhs_child_type.zigTypeTag() == .Array) {
         // For this memcpy to safely work we need the rhs to have the same
         // underlying type as the lhs (i.e. they must both be arrays of the same underlying type).
         const rhs_type = f.air.typeOf(bin_op.rhs);
-        assert(rhs_type.eql(lhs_child_type, target));
+        assert(rhs_type.eql(lhs_child_type, f.object.dg.module));
 
         // If the source is a constant, writeCValue will emit a brace initialization
         // so work around this by initializing into new local.
@@ -2780,7 +2778,8 @@ fn airDbgInline(f: *Function, inst: Air.Inst.Index) !CValue {
     const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
     const writer = f.object.writer();
     const function = f.air.values[ty_pl.payload].castTag(.function).?.data;
-    try writer.print("/* dbg func:{s} */\n", .{function.owner_decl.name});
+    const mod = f.object.dg.module;
+    try writer.print("/* dbg func:{s} */\n", .{mod.declPtr(function.owner_decl).name});
     return CValue.none;
 }
 
src/codegen/llvm.zig
@@ -161,6 +161,7 @@ pub fn targetTriple(allocator: Allocator, target: std.Target) ![:0]u8 {
 
 pub const Object = struct {
     gpa: Allocator,
+    module: *Module,
     llvm_module: *const llvm.Module,
     di_builder: ?*llvm.DIBuilder,
     /// One of these mappings:
@@ -181,7 +182,7 @@ pub const Object = struct {
     ///   version of the name and incorrectly get function not found in the llvm module.
     /// * it works for functions not all globals.
     /// Therefore, this table keeps track of the mapping.
-    decl_map: std.AutoHashMapUnmanaged(*const Module.Decl, *const llvm.Value),
+    decl_map: std.AutoHashMapUnmanaged(Module.Decl.Index, *const llvm.Value),
     /// Maps Zig types to LLVM types. The table memory itself is backed by the GPA of
     /// the compiler, but the Type/Value memory here is backed by `type_map_arena`.
     /// TODO we need to remove entries from this map in response to incremental compilation
@@ -340,6 +341,7 @@ pub const Object = struct {
 
         return Object{
             .gpa = gpa,
+            .module = options.module.?,
             .llvm_module = llvm_module,
             .di_map = .{},
             .di_builder = opt_di_builder,
@@ -568,18 +570,20 @@ pub const Object = struct {
         air: Air,
         liveness: Liveness,
     ) !void {
-        const decl = func.owner_decl;
+        const decl_index = func.owner_decl;
+        const decl = module.declPtr(decl_index);
 
         var dg: DeclGen = .{
             .context = o.context,
             .object = o,
             .module = module,
+            .decl_index = decl_index,
             .decl = decl,
             .err_msg = null,
             .gpa = module.gpa,
         };
 
-        const llvm_func = try dg.resolveLlvmFunction(decl);
+        const llvm_func = try dg.resolveLlvmFunction(decl_index);
 
         if (module.align_stack_fns.get(func)) |align_info| {
             dg.addFnAttrInt(llvm_func, "alignstack", align_info.alignment);
@@ -632,7 +636,7 @@ pub const Object = struct {
 
             const line_number = decl.src_line + 1;
             const is_internal_linkage = decl.val.tag() != .extern_fn and
-                !dg.module.decl_exports.contains(decl);
+                !dg.module.decl_exports.contains(decl_index);
             const noret_bit: c_uint = if (fn_info.return_type.isNoReturn())
                 llvm.DIFlags.NoReturn
             else
@@ -684,48 +688,51 @@ pub const Object = struct {
         fg.genBody(air.getMainBody()) catch |err| switch (err) {
             error.CodegenFail => {
                 decl.analysis = .codegen_failure;
-                try module.failed_decls.put(module.gpa, decl, dg.err_msg.?);
+                try module.failed_decls.put(module.gpa, decl_index, dg.err_msg.?);
                 dg.err_msg = null;
                 return;
             },
             else => |e| return e,
         };
 
-        const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
-        try o.updateDeclExports(module, decl, decl_exports);
+        const decl_exports = module.decl_exports.get(decl_index) orelse &[0]*Module.Export{};
+        try o.updateDeclExports(module, decl_index, decl_exports);
     }
 
-    pub fn updateDecl(self: *Object, module: *Module, decl: *Module.Decl) !void {
+    pub fn updateDecl(self: *Object, module: *Module, decl_index: Module.Decl.Index) !void {
+        const decl = module.declPtr(decl_index);
         var dg: DeclGen = .{
             .context = self.context,
             .object = self,
             .module = module,
             .decl = decl,
+            .decl_index = decl_index,
             .err_msg = null,
             .gpa = module.gpa,
         };
         dg.genDecl() catch |err| switch (err) {
             error.CodegenFail => {
                 decl.analysis = .codegen_failure;
-                try module.failed_decls.put(module.gpa, decl, dg.err_msg.?);
+                try module.failed_decls.put(module.gpa, decl_index, dg.err_msg.?);
                 dg.err_msg = null;
                 return;
             },
             else => |e| return e,
         };
-        const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
-        try self.updateDeclExports(module, decl, decl_exports);
+        const decl_exports = module.decl_exports.get(decl_index) orelse &[0]*Module.Export{};
+        try self.updateDeclExports(module, decl_index, decl_exports);
     }
 
     pub fn updateDeclExports(
         self: *Object,
-        module: *const Module,
-        decl: *const Module.Decl,
+        module: *Module,
+        decl_index: Module.Decl.Index,
         exports: []const *Module.Export,
     ) !void {
         // If the module does not already have the function, we ignore this function call
         // because we call `updateDeclExports` at the end of `updateFunc` and `updateDecl`.
-        const llvm_global = self.decl_map.get(decl) orelse return;
+        const llvm_global = self.decl_map.get(decl_index) orelse return;
+        const decl = module.declPtr(decl_index);
         if (decl.isExtern()) {
             llvm_global.setValueName(decl.name);
             llvm_global.setUnnamedAddr(.False);
@@ -798,7 +805,7 @@ pub const Object = struct {
                 }
             }
         } else {
-            const fqn = try decl.getFullyQualifiedName(module.gpa);
+            const fqn = try decl.getFullyQualifiedName(module);
             defer module.gpa.free(fqn);
             llvm_global.setValueName2(fqn.ptr, fqn.len);
             llvm_global.setLinkage(.Internal);
@@ -814,8 +821,8 @@ pub const Object = struct {
         }
     }
 
-    pub fn freeDecl(self: *Object, decl: *Module.Decl) void {
-        const llvm_value = self.decl_map.get(decl) orelse return;
+    pub fn freeDecl(self: *Object, decl_index: Module.Decl.Index) void {
+        const llvm_value = self.decl_map.get(decl_index) orelse return;
         llvm_value.deleteGlobal();
     }
 
@@ -847,7 +854,7 @@ pub const Object = struct {
         const gpa = o.gpa;
         // Be careful not to reference this `gop` variable after any recursive calls
         // to `lowerDebugType`.
-        const gop = try o.di_type_map.getOrPutContext(gpa, ty, .{ .target = o.target });
+        const gop = try o.di_type_map.getOrPutContext(gpa, ty, .{ .mod = o.module });
         if (gop.found_existing) {
             const annotated = gop.value_ptr.*;
             const di_type = annotated.toDIType();
@@ -860,7 +867,7 @@ pub const Object = struct {
             };
             return o.lowerDebugTypeImpl(entry, resolve, di_type);
         }
-        errdefer assert(o.di_type_map.orderedRemoveContext(ty, .{ .target = o.target }));
+        errdefer assert(o.di_type_map.orderedRemoveContext(ty, .{ .mod = o.module }));
         // The Type memory is ephemeral; since we want to store a longer-lived
         // reference, we need to copy it here.
         gop.key_ptr.* = try ty.copy(o.type_map_arena.allocator());
@@ -891,7 +898,7 @@ pub const Object = struct {
             .Int => {
                 const info = ty.intInfo(target);
                 assert(info.bits != 0);
-                const name = try ty.nameAlloc(gpa, target);
+                const name = try ty.nameAlloc(gpa, o.module);
                 defer gpa.free(name);
                 const dwarf_encoding: c_uint = switch (info.signedness) {
                     .signed => DW.ATE.signed,
@@ -902,13 +909,14 @@ pub const Object = struct {
                 return di_type;
             },
             .Enum => {
-                const owner_decl = ty.getOwnerDecl();
+                const owner_decl_index = ty.getOwnerDecl();
+                const owner_decl = o.module.declPtr(owner_decl_index);
 
                 if (!ty.hasRuntimeBitsIgnoreComptime()) {
-                    const enum_di_ty = try o.makeEmptyNamespaceDIType(owner_decl);
+                    const enum_di_ty = try o.makeEmptyNamespaceDIType(owner_decl_index);
                     // The recursive call to `lowerDebugType` via `makeEmptyNamespaceDIType`
                     // means we can't use `gop` anymore.
-                    try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(enum_di_ty), .{ .target = o.target });
+                    try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(enum_di_ty), .{ .mod = o.module });
                     return enum_di_ty;
                 }
 
@@ -938,7 +946,7 @@ pub const Object = struct {
                 const di_file = try o.getDIFile(gpa, owner_decl.src_namespace.file_scope);
                 const di_scope = try o.namespaceToDebugScope(owner_decl.src_namespace);
 
-                const name = try ty.nameAlloc(gpa, target);
+                const name = try ty.nameAlloc(gpa, o.module);
                 defer gpa.free(name);
                 var buffer: Type.Payload.Bits = undefined;
                 const int_ty = ty.intTagType(&buffer);
@@ -956,12 +964,12 @@ pub const Object = struct {
                     "",
                 );
                 // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
-                try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(enum_di_ty), .{ .target = o.target });
+                try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(enum_di_ty), .{ .mod = o.module });
                 return enum_di_ty;
             },
             .Float => {
                 const bits = ty.floatBits(target);
-                const name = try ty.nameAlloc(gpa, target);
+                const name = try ty.nameAlloc(gpa, o.module);
                 defer gpa.free(name);
                 const di_type = dib.createBasicType(name, bits, DW.ATE.float);
                 gop.value_ptr.* = AnnotatedDITypePtr.initFull(di_type);
@@ -1009,7 +1017,7 @@ pub const Object = struct {
                     const bland_ptr_ty = Type.initPayload(&payload.base);
                     const ptr_di_ty = try o.lowerDebugType(bland_ptr_ty, resolve);
                     // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
-                    try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.init(ptr_di_ty, resolve), .{ .target = o.target });
+                    try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.init(ptr_di_ty, resolve), .{ .mod = o.module });
                     return ptr_di_ty;
                 }
 
@@ -1018,7 +1026,7 @@ pub const Object = struct {
                     const ptr_ty = ty.slicePtrFieldType(&buf);
                     const len_ty = Type.usize;
 
-                    const name = try ty.nameAlloc(gpa, target);
+                    const name = try ty.nameAlloc(gpa, o.module);
                     defer gpa.free(name);
                     const di_file: ?*llvm.DIFile = null;
                     const line = 0;
@@ -1089,12 +1097,12 @@ pub const Object = struct {
                     );
                     dib.replaceTemporary(fwd_decl, full_di_ty);
                     // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
-                    try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .target = o.target });
+                    try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .mod = o.module });
                     return full_di_ty;
                 }
 
                 const elem_di_ty = try o.lowerDebugType(ptr_info.pointee_type, .fwd);
-                const name = try ty.nameAlloc(gpa, target);
+                const name = try ty.nameAlloc(gpa, o.module);
                 defer gpa.free(name);
                 const ptr_di_ty = dib.createPointerType(
                     elem_di_ty,
@@ -1103,7 +1111,7 @@ pub const Object = struct {
                     name,
                 );
                 // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
-                try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(ptr_di_ty), .{ .target = o.target });
+                try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(ptr_di_ty), .{ .mod = o.module });
                 return ptr_di_ty;
             },
             .Opaque => {
@@ -1112,9 +1120,10 @@ pub const Object = struct {
                     gop.value_ptr.* = AnnotatedDITypePtr.initFull(di_ty);
                     return di_ty;
                 }
-                const name = try ty.nameAlloc(gpa, target);
+                const name = try ty.nameAlloc(gpa, o.module);
                 defer gpa.free(name);
-                const owner_decl = ty.getOwnerDecl();
+                const owner_decl_index = ty.getOwnerDecl();
+                const owner_decl = o.module.declPtr(owner_decl_index);
                 const opaque_di_ty = dib.createForwardDeclType(
                     DW.TAG.structure_type,
                     name,
@@ -1124,7 +1133,7 @@ pub const Object = struct {
                 );
                 // The recursive call to `lowerDebugType` va `namespaceToDebugScope`
                 // means we can't use `gop` anymore.
-                try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(opaque_di_ty), .{ .target = o.target });
+                try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(opaque_di_ty), .{ .mod = o.module });
                 return opaque_di_ty;
             },
             .Array => {
@@ -1135,7 +1144,7 @@ pub const Object = struct {
                     @intCast(c_int, ty.arrayLen()),
                 );
                 // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
-                try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(array_di_ty), .{ .target = o.target });
+                try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(array_di_ty), .{ .mod = o.module });
                 return array_di_ty;
             },
             .Vector => {
@@ -1146,11 +1155,11 @@ pub const Object = struct {
                     ty.vectorLen(),
                 );
                 // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
-                try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(vector_di_ty), .{ .target = o.target });
+                try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(vector_di_ty), .{ .mod = o.module });
                 return vector_di_ty;
             },
             .Optional => {
-                const name = try ty.nameAlloc(gpa, target);
+                const name = try ty.nameAlloc(gpa, o.module);
                 defer gpa.free(name);
                 var buf: Type.Payload.ElemType = undefined;
                 const child_ty = ty.optionalChild(&buf);
@@ -1162,7 +1171,7 @@ pub const Object = struct {
                 if (ty.isPtrLikeOptional()) {
                     const ptr_di_ty = try o.lowerDebugType(child_ty, resolve);
                     // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
-                    try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(ptr_di_ty), .{ .target = o.target });
+                    try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(ptr_di_ty), .{ .mod = o.module });
                     return ptr_di_ty;
                 }
 
@@ -1235,7 +1244,7 @@ pub const Object = struct {
                 );
                 dib.replaceTemporary(fwd_decl, full_di_ty);
                 // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
-                try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .target = o.target });
+                try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .mod = o.module });
                 return full_di_ty;
             },
             .ErrorUnion => {
@@ -1244,10 +1253,10 @@ pub const Object = struct {
                 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
                     const err_set_di_ty = try o.lowerDebugType(err_set_ty, .full);
                     // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
-                    try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(err_set_di_ty), .{ .target = o.target });
+                    try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(err_set_di_ty), .{ .mod = o.module });
                     return err_set_di_ty;
                 }
-                const name = try ty.nameAlloc(gpa, target);
+                const name = try ty.nameAlloc(gpa, o.module);
                 defer gpa.free(name);
                 const di_file: ?*llvm.DIFile = null;
                 const line = 0;
@@ -1332,7 +1341,7 @@ pub const Object = struct {
                 );
                 dib.replaceTemporary(fwd_decl, full_di_ty);
                 // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
-                try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .target = o.target });
+                try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .mod = o.module });
                 return full_di_ty;
             },
             .ErrorSet => {
@@ -1344,7 +1353,7 @@ pub const Object = struct {
             },
             .Struct => {
                 const compile_unit_scope = o.di_compile_unit.?.toScope();
-                const name = try ty.nameAlloc(gpa, target);
+                const name = try ty.nameAlloc(gpa, o.module);
                 defer gpa.free(name);
 
                 if (ty.castTag(.@"struct")) |payload| {
@@ -1431,7 +1440,7 @@ pub const Object = struct {
                     );
                     dib.replaceTemporary(fwd_decl, full_di_ty);
                     // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
-                    try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .target = o.target });
+                    try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .mod = o.module });
                     return full_di_ty;
                 }
 
@@ -1445,23 +1454,23 @@ pub const Object = struct {
                         // into. Therefore we can satisfy this by making an empty namespace,
                         // rather than changing the frontend to unnecessarily resolve the
                         // struct field types.
-                        const owner_decl = ty.getOwnerDecl();
-                        const struct_di_ty = try o.makeEmptyNamespaceDIType(owner_decl);
+                        const owner_decl_index = ty.getOwnerDecl();
+                        const struct_di_ty = try o.makeEmptyNamespaceDIType(owner_decl_index);
                         dib.replaceTemporary(fwd_decl, struct_di_ty);
                         // The recursive call to `lowerDebugType` via `makeEmptyNamespaceDIType`
                         // means we can't use `gop` anymore.
-                        try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(struct_di_ty), .{ .target = o.target });
+                        try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(struct_di_ty), .{ .mod = o.module });
                         return struct_di_ty;
                     }
                 }
 
                 if (!ty.hasRuntimeBitsIgnoreComptime()) {
-                    const owner_decl = ty.getOwnerDecl();
-                    const struct_di_ty = try o.makeEmptyNamespaceDIType(owner_decl);
+                    const owner_decl_index = ty.getOwnerDecl();
+                    const struct_di_ty = try o.makeEmptyNamespaceDIType(owner_decl_index);
                     dib.replaceTemporary(fwd_decl, struct_di_ty);
                     // The recursive call to `lowerDebugType` via `makeEmptyNamespaceDIType`
                     // means we can't use `gop` anymore.
-                    try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(struct_di_ty), .{ .target = o.target });
+                    try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(struct_di_ty), .{ .mod = o.module });
                     return struct_di_ty;
                 }
 
@@ -1516,14 +1525,14 @@ pub const Object = struct {
                 );
                 dib.replaceTemporary(fwd_decl, full_di_ty);
                 // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
-                try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .target = o.target });
+                try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .mod = o.module });
                 return full_di_ty;
             },
             .Union => {
                 const compile_unit_scope = o.di_compile_unit.?.toScope();
-                const owner_decl = ty.getOwnerDecl();
+                const owner_decl_index = ty.getOwnerDecl();
 
-                const name = try ty.nameAlloc(gpa, target);
+                const name = try ty.nameAlloc(gpa, o.module);
                 defer gpa.free(name);
 
                 const fwd_decl = opt_fwd_decl orelse blk: {
@@ -1540,11 +1549,11 @@ pub const Object = struct {
                 };
 
                 if (!ty.hasRuntimeBitsIgnoreComptime()) {
-                    const union_di_ty = try o.makeEmptyNamespaceDIType(owner_decl);
+                    const union_di_ty = try o.makeEmptyNamespaceDIType(owner_decl_index);
                     dib.replaceTemporary(fwd_decl, union_di_ty);
                     // The recursive call to `lowerDebugType` via `makeEmptyNamespaceDIType`
                     // means we can't use `gop` anymore.
-                    try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(union_di_ty), .{ .target = o.target });
+                    try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(union_di_ty), .{ .mod = o.module });
                     return union_di_ty;
                 }
 
@@ -1572,7 +1581,7 @@ pub const Object = struct {
                     dib.replaceTemporary(fwd_decl, full_di_ty);
                     // The recursive call to `lowerDebugType` via `makeEmptyNamespaceDIType`
                     // means we can't use `gop` anymore.
-                    try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .target = o.target });
+                    try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .mod = o.module });
                     return full_di_ty;
                 }
 
@@ -1626,7 +1635,7 @@ pub const Object = struct {
                 if (layout.tag_size == 0) {
                     dib.replaceTemporary(fwd_decl, union_di_ty);
                     // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
-                    try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(union_di_ty), .{ .target = o.target });
+                    try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(union_di_ty), .{ .mod = o.module });
                     return union_di_ty;
                 }
 
@@ -1685,7 +1694,7 @@ pub const Object = struct {
                 );
                 dib.replaceTemporary(fwd_decl, full_di_ty);
                 // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
-                try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .target = o.target });
+                try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(full_di_ty), .{ .mod = o.module });
                 return full_di_ty;
             },
             .Fn => {
@@ -1733,7 +1742,7 @@ pub const Object = struct {
                     0,
                 );
                 // The recursive call to `lowerDebugType` means we can't use `gop` anymore.
-                try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(fn_di_ty), .{ .target = o.target });
+                try o.di_type_map.putContext(gpa, ty, AnnotatedDITypePtr.initFull(fn_di_ty), .{ .mod = o.module });
                 return fn_di_ty;
             },
             .ComptimeInt => unreachable,
@@ -1762,7 +1771,8 @@ pub const Object = struct {
     /// This is to be used instead of void for debug info types, to avoid tripping
     /// Assertion `!isa<DIType>(Scope) && "shouldn't make a namespace scope for a type"'
     /// when targeting CodeView (Windows).
-    fn makeEmptyNamespaceDIType(o: *Object, decl: *const Module.Decl) !*llvm.DIType {
+    fn makeEmptyNamespaceDIType(o: *Object, decl_index: Module.Decl.Index) !*llvm.DIType {
+        const decl = o.module.declPtr(decl_index);
         const fields: [0]*llvm.DIType = .{};
         return o.di_builder.?.createStructType(
             try o.namespaceToDebugScope(decl.src_namespace),
@@ -1787,6 +1797,7 @@ pub const DeclGen = struct {
     object: *Object,
     module: *Module,
     decl: *Module.Decl,
+    decl_index: Module.Decl.Index,
     gpa: Allocator,
     err_msg: ?*Module.ErrorMsg,
 
@@ -1804,6 +1815,7 @@ pub const DeclGen = struct {
 
     fn genDecl(dg: *DeclGen) !void {
         const decl = dg.decl;
+        const decl_index = dg.decl_index;
         assert(decl.has_tv);
 
         log.debug("gen: {s} type: {}, value: {}", .{
@@ -1817,7 +1829,7 @@ pub const DeclGen = struct {
             _ = try dg.resolveLlvmFunction(extern_fn.data.owner_decl);
         } else {
             const target = dg.module.getTarget();
-            var global = try dg.resolveGlobalDecl(decl);
+            var global = try dg.resolveGlobalDecl(decl_index);
             global.setAlignment(decl.getAlignment(target));
             assert(decl.has_tv);
             const init_val = if (decl.val.castTag(.variable)) |payload| init_val: {
@@ -1858,7 +1870,7 @@ pub const DeclGen = struct {
                     // old uses.
                     const new_global_ptr = new_global.constBitCast(global.typeOf());
                     global.replaceAllUsesWith(new_global_ptr);
-                    dg.object.decl_map.putAssumeCapacity(decl, new_global);
+                    dg.object.decl_map.putAssumeCapacity(decl_index, new_global);
                     new_global.takeName(global);
                     global.deleteGlobal();
                     global = new_global;
@@ -1869,7 +1881,7 @@ pub const DeclGen = struct {
                 const di_file = try dg.object.getDIFile(dg.gpa, decl.src_namespace.file_scope);
 
                 const line_number = decl.src_line + 1;
-                const is_internal_linkage = !dg.module.decl_exports.contains(decl);
+                const is_internal_linkage = !dg.module.decl_exports.contains(decl_index);
                 const di_global = dib.createGlobalVariable(
                     di_file.toScope(),
                     decl.name,
@@ -1888,12 +1900,10 @@ pub const DeclGen = struct {
     /// If the llvm function does not exist, create it.
     /// Note that this can be called before the function's semantic analysis has
     /// completed, so if any attributes rely on that, they must be done in updateFunc, not here.
-    fn resolveLlvmFunction(dg: *DeclGen, decl: *Module.Decl) !*const llvm.Value {
-        return dg.resolveLlvmFunctionExtra(decl, decl.ty);
-    }
-
-    fn resolveLlvmFunctionExtra(dg: *DeclGen, decl: *Module.Decl, zig_fn_type: Type) !*const llvm.Value {
-        const gop = try dg.object.decl_map.getOrPut(dg.gpa, decl);
+    fn resolveLlvmFunction(dg: *DeclGen, decl_index: Module.Decl.Index) !*const llvm.Value {
+        const decl = dg.module.declPtr(decl_index);
+        const zig_fn_type = decl.ty;
+        const gop = try dg.object.decl_map.getOrPut(dg.gpa, decl_index);
         if (gop.found_existing) return gop.value_ptr.*;
 
         assert(decl.has_tv);
@@ -1903,7 +1913,7 @@ pub const DeclGen = struct {
 
         const fn_type = try dg.llvmType(zig_fn_type);
 
-        const fqn = try decl.getFullyQualifiedName(dg.gpa);
+        const fqn = try decl.getFullyQualifiedName(dg.module);
         defer dg.gpa.free(fqn);
 
         const llvm_addrspace = dg.llvmAddressSpace(decl.@"addrspace");
@@ -1996,12 +2006,13 @@ pub const DeclGen = struct {
         // TODO add target-cpu and target-features fn attributes
     }
 
-    fn resolveGlobalDecl(dg: *DeclGen, decl: *Module.Decl) Error!*const llvm.Value {
-        const gop = try dg.object.decl_map.getOrPut(dg.gpa, decl);
+    fn resolveGlobalDecl(dg: *DeclGen, decl_index: Module.Decl.Index) Error!*const llvm.Value {
+        const gop = try dg.object.decl_map.getOrPut(dg.gpa, decl_index);
         if (gop.found_existing) return gop.value_ptr.*;
-        errdefer assert(dg.object.decl_map.remove(decl));
+        errdefer assert(dg.object.decl_map.remove(decl_index));
 
-        const fqn = try decl.getFullyQualifiedName(dg.gpa);
+        const decl = dg.module.declPtr(decl_index);
+        const fqn = try decl.getFullyQualifiedName(dg.module);
         defer dg.gpa.free(fqn);
 
         const llvm_type = try dg.llvmType(decl.ty);
@@ -2122,7 +2133,7 @@ pub const DeclGen = struct {
             },
             .Opaque => switch (t.tag()) {
                 .@"opaque" => {
-                    const gop = try dg.object.type_map.getOrPutContext(gpa, t, .{ .target = target });
+                    const gop = try dg.object.type_map.getOrPutContext(gpa, t, .{ .mod = dg.module });
                     if (gop.found_existing) return gop.value_ptr.*;
 
                     // The Type memory is ephemeral; since we want to store a longer-lived
@@ -2130,7 +2141,7 @@ pub const DeclGen = struct {
                     gop.key_ptr.* = try t.copy(dg.object.type_map_arena.allocator());
 
                     const opaque_obj = t.castTag(.@"opaque").?.data;
-                    const name = try opaque_obj.getFullyQualifiedName(gpa);
+                    const name = try opaque_obj.getFullyQualifiedName(dg.module);
                     defer gpa.free(name);
 
                     const llvm_struct_ty = dg.context.structCreateNamed(name);
@@ -2191,7 +2202,7 @@ pub const DeclGen = struct {
                 return dg.context.intType(16);
             },
             .Struct => {
-                const gop = try dg.object.type_map.getOrPutContext(gpa, t, .{ .target = target });
+                const gop = try dg.object.type_map.getOrPutContext(gpa, t, .{ .mod = dg.module });
                 if (gop.found_existing) return gop.value_ptr.*;
 
                 // The Type memory is ephemeral; since we want to store a longer-lived
@@ -2260,7 +2271,7 @@ pub const DeclGen = struct {
                     return int_llvm_ty;
                 }
 
-                const name = try struct_obj.getFullyQualifiedName(gpa);
+                const name = try struct_obj.getFullyQualifiedName(dg.module);
                 defer gpa.free(name);
 
                 const llvm_struct_ty = dg.context.structCreateNamed(name);
@@ -2314,7 +2325,7 @@ pub const DeclGen = struct {
                 return llvm_struct_ty;
             },
             .Union => {
-                const gop = try dg.object.type_map.getOrPutContext(gpa, t, .{ .target = target });
+                const gop = try dg.object.type_map.getOrPutContext(gpa, t, .{ .mod = dg.module });
                 if (gop.found_existing) return gop.value_ptr.*;
 
                 // The Type memory is ephemeral; since we want to store a longer-lived
@@ -2330,7 +2341,7 @@ pub const DeclGen = struct {
                     return enum_tag_llvm_ty;
                 }
 
-                const name = try union_obj.getFullyQualifiedName(gpa);
+                const name = try union_obj.getFullyQualifiedName(dg.module);
                 defer gpa.free(name);
 
                 const llvm_union_ty = dg.context.structCreateNamed(name);
@@ -2439,7 +2450,7 @@ pub const DeclGen = struct {
             // TODO this duplicates code with Pointer but they should share the handling
             // of the tv.val.tag() and then Int should do extra constPtrToInt on top
             .Int => switch (tv.val.tag()) {
-                .decl_ref_mut => return lowerDeclRefValue(dg, tv, tv.val.castTag(.decl_ref_mut).?.data.decl),
+                .decl_ref_mut => return lowerDeclRefValue(dg, tv, tv.val.castTag(.decl_ref_mut).?.data.decl_index),
                 .decl_ref => return lowerDeclRefValue(dg, tv, tv.val.castTag(.decl_ref).?.data),
                 else => {
                     var bigint_space: Value.BigIntSpace = undefined;
@@ -2524,12 +2535,13 @@ pub const DeclGen = struct {
                 }
             },
             .Pointer => switch (tv.val.tag()) {
-                .decl_ref_mut => return lowerDeclRefValue(dg, tv, tv.val.castTag(.decl_ref_mut).?.data.decl),
+                .decl_ref_mut => return lowerDeclRefValue(dg, tv, tv.val.castTag(.decl_ref_mut).?.data.decl_index),
                 .decl_ref => return lowerDeclRefValue(dg, tv, tv.val.castTag(.decl_ref).?.data),
                 .variable => {
-                    const decl = tv.val.castTag(.variable).?.data.owner_decl;
-                    decl.markAlive();
-                    const val = try dg.resolveGlobalDecl(decl);
+                    const decl_index = tv.val.castTag(.variable).?.data.owner_decl;
+                    const decl = dg.module.declPtr(decl_index);
+                    dg.module.markDeclAlive(decl);
+                    const val = try dg.resolveGlobalDecl(decl_index);
                     const llvm_var_type = try dg.llvmType(tv.ty);
                     const llvm_addrspace = dg.llvmAddressSpace(decl.@"addrspace");
                     const llvm_type = llvm_var_type.pointerType(llvm_addrspace);
@@ -2683,13 +2695,14 @@ pub const DeclGen = struct {
                 return dg.context.constStruct(&fields, fields.len, .False);
             },
             .Fn => {
-                const fn_decl = switch (tv.val.tag()) {
+                const fn_decl_index = switch (tv.val.tag()) {
                     .extern_fn => tv.val.castTag(.extern_fn).?.data.owner_decl,
                     .function => tv.val.castTag(.function).?.data.owner_decl,
                     else => unreachable,
                 };
-                fn_decl.markAlive();
-                return dg.resolveLlvmFunction(fn_decl);
+                const fn_decl = dg.module.declPtr(fn_decl_index);
+                dg.module.markDeclAlive(fn_decl);
+                return dg.resolveLlvmFunction(fn_decl_index);
             },
             .ErrorSet => {
                 const llvm_ty = try dg.llvmType(tv.ty);
@@ -2911,7 +2924,7 @@ pub const DeclGen = struct {
                     });
                 }
                 const union_obj = tv.ty.cast(Type.Payload.Union).?.data;
-                const field_index = union_obj.tag_ty.enumTagFieldIndex(tag_and_val.tag, target).?;
+                const field_index = union_obj.tag_ty.enumTagFieldIndex(tag_and_val.tag, dg.module).?;
                 assert(union_obj.haveFieldTypes());
                 const field_ty = union_obj.fields.values()[field_index].ty;
                 const payload = p: {
@@ -3049,17 +3062,22 @@ pub const DeclGen = struct {
         llvm_ptr: *const llvm.Value,
     };
 
-    fn lowerParentPtrDecl(dg: *DeclGen, ptr_val: Value, decl: *Module.Decl, ptr_child_ty: Type) Error!*const llvm.Value {
-        decl.markAlive();
+    fn lowerParentPtrDecl(
+        dg: *DeclGen,
+        ptr_val: Value,
+        decl_index: Module.Decl.Index,
+        ptr_child_ty: Type,
+    ) Error!*const llvm.Value {
+        const decl = dg.module.declPtr(decl_index);
+        dg.module.markDeclAlive(decl);
         var ptr_ty_payload: Type.Payload.ElemType = .{
             .base = .{ .tag = .single_mut_pointer },
             .data = decl.ty,
         };
         const ptr_ty = Type.initPayload(&ptr_ty_payload.base);
-        const llvm_ptr = try dg.lowerDeclRefValue(.{ .ty = ptr_ty, .val = ptr_val }, decl);
+        const llvm_ptr = try dg.lowerDeclRefValue(.{ .ty = ptr_ty, .val = ptr_val }, decl_index);
 
-        const target = dg.module.getTarget();
-        if (ptr_child_ty.eql(decl.ty, target)) {
+        if (ptr_child_ty.eql(decl.ty, dg.module)) {
             return llvm_ptr;
         } else {
             return llvm_ptr.constBitCast((try dg.llvmType(ptr_child_ty)).pointerType(0));
@@ -3071,7 +3089,7 @@ pub const DeclGen = struct {
         var bitcast_needed: bool = undefined;
         const llvm_ptr = switch (ptr_val.tag()) {
             .decl_ref_mut => {
-                const decl = ptr_val.castTag(.decl_ref_mut).?.data.decl;
+                const decl = ptr_val.castTag(.decl_ref_mut).?.data.decl_index;
                 return dg.lowerParentPtrDecl(ptr_val, decl, ptr_child_ty);
             },
             .decl_ref => {
@@ -3123,7 +3141,7 @@ pub const DeclGen = struct {
                     },
                     .Struct => {
                         const field_ty = parent_ty.structFieldType(field_index);
-                        bitcast_needed = !field_ty.eql(ptr_child_ty, target);
+                        bitcast_needed = !field_ty.eql(ptr_child_ty, dg.module);
 
                         var ty_buf: Type.Payload.Pointer = undefined;
                         const llvm_field_index = llvmFieldIndex(parent_ty, field_index, target, &ty_buf).?;
@@ -3139,7 +3157,7 @@ pub const DeclGen = struct {
             .elem_ptr => blk: {
                 const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;
                 const parent_llvm_ptr = try dg.lowerParentPtr(elem_ptr.array_ptr, elem_ptr.elem_ty);
-                bitcast_needed = !elem_ptr.elem_ty.eql(ptr_child_ty, target);
+                bitcast_needed = !elem_ptr.elem_ty.eql(ptr_child_ty, dg.module);
 
                 const llvm_usize = try dg.llvmType(Type.usize);
                 const indices: [1]*const llvm.Value = .{
@@ -3153,7 +3171,7 @@ pub const DeclGen = struct {
                 var buf: Type.Payload.ElemType = undefined;
 
                 const payload_ty = opt_payload_ptr.container_ty.optionalChild(&buf);
-                bitcast_needed = !payload_ty.eql(ptr_child_ty, target);
+                bitcast_needed = !payload_ty.eql(ptr_child_ty, dg.module);
 
                 if (!payload_ty.hasRuntimeBitsIgnoreComptime() or payload_ty.isPtrLikeOptional()) {
                     // In this case, we represent pointer to optional the same as pointer
@@ -3173,7 +3191,7 @@ pub const DeclGen = struct {
                 const parent_llvm_ptr = try dg.lowerParentPtr(eu_payload_ptr.container_ptr, eu_payload_ptr.container_ty);
 
                 const payload_ty = eu_payload_ptr.container_ty.errorUnionPayload();
-                bitcast_needed = !payload_ty.eql(ptr_child_ty, target);
+                bitcast_needed = !payload_ty.eql(ptr_child_ty, dg.module);
 
                 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
                     // In this case, we represent pointer to error union the same as pointer
@@ -3201,15 +3219,14 @@ pub const DeclGen = struct {
     fn lowerDeclRefValue(
         self: *DeclGen,
         tv: TypedValue,
-        decl: *Module.Decl,
+        decl_index: Module.Decl.Index,
     ) Error!*const llvm.Value {
-        const target = self.module.getTarget();
         if (tv.ty.isSlice()) {
             var buf: Type.SlicePtrFieldTypeBuffer = undefined;
             const ptr_ty = tv.ty.slicePtrFieldType(&buf);
             var slice_len: Value.Payload.U64 = .{
                 .base = .{ .tag = .int_u64 },
-                .data = tv.val.sliceLen(target),
+                .data = tv.val.sliceLen(self.module),
             };
             const fields: [2]*const llvm.Value = .{
                 try self.genTypedValue(.{
@@ -3229,8 +3246,9 @@ pub const DeclGen = struct {
         // const bar = foo;
         // ... &bar;
         // `bar` is just an alias and we actually want to lower a reference to `foo`.
+        const decl = self.module.declPtr(decl_index);
         if (decl.val.castTag(.function)) |func| {
-            if (func.data.owner_decl != decl) {
+            if (func.data.owner_decl != decl_index) {
                 return self.lowerDeclRefValue(tv, func.data.owner_decl);
             }
         }
@@ -3240,12 +3258,12 @@ pub const DeclGen = struct {
             return self.lowerPtrToVoid(tv.ty);
         }
 
-        decl.markAlive();
+        self.module.markDeclAlive(decl);
 
         const llvm_val = if (is_fn_body)
-            try self.resolveLlvmFunction(decl)
+            try self.resolveLlvmFunction(decl_index)
         else
-            try self.resolveGlobalDecl(decl);
+            try self.resolveGlobalDecl(decl_index);
 
         const llvm_type = try self.llvmType(tv.ty);
         if (tv.ty.zigTypeTag() == .Int) {
@@ -4405,7 +4423,8 @@ pub const FuncGen = struct {
         const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
 
         const func = self.air.values[ty_pl.payload].castTag(.function).?.data;
-        const decl = func.owner_decl;
+        const decl_index = func.owner_decl;
+        const decl = self.dg.module.declPtr(decl_index);
         const di_file = try self.dg.object.getDIFile(self.gpa, decl.src_namespace.file_scope);
         self.di_file = di_file;
         const line_number = decl.src_line + 1;
@@ -4417,10 +4436,10 @@ pub const FuncGen = struct {
             .base_line = self.base_line,
         });
 
-        const fqn = try decl.getFullyQualifiedName(self.gpa);
+        const fqn = try decl.getFullyQualifiedName(self.dg.module);
         defer self.gpa.free(fqn);
 
-        const is_internal_linkage = !self.dg.module.decl_exports.contains(decl);
+        const is_internal_linkage = !self.dg.module.decl_exports.contains(decl_index);
         const subprogram = dib.createFunction(
             di_file.toScope(),
             decl.name,
@@ -4447,7 +4466,8 @@ pub const FuncGen = struct {
         const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
 
         const func = self.air.values[ty_pl.payload].castTag(.function).?.data;
-        const decl = func.owner_decl;
+        const mod = self.dg.module;
+        const decl = mod.declPtr(func.owner_decl);
         const di_file = try self.dg.object.getDIFile(self.gpa, decl.src_namespace.file_scope);
         self.di_file = di_file;
         const old = self.dbg_inlined.pop();
@@ -5887,7 +5907,7 @@ pub const FuncGen = struct {
         if (self.dg.object.di_builder) |dib| {
             const src_index = self.getSrcArgIndex(self.arg_index - 1);
             const func = self.dg.decl.getFunction().?;
-            const lbrace_line = func.owner_decl.src_line + func.lbrace_line + 1;
+            const lbrace_line = self.dg.module.declPtr(func.owner_decl).src_line + func.lbrace_line + 1;
             const lbrace_col = func.lbrace_column + 1;
             const di_local_var = dib.createParameterVariable(
                 self.di_scope.?,
@@ -6430,8 +6450,9 @@ pub const FuncGen = struct {
         const operand = try self.resolveInst(un_op);
         const enum_ty = self.air.typeOf(un_op);
 
+        const mod = self.dg.module;
         const llvm_fn_name = try std.fmt.allocPrintZ(arena, "__zig_tag_name_{s}", .{
-            try enum_ty.getOwnerDecl().getFullyQualifiedName(arena),
+            try mod.declPtr(enum_ty.getOwnerDecl()).getFullyQualifiedName(mod),
         });
 
         const llvm_fn = try self.getEnumTagNameFunction(enum_ty, llvm_fn_name);
@@ -6617,7 +6638,7 @@ pub const FuncGen = struct {
 
         for (values) |*val, i| {
             var buf: Value.ElemValueBuffer = undefined;
-            const elem = mask.elemValueBuffer(i, &buf);
+            const elem = mask.elemValueBuffer(self.dg.module, i, &buf);
             if (elem.isUndef()) {
                 val.* = llvm_i32.getUndef();
             } else {
src/codegen/spirv.zig
@@ -633,7 +633,13 @@ pub const DeclGen = struct {
         return result_id.toRef();
     }
 
-    fn airArithOp(self: *DeclGen, inst: Air.Inst.Index, comptime fop: Opcode, comptime sop: Opcode, comptime uop: Opcode) !IdRef {
+    fn airArithOp(
+        self: *DeclGen,
+        inst: Air.Inst.Index,
+        comptime fop: Opcode,
+        comptime sop: Opcode,
+        comptime uop: Opcode,
+    ) !IdRef {
         // LHS and RHS are guaranteed to have the same type, and AIR guarantees
         // the result to be the same as the LHS and RHS, which matches SPIR-V.
         const ty = self.air.typeOfIndex(inst);
@@ -644,10 +650,8 @@ pub const DeclGen = struct {
         const result_id = self.spv.allocId();
         const result_type_id = try self.resolveTypeId(ty);
 
-        const target = self.getTarget();
-
-        assert(self.air.typeOf(bin_op.lhs).eql(ty, target));
-        assert(self.air.typeOf(bin_op.rhs).eql(ty, target));
+        assert(self.air.typeOf(bin_op.lhs).eql(ty, self.module));
+        assert(self.air.typeOf(bin_op.rhs).eql(ty, self.module));
 
         // Binary operations are generally applicable to both scalar and vector operations
         // in SPIR-V, but int and float versions of operations require different opcodes.
@@ -694,7 +698,7 @@ pub const DeclGen = struct {
         const result_id = self.spv.allocId();
         const result_type_id = try self.resolveTypeId(Type.initTag(.bool));
         const op_ty = self.air.typeOf(bin_op.lhs);
-        assert(op_ty.eql(self.air.typeOf(bin_op.rhs), self.getTarget()));
+        assert(op_ty.eql(self.air.typeOf(bin_op.rhs), self.module));
 
         // Comparisons are generally applicable to both scalar and vector operations in SPIR-V,
         // but int and float versions of operations require different opcodes.
src/link/C.zig
@@ -21,7 +21,7 @@ base: link.File,
 /// This linker backend does not try to incrementally link output C source code.
 /// Instead, it tracks all declarations in this table, and iterates over it
 /// in the flush function, stitching pre-rendered pieces of C code together.
-decl_table: std.AutoArrayHashMapUnmanaged(*const Module.Decl, DeclBlock) = .{},
+decl_table: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, DeclBlock) = .{},
 /// Stores Type/Value data for `typedefs` to reference.
 /// Accumulates allocations and then there is a periodic garbage collection after flush().
 arena: std.heap.ArenaAllocator,
@@ -87,9 +87,9 @@ pub fn deinit(self: *C) void {
     self.arena.deinit();
 }
 
-pub fn freeDecl(self: *C, decl: *Module.Decl) void {
+pub fn freeDecl(self: *C, decl_index: Module.Decl.Index) void {
     const gpa = self.base.allocator;
-    if (self.decl_table.fetchSwapRemove(decl)) |kv| {
+    if (self.decl_table.fetchSwapRemove(decl_index)) |kv| {
         var decl_block = kv.value;
         decl_block.deinit(gpa);
     }
@@ -99,8 +99,8 @@ pub fn updateFunc(self: *C, module: *Module, func: *Module.Fn, air: Air, livenes
     const tracy = trace(@src());
     defer tracy.end();
 
-    const decl = func.owner_decl;
-    const gop = try self.decl_table.getOrPut(self.base.allocator, decl);
+    const decl_index = func.owner_decl;
+    const gop = try self.decl_table.getOrPut(self.base.allocator, decl_index);
     if (!gop.found_existing) {
         gop.value_ptr.* = .{};
     }
@@ -126,9 +126,10 @@ pub fn updateFunc(self: *C, module: *Module, func: *Module.Fn, air: Air, livenes
                 .gpa = module.gpa,
                 .module = module,
                 .error_msg = null,
-                .decl = decl,
+                .decl_index = decl_index,
+                .decl = module.declPtr(decl_index),
                 .fwd_decl = fwd_decl.toManaged(module.gpa),
-                .typedefs = typedefs.promoteContext(module.gpa, .{ .target = module.getTarget() }),
+                .typedefs = typedefs.promoteContext(module.gpa, .{ .mod = module }),
                 .typedefs_arena = self.arena.allocator(),
             },
             .code = code.toManaged(module.gpa),
@@ -150,7 +151,7 @@ pub fn updateFunc(self: *C, module: *Module, func: *Module.Fn, air: Air, livenes
 
     codegen.genFunc(&function) catch |err| switch (err) {
         error.AnalysisFail => {
-            try module.failed_decls.put(module.gpa, decl, function.object.dg.error_msg.?);
+            try module.failed_decls.put(module.gpa, decl_index, function.object.dg.error_msg.?);
             return;
         },
         else => |e| return e,
@@ -166,11 +167,11 @@ pub fn updateFunc(self: *C, module: *Module, func: *Module.Fn, air: Air, livenes
     code.shrinkAndFree(module.gpa, code.items.len);
 }
 
-pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {
+pub fn updateDecl(self: *C, module: *Module, decl_index: Module.Decl.Index) !void {
     const tracy = trace(@src());
     defer tracy.end();
 
-    const gop = try self.decl_table.getOrPut(self.base.allocator, decl);
+    const gop = try self.decl_table.getOrPut(self.base.allocator, decl_index);
     if (!gop.found_existing) {
         gop.value_ptr.* = .{};
     }
@@ -186,14 +187,17 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {
     typedefs.clearRetainingCapacity();
     code.shrinkRetainingCapacity(0);
 
+    const decl = module.declPtr(decl_index);
+
     var object: codegen.Object = .{
         .dg = .{
             .gpa = module.gpa,
             .module = module,
             .error_msg = null,
+            .decl_index = decl_index,
             .decl = decl,
             .fwd_decl = fwd_decl.toManaged(module.gpa),
-            .typedefs = typedefs.promoteContext(module.gpa, .{ .target = module.getTarget() }),
+            .typedefs = typedefs.promoteContext(module.gpa, .{ .mod = module }),
             .typedefs_arena = self.arena.allocator(),
         },
         .code = code.toManaged(module.gpa),
@@ -211,7 +215,7 @@ pub fn updateDecl(self: *C, module: *Module, decl: *Module.Decl) !void {
 
     codegen.genDecl(&object) catch |err| switch (err) {
         error.AnalysisFail => {
-            try module.failed_decls.put(module.gpa, decl, object.dg.error_msg.?);
+            try module.failed_decls.put(module.gpa, decl_index, object.dg.error_msg.?);
             return;
         },
         else => |e| return e,
@@ -287,14 +291,14 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node)
 
     const decl_keys = self.decl_table.keys();
     const decl_values = self.decl_table.values();
-    for (decl_keys) |decl| {
-        assert(decl.has_tv);
-        f.remaining_decls.putAssumeCapacityNoClobber(decl, {});
+    for (decl_keys) |decl_index| {
+        assert(module.declPtr(decl_index).has_tv);
+        f.remaining_decls.putAssumeCapacityNoClobber(decl_index, {});
     }
 
     while (f.remaining_decls.popOrNull()) |kv| {
-        const decl = kv.key;
-        try flushDecl(self, &f, decl);
+        const decl_index = kv.key;
+        try flushDecl(self, &f, decl_index);
     }
 
     f.all_buffers.items[err_typedef_index] = .{
@@ -305,7 +309,8 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node)
 
     // Now the function bodies.
     try f.all_buffers.ensureUnusedCapacity(gpa, f.fn_count);
-    for (decl_keys) |decl, i| {
+    for (decl_keys) |decl_index, i| {
+        const decl = module.declPtr(decl_index);
         if (decl.getFunction() != null) {
             const decl_block = &decl_values[i];
             const buf = decl_block.code.items;
@@ -325,7 +330,7 @@ pub fn flushModule(self: *C, comp: *Compilation, prog_node: *std.Progress.Node)
 }
 
 const Flush = struct {
-    remaining_decls: std.AutoArrayHashMapUnmanaged(*const Module.Decl, void) = .{},
+    remaining_decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, void) = .{},
     typedefs: Typedefs = .{},
     err_typedef_buf: std.ArrayListUnmanaged(u8) = .{},
     /// We collect a list of buffers to write, and write them all at once with pwritev ๐Ÿ˜Ž
@@ -354,7 +359,9 @@ const FlushDeclError = error{
 };
 
 /// Assumes `decl` was in the `remaining_decls` set, and has already been removed.
-fn flushDecl(self: *C, f: *Flush, decl: *const Module.Decl) FlushDeclError!void {
+fn flushDecl(self: *C, f: *Flush, decl_index: Module.Decl.Index) FlushDeclError!void {
+    const module = self.base.options.module.?;
+    const decl = module.declPtr(decl_index);
     // Before flushing any particular Decl we must ensure its
     // dependencies are already flushed, so that the order in the .c
     // file comes out correctly.
@@ -364,15 +371,17 @@ fn flushDecl(self: *C, f: *Flush, decl: *const Module.Decl) FlushDeclError!void
         }
     }
 
-    const decl_block = self.decl_table.getPtr(decl).?;
+    const decl_block = self.decl_table.getPtr(decl_index).?;
     const gpa = self.base.allocator;
 
     if (decl_block.typedefs.count() != 0) {
-        try f.typedefs.ensureUnusedCapacity(gpa, @intCast(u32, decl_block.typedefs.count()));
+        try f.typedefs.ensureUnusedCapacityContext(gpa, @intCast(u32, decl_block.typedefs.count()), .{
+            .mod = module,
+        });
         var it = decl_block.typedefs.iterator();
         while (it.next()) |new| {
             const gop = f.typedefs.getOrPutAssumeCapacityContext(new.key_ptr.*, .{
-                .target = self.base.options.target,
+                .mod = module,
             });
             if (!gop.found_existing) {
                 try f.err_typedef_buf.appendSlice(gpa, new.value_ptr.rendered);
@@ -417,8 +426,8 @@ pub fn flushEmitH(module: *Module) !void {
         .iov_len = zig_h.len,
     });
 
-    for (emit_h.decl_table.keys()) |decl| {
-        const decl_emit_h = decl.getEmitH(module);
+    for (emit_h.decl_table.keys()) |decl_index| {
+        const decl_emit_h = emit_h.declPtr(decl_index);
         const buf = decl_emit_h.fwd_decl.items;
         all_buffers.appendAssumeCapacity(.{
             .iov_base = buf.ptr,
@@ -442,11 +451,11 @@ pub fn flushEmitH(module: *Module) !void {
 pub fn updateDeclExports(
     self: *C,
     module: *Module,
-    decl: *Module.Decl,
+    decl_index: Module.Decl.Index,
     exports: []const *Module.Export,
 ) !void {
     _ = exports;
-    _ = decl;
+    _ = decl_index;
     _ = module;
     _ = self;
 }
src/link/Coff.zig
@@ -418,11 +418,12 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Coff {
     return self;
 }
 
-pub fn allocateDeclIndexes(self: *Coff, decl: *Module.Decl) !void {
+pub fn allocateDeclIndexes(self: *Coff, decl_index: Module.Decl.Index) !void {
     if (self.llvm_object) |_| return;
 
     try self.offset_table.ensureUnusedCapacity(self.base.allocator, 1);
 
+    const decl = self.base.options.module.?.declPtr(decl_index);
     if (self.offset_table_free_list.popOrNull()) |i| {
         decl.link.coff.offset_table_index = i;
     } else {
@@ -674,7 +675,8 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live
     var code_buffer = std.ArrayList(u8).init(self.base.allocator);
     defer code_buffer.deinit();
 
-    const decl = func.owner_decl;
+    const decl_index = func.owner_decl;
+    const decl = module.declPtr(decl_index);
     const res = try codegen.generateFunction(
         &self.base,
         decl.srcLoc(),
@@ -688,7 +690,7 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live
         .appended => code_buffer.items,
         .fail => |em| {
             decl.analysis = .codegen_failure;
-            try module.failed_decls.put(module.gpa, decl, em);
+            try module.failed_decls.put(module.gpa, decl_index, em);
             return;
         },
     };
@@ -696,24 +698,26 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live
     return self.finishUpdateDecl(module, func.owner_decl, code);
 }
 
-pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl: *Module.Decl) !u32 {
+pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.Index) !u32 {
     _ = self;
     _ = tv;
-    _ = decl;
+    _ = decl_index;
     log.debug("TODO lowerUnnamedConst for Coff", .{});
     return error.AnalysisFail;
 }
 
-pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void {
+pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !void {
     if (build_options.skip_non_native and builtin.object_format != .coff) {
         @panic("Attempted to compile for object format that was disabled by build configuration");
     }
     if (build_options.have_llvm) {
-        if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(module, decl);
+        if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(module, decl_index);
     }
     const tracy = trace(@src());
     defer tracy.end();
 
+    const decl = module.declPtr(decl_index);
+
     if (decl.val.tag() == .extern_fn) {
         return; // TODO Should we do more when front-end analyzed extern decl?
     }
@@ -735,15 +739,16 @@ pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void {
         .appended => code_buffer.items,
         .fail => |em| {
             decl.analysis = .codegen_failure;
-            try module.failed_decls.put(module.gpa, decl, em);
+            try module.failed_decls.put(module.gpa, decl_index, em);
             return;
         },
     };
 
-    return self.finishUpdateDecl(module, decl, code);
+    return self.finishUpdateDecl(module, decl_index, code);
 }
 
-fn finishUpdateDecl(self: *Coff, module: *Module, decl: *Module.Decl, code: []const u8) !void {
+fn finishUpdateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index, code: []const u8) !void {
+    const decl = module.declPtr(decl_index);
     const required_alignment = decl.ty.abiAlignment(self.base.options.target);
     const curr_size = decl.link.coff.size;
     if (curr_size != 0) {
@@ -778,15 +783,18 @@ fn finishUpdateDecl(self: *Coff, module: *Module, decl: *Module.Decl, code: []co
     try self.base.file.?.pwriteAll(code, self.section_data_offset + self.offset_table_size + decl.link.coff.text_offset);
 
     // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
-    const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
-    return self.updateDeclExports(module, decl, decl_exports);
+    const decl_exports = module.decl_exports.get(decl_index) orelse &[0]*Module.Export{};
+    return self.updateDeclExports(module, decl_index, decl_exports);
 }
 
-pub fn freeDecl(self: *Coff, decl: *Module.Decl) void {
+pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void {
     if (build_options.have_llvm) {
-        if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl);
+        if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index);
     }
 
+    const mod = self.base.options.module.?;
+    const decl = mod.declPtr(decl_index);
+
     // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
     self.freeTextBlock(&decl.link.coff);
     self.offset_table_free_list.append(self.base.allocator, decl.link.coff.offset_table_index) catch {};
@@ -795,16 +803,17 @@ pub fn freeDecl(self: *Coff, decl: *Module.Decl) void {
 pub fn updateDeclExports(
     self: *Coff,
     module: *Module,
-    decl: *Module.Decl,
+    decl_index: Module.Decl.Index,
     exports: []const *Module.Export,
 ) !void {
     if (build_options.skip_non_native and builtin.object_format != .coff) {
         @panic("Attempted to compile for object format that was disabled by build configuration");
     }
     if (build_options.have_llvm) {
-        if (self.llvm_object) |llvm_object| return llvm_object.updateDeclExports(module, decl, exports);
+        if (self.llvm_object) |llvm_object| return llvm_object.updateDeclExports(module, decl_index, exports);
     }
 
+    const decl = module.declPtr(decl_index);
     for (exports) |exp| {
         if (exp.options.section) |section_name| {
             if (!mem.eql(u8, section_name, ".text")) {
@@ -1474,8 +1483,14 @@ fn findLib(self: *Coff, arena: Allocator, name: []const u8) !?[]const u8 {
     return null;
 }
 
-pub fn getDeclVAddr(self: *Coff, decl: *const Module.Decl, reloc_info: link.File.RelocInfo) !u64 {
+pub fn getDeclVAddr(
+    self: *Coff,
+    decl_index: Module.Decl.Index,
+    reloc_info: link.File.RelocInfo,
+) !u64 {
     _ = reloc_info;
+    const mod = self.base.options.module.?;
+    const decl = mod.declPtr(decl_index);
     assert(self.llvm_object == null);
     return self.text_section_virtual_address + decl.link.coff.text_offset;
 }
src/link/Dwarf.zig
@@ -67,7 +67,7 @@ pub const Atom = struct {
 /// Decl's inner Atom is assigned an offset within the DWARF section.
 pub const DeclState = struct {
     gpa: Allocator,
-    target: std.Target,
+    mod: *Module,
     dbg_line: std.ArrayList(u8),
     dbg_info: std.ArrayList(u8),
     abbrev_type_arena: std.heap.ArenaAllocator,
@@ -81,10 +81,10 @@ pub const DeclState = struct {
     abbrev_relocs: std.ArrayListUnmanaged(AbbrevRelocation) = .{},
     exprloc_relocs: std.ArrayListUnmanaged(ExprlocRelocation) = .{},
 
-    fn init(gpa: Allocator, target: std.Target) DeclState {
+    fn init(gpa: Allocator, mod: *Module) DeclState {
         return .{
             .gpa = gpa,
-            .target = target,
+            .mod = mod,
             .dbg_line = std.ArrayList(u8).init(gpa),
             .dbg_info = std.ArrayList(u8).init(gpa),
             .abbrev_type_arena = std.heap.ArenaAllocator.init(gpa),
@@ -118,7 +118,7 @@ pub const DeclState = struct {
         addend: ?u32,
     ) !void {
         const resolv = self.abbrev_resolver.getContext(ty, .{
-            .target = self.target,
+            .mod = self.mod,
         }) orelse blk: {
             const sym_index = @intCast(u32, self.abbrev_table.items.len);
             try self.abbrev_table.append(self.gpa, .{
@@ -128,10 +128,10 @@ pub const DeclState = struct {
             });
             log.debug("@{d}: {}", .{ sym_index, ty.fmtDebug() });
             try self.abbrev_resolver.putNoClobberContext(self.gpa, ty, sym_index, .{
-                .target = self.target,
+                .mod = self.mod,
             });
             break :blk self.abbrev_resolver.getContext(ty, .{
-                .target = self.target,
+                .mod = self.mod,
             }).?;
         };
         const add: u32 = addend orelse 0;
@@ -153,8 +153,8 @@ pub const DeclState = struct {
     ) error{OutOfMemory}!void {
         const arena = self.abbrev_type_arena.allocator();
         const dbg_info_buffer = &self.dbg_info;
-        const target = self.target;
-        const target_endian = self.target.cpu.arch.endian();
+        const target = module.getTarget();
+        const target_endian = target.cpu.arch.endian();
 
         switch (ty.zigTypeTag()) {
             .NoReturn => unreachable,
@@ -181,7 +181,7 @@ pub const DeclState = struct {
                 // DW.AT.byte_size,  DW.FORM.data1
                 dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(target)));
                 // DW.AT.name,  DW.FORM.string
-                try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(target)});
+                try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(module)});
             },
             .Optional => {
                 if (ty.isPtrLikeOptional()) {
@@ -192,7 +192,7 @@ pub const DeclState = struct {
                     // DW.AT.byte_size,  DW.FORM.data1
                     dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(target)));
                     // DW.AT.name,  DW.FORM.string
-                    try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(target)});
+                    try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(module)});
                 } else {
                     // Non-pointer optionals are structs: struct { .maybe = *, .val = * }
                     var buf = try arena.create(Type.Payload.ElemType);
@@ -203,7 +203,7 @@ pub const DeclState = struct {
                     const abi_size = ty.abiSize(target);
                     try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size);
                     // DW.AT.name, DW.FORM.string
-                    try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(target)});
+                    try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(module)});
                     // DW.AT.member
                     try dbg_info_buffer.ensureUnusedCapacity(7);
                     dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.struct_member));
@@ -242,7 +242,7 @@ pub const DeclState = struct {
                     // DW.AT.byte_size, DW.FORM.sdata
                     dbg_info_buffer.appendAssumeCapacity(@sizeOf(usize) * 2);
                     // DW.AT.name, DW.FORM.string
-                    try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(target)});
+                    try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(module)});
                     // DW.AT.member
                     try dbg_info_buffer.ensureUnusedCapacity(5);
                     dbg_info_buffer.appendAssumeCapacity(@enumToInt(AbbrevKind.struct_member));
@@ -285,7 +285,7 @@ pub const DeclState = struct {
                 // DW.AT.array_type
                 try dbg_info_buffer.append(@enumToInt(AbbrevKind.array_type));
                 // DW.AT.name, DW.FORM.string
-                try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(target)});
+                try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(module)});
                 // DW.AT.type, DW.FORM.ref4
                 var index = dbg_info_buffer.items.len;
                 try dbg_info_buffer.resize(index + 4);
@@ -312,7 +312,7 @@ pub const DeclState = struct {
                 switch (ty.tag()) {
                     .tuple, .anon_struct => {
                         // DW.AT.name, DW.FORM.string
-                        try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(target)});
+                        try dbg_info_buffer.writer().print("{}\x00", .{ty.fmt(module)});
 
                         const fields = ty.tupleFields();
                         for (fields.types) |field, field_index| {
@@ -331,7 +331,7 @@ pub const DeclState = struct {
                     },
                     else => {
                         // DW.AT.name, DW.FORM.string
-                        const struct_name = try ty.nameAllocArena(arena, target);
+                        const struct_name = try ty.nameAllocArena(arena, module);
                         try dbg_info_buffer.ensureUnusedCapacity(struct_name.len + 1);
                         dbg_info_buffer.appendSliceAssumeCapacity(struct_name);
                         dbg_info_buffer.appendAssumeCapacity(0);
@@ -372,7 +372,7 @@ pub const DeclState = struct {
                 const abi_size = ty.abiSize(target);
                 try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size);
                 // DW.AT.name, DW.FORM.string
-                const enum_name = try ty.nameAllocArena(arena, target);
+                const enum_name = try ty.nameAllocArena(arena, module);
                 try dbg_info_buffer.ensureUnusedCapacity(enum_name.len + 1);
                 dbg_info_buffer.appendSliceAssumeCapacity(enum_name);
                 dbg_info_buffer.appendAssumeCapacity(0);
@@ -410,7 +410,7 @@ pub const DeclState = struct {
                 const payload_offset = if (layout.tag_align >= layout.payload_align) layout.tag_size else 0;
                 const tag_offset = if (layout.tag_align >= layout.payload_align) 0 else layout.payload_size;
                 const is_tagged = layout.tag_size > 0;
-                const union_name = try ty.nameAllocArena(arena, target);
+                const union_name = try ty.nameAllocArena(arena, module);
 
                 // TODO this is temporary to match current state of unions in Zig - we don't yet have
                 // safety checks implemented meaning the implicit tag is not yet stored and generated
@@ -491,7 +491,7 @@ pub const DeclState = struct {
                     self.abbrev_type_arena.allocator(),
                     module,
                     ty,
-                    self.target,
+                    target,
                     &self.dbg_info,
                 );
             },
@@ -507,7 +507,7 @@ pub const DeclState = struct {
                 // DW.AT.byte_size, DW.FORM.sdata
                 try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size);
                 // DW.AT.name, DW.FORM.string
-                const name = try ty.nameAllocArena(arena, target);
+                const name = try ty.nameAllocArena(arena, module);
                 try dbg_info_buffer.writer().print("{s}\x00", .{name});
 
                 // DW.AT.member
@@ -654,17 +654,17 @@ pub fn deinit(self: *Dwarf) void {
 
 /// Initializes Decl's state and its matching output buffers.
 /// Call this before `commitDeclState`.
-pub fn initDeclState(self: *Dwarf, decl: *Module.Decl) !DeclState {
+pub fn initDeclState(self: *Dwarf, mod: *Module, decl: *Module.Decl) !DeclState {
     const tracy = trace(@src());
     defer tracy.end();
 
-    const decl_name = try decl.getFullyQualifiedName(self.allocator);
+    const decl_name = try decl.getFullyQualifiedName(mod);
     defer self.allocator.free(decl_name);
 
     log.debug("initDeclState {s}{*}", .{ decl_name, decl });
 
     const gpa = self.allocator;
-    var decl_state = DeclState.init(gpa, self.target);
+    var decl_state = DeclState.init(gpa, mod);
     errdefer decl_state.deinit();
     const dbg_line_buffer = &decl_state.dbg_line;
     const dbg_info_buffer = &decl_state.dbg_info;
@@ -2133,7 +2133,7 @@ fn addDbgInfoErrorSet(
     const abi_size = ty.abiSize(target);
     try leb128.writeULEB128(dbg_info_buffer.writer(), abi_size);
     // DW.AT.name, DW.FORM.string
-    const name = try ty.nameAllocArena(arena, target);
+    const name = try ty.nameAllocArena(arena, module);
     try dbg_info_buffer.writer().print("{s}\x00", .{name});
 
     // DW.AT.enumerator
src/link/Elf.zig
@@ -134,7 +134,7 @@ atom_free_lists: std.AutoHashMapUnmanaged(u16, std.ArrayListUnmanaged(*TextBlock
 /// We store them here so that we can properly dispose of any allocated
 /// memory within the atom in the incremental linker.
 /// TODO consolidate this.
-decls: std.AutoHashMapUnmanaged(*Module.Decl, ?u16) = .{},
+decls: std.AutoHashMapUnmanaged(Module.Decl.Index, ?u16) = .{},
 
 /// List of atoms that are owned directly by the linker.
 /// Currently these are only atoms that are the result of linking
@@ -178,7 +178,7 @@ const Reloc = struct {
 };
 
 const RelocTable = std.AutoHashMapUnmanaged(*TextBlock, std.ArrayListUnmanaged(Reloc));
-const UnnamedConstTable = std.AutoHashMapUnmanaged(*Module.Decl, std.ArrayListUnmanaged(*TextBlock));
+const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(*TextBlock));
 
 /// When allocating, the ideal_capacity is calculated by
 /// actual_capacity + (actual_capacity / ideal_factor)
@@ -389,7 +389,10 @@ pub fn deinit(self: *Elf) void {
     }
 }
 
-pub fn getDeclVAddr(self: *Elf, decl: *const Module.Decl, reloc_info: File.RelocInfo) !u64 {
+pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: File.RelocInfo) !u64 {
+    const mod = self.base.options.module.?;
+    const decl = mod.declPtr(decl_index);
+
     assert(self.llvm_object == null);
     assert(decl.link.elf.local_sym_index != 0);
 
@@ -2189,15 +2192,17 @@ fn allocateLocalSymbol(self: *Elf) !u32 {
     return index;
 }
 
-pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void {
+pub fn allocateDeclIndexes(self: *Elf, decl_index: Module.Decl.Index) !void {
     if (self.llvm_object) |_| return;
 
+    const mod = self.base.options.module.?;
+    const decl = mod.declPtr(decl_index);
     if (decl.link.elf.local_sym_index != 0) return;
 
     try self.offset_table.ensureUnusedCapacity(self.base.allocator, 1);
-    try self.decls.putNoClobber(self.base.allocator, decl, null);
+    try self.decls.putNoClobber(self.base.allocator, decl_index, null);
 
-    const decl_name = try decl.getFullyQualifiedName(self.base.allocator);
+    const decl_name = try decl.getFullyQualifiedName(mod);
     defer self.base.allocator.free(decl_name);
 
     log.debug("allocating symbol indexes for {s}", .{decl_name});
@@ -2214,8 +2219,8 @@ pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void {
     self.offset_table.items[decl.link.elf.offset_table_index] = 0;
 }
 
-fn freeUnnamedConsts(self: *Elf, decl: *Module.Decl) void {
-    const unnamed_consts = self.unnamed_const_atoms.getPtr(decl) orelse return;
+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| {
         self.freeTextBlock(atom, self.phdr_load_ro_index.?);
         self.local_symbol_free_list.append(self.base.allocator, atom.local_sym_index) catch {};
@@ -2225,15 +2230,18 @@ fn freeUnnamedConsts(self: *Elf, decl: *Module.Decl) void {
     unnamed_consts.clearAndFree(self.base.allocator);
 }
 
-pub fn freeDecl(self: *Elf, decl: *Module.Decl) void {
+pub fn freeDecl(self: *Elf, decl_index: Module.Decl.Index) void {
     if (build_options.have_llvm) {
-        if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl);
+        if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index);
     }
 
-    const kv = self.decls.fetchRemove(decl);
+    const mod = self.base.options.module.?;
+    const decl = mod.declPtr(decl_index);
+
+    const kv = self.decls.fetchRemove(decl_index);
     if (kv.?.value) |index| {
         self.freeTextBlock(&decl.link.elf, index);
-        self.freeUnnamedConsts(decl);
+        self.freeUnnamedConsts(decl_index);
     }
 
     // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
@@ -2274,14 +2282,17 @@ fn getDeclPhdrIndex(self: *Elf, decl: *Module.Decl) !u16 {
     return phdr_index;
 }
 
-fn updateDeclCode(self: *Elf, decl: *Module.Decl, code: []const u8, stt_bits: u8) !*elf.Elf64_Sym {
-    const decl_name = try decl.getFullyQualifiedName(self.base.allocator);
+fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, stt_bits: u8) !*elf.Elf64_Sym {
+    const mod = self.base.options.module.?;
+    const decl = mod.declPtr(decl_index);
+
+    const decl_name = try decl.getFullyQualifiedName(mod);
     defer self.base.allocator.free(decl_name);
 
     log.debug("updateDeclCode {s}{*}", .{ decl_name, decl });
     const required_alignment = decl.ty.abiAlignment(self.base.options.target);
 
-    const decl_ptr = self.decls.getPtr(decl).?;
+    const decl_ptr = self.decls.getPtr(decl_index).?;
     if (decl_ptr.* == null) {
         decl_ptr.* = try self.getDeclPhdrIndex(decl);
     }
@@ -2355,10 +2366,11 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven
     var code_buffer = std.ArrayList(u8).init(self.base.allocator);
     defer code_buffer.deinit();
 
-    const decl = func.owner_decl;
-    self.freeUnnamedConsts(decl);
+    const decl_index = func.owner_decl;
+    const decl = module.declPtr(decl_index);
+    self.freeUnnamedConsts(decl_index);
 
-    var decl_state: ?Dwarf.DeclState = if (self.dwarf) |*dw| try dw.initDeclState(decl) else null;
+    var decl_state: ?Dwarf.DeclState = if (self.dwarf) |*dw| try dw.initDeclState(module, decl) else null;
     defer if (decl_state) |*ds| ds.deinit();
 
     const res = if (decl_state) |*ds|
@@ -2372,11 +2384,11 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven
         .appended => code_buffer.items,
         .fail => |em| {
             decl.analysis = .codegen_failure;
-            try module.failed_decls.put(module.gpa, decl, em);
+            try module.failed_decls.put(module.gpa, decl_index, em);
             return;
         },
     };
-    const local_sym = try self.updateDeclCode(decl, code, elf.STT_FUNC);
+    const local_sym = try self.updateDeclCode(decl_index, code, elf.STT_FUNC);
     if (decl_state) |*ds| {
         try self.dwarf.?.commitDeclState(
             &self.base,
@@ -2389,21 +2401,23 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven
     }
 
     // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
-    const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
-    return self.updateDeclExports(module, decl, decl_exports);
+    const decl_exports = module.decl_exports.get(decl_index) orelse &[0]*Module.Export{};
+    return self.updateDeclExports(module, decl_index, decl_exports);
 }
 
-pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
+pub fn updateDecl(self: *Elf, module: *Module, decl_index: Module.Decl.Index) !void {
     if (build_options.skip_non_native and builtin.object_format != .elf) {
         @panic("Attempted to compile for object format that was disabled by build configuration");
     }
     if (build_options.have_llvm) {
-        if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(module, decl);
+        if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(module, decl_index);
     }
 
     const tracy = trace(@src());
     defer tracy.end();
 
+    const decl = module.declPtr(decl_index);
+
     if (decl.val.tag() == .extern_fn) {
         return; // TODO Should we do more when front-end analyzed extern decl?
     }
@@ -2414,12 +2428,12 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
         }
     }
 
-    assert(!self.unnamed_const_atoms.contains(decl));
+    assert(!self.unnamed_const_atoms.contains(decl_index));
 
     var code_buffer = std.ArrayList(u8).init(self.base.allocator);
     defer code_buffer.deinit();
 
-    var decl_state: ?Dwarf.DeclState = if (self.dwarf) |*dw| try dw.initDeclState(decl) else null;
+    var decl_state: ?Dwarf.DeclState = if (self.dwarf) |*dw| try dw.initDeclState(module, decl) else null;
     defer if (decl_state) |*ds| ds.deinit();
 
     // TODO implement .debug_info for global variables
@@ -2446,12 +2460,12 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
         .appended => code_buffer.items,
         .fail => |em| {
             decl.analysis = .codegen_failure;
-            try module.failed_decls.put(module.gpa, decl, em);
+            try module.failed_decls.put(module.gpa, decl_index, em);
             return;
         },
     };
 
-    const local_sym = try self.updateDeclCode(decl, code, elf.STT_OBJECT);
+    const local_sym = try self.updateDeclCode(decl_index, code, elf.STT_OBJECT);
     if (decl_state) |*ds| {
         try self.dwarf.?.commitDeclState(
             &self.base,
@@ -2464,16 +2478,18 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
     }
 
     // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
-    const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
-    return self.updateDeclExports(module, decl, decl_exports);
+    const decl_exports = module.decl_exports.get(decl_index) orelse &[0]*Module.Export{};
+    return self.updateDeclExports(module, decl_index, decl_exports);
 }
 
-pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl: *Module.Decl) !u32 {
+pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module.Decl.Index) !u32 {
     var code_buffer = std.ArrayList(u8).init(self.base.allocator);
     defer code_buffer.deinit();
 
-    const module = self.base.options.module.?;
-    const gop = try self.unnamed_const_atoms.getOrPut(self.base.allocator, decl);
+    const mod = self.base.options.module.?;
+    const decl = mod.declPtr(decl_index);
+
+    const gop = try self.unnamed_const_atoms.getOrPut(self.base.allocator, decl_index);
     if (!gop.found_existing) {
         gop.value_ptr.* = .{};
     }
@@ -2485,7 +2501,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl: *Module.Decl
     try self.managed_atoms.append(self.base.allocator, atom);
 
     const name_str_index = blk: {
-        const decl_name = try decl.getFullyQualifiedName(self.base.allocator);
+        const decl_name = try decl.getFullyQualifiedName(mod);
         defer self.base.allocator.free(decl_name);
 
         const index = unnamed_consts.items.len;
@@ -2510,7 +2526,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl: *Module.Decl
         .appended => code_buffer.items,
         .fail => |em| {
             decl.analysis = .codegen_failure;
-            try module.failed_decls.put(module.gpa, decl, em);
+            try mod.failed_decls.put(mod.gpa, decl_index, em);
             log.err("{s}", .{em.msg});
             return error.AnalysisFail;
         },
@@ -2547,24 +2563,25 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl: *Module.Decl
 pub fn updateDeclExports(
     self: *Elf,
     module: *Module,
-    decl: *Module.Decl,
+    decl_index: Module.Decl.Index,
     exports: []const *Module.Export,
 ) !void {
     if (build_options.skip_non_native and builtin.object_format != .elf) {
         @panic("Attempted to compile for object format that was disabled by build configuration");
     }
     if (build_options.have_llvm) {
-        if (self.llvm_object) |llvm_object| return llvm_object.updateDeclExports(module, decl, exports);
+        if (self.llvm_object) |llvm_object| return llvm_object.updateDeclExports(module, decl_index, exports);
     }
 
     const tracy = trace(@src());
     defer tracy.end();
 
     try self.global_symbols.ensureUnusedCapacity(self.base.allocator, exports.len);
+    const decl = module.declPtr(decl_index);
     if (decl.link.elf.local_sym_index == 0) return;
     const decl_sym = self.local_symbols.items[decl.link.elf.local_sym_index];
 
-    const decl_ptr = self.decls.getPtr(decl).?;
+    const decl_ptr = self.decls.getPtr(decl_index).?;
     if (decl_ptr.* == null) {
         decl_ptr.* = try self.getDeclPhdrIndex(decl);
     }
@@ -2633,12 +2650,11 @@ pub fn updateDeclExports(
 }
 
 /// Must be called only after a successful call to `updateDecl`.
-pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Decl) !void {
-    _ = module;
+pub fn updateDeclLineNumber(self: *Elf, mod: *Module, decl: *const Module.Decl) !void {
     const tracy = trace(@src());
     defer tracy.end();
 
-    const decl_name = try decl.getFullyQualifiedName(self.base.allocator);
+    const decl_name = try decl.getFullyQualifiedName(mod);
     defer self.base.allocator.free(decl_name);
 
     log.debug("updateDeclLineNumber {s}{*}", .{ decl_name, decl });
src/link/MachO.zig
@@ -247,14 +247,14 @@ unnamed_const_atoms: UnnamedConstTable = .{},
 /// We store them here so that we can properly dispose of any allocated
 /// memory within the atom in the incremental linker.
 /// TODO consolidate this.
-decls: std.AutoArrayHashMapUnmanaged(*Module.Decl, ?MatchingSection) = .{},
+decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, ?MatchingSection) = .{},
 
 const Entry = struct {
     target: Atom.Relocation.Target,
     atom: *Atom,
 };
 
-const UnnamedConstTable = std.AutoHashMapUnmanaged(*Module.Decl, std.ArrayListUnmanaged(*Atom));
+const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(*Atom));
 
 const PendingUpdate = union(enum) {
     resolve_undef: u32,
@@ -3451,10 +3451,15 @@ pub fn deinit(self: *MachO) void {
         }
         self.atom_free_lists.deinit(self.base.allocator);
     }
-    for (self.decls.keys()) |decl| {
-        decl.link.macho.deinit(self.base.allocator);
+    if (self.base.options.module) |mod| {
+        for (self.decls.keys()) |decl_index| {
+            const decl = mod.declPtr(decl_index);
+            decl.link.macho.deinit(self.base.allocator);
+        }
+        self.decls.deinit(self.base.allocator);
+    } else {
+        assert(self.decls.count() == 0);
     }
-    self.decls.deinit(self.base.allocator);
 
     {
         var it = self.unnamed_const_atoms.valueIterator();
@@ -3652,13 +3657,14 @@ pub fn allocateTlvPtrEntry(self: *MachO, target: Atom.Relocation.Target) !u32 {
     return index;
 }
 
-pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {
+pub fn allocateDeclIndexes(self: *MachO, decl_index: Module.Decl.Index) !void {
     if (self.llvm_object) |_| return;
+    const decl = self.base.options.module.?.declPtr(decl_index);
     if (decl.link.macho.local_sym_index != 0) return;
 
     decl.link.macho.local_sym_index = try self.allocateLocalSymbol();
     try self.atom_by_index_table.putNoClobber(self.base.allocator, decl.link.macho.local_sym_index, &decl.link.macho);
-    try self.decls.putNoClobber(self.base.allocator, decl, null);
+    try self.decls.putNoClobber(self.base.allocator, decl_index, null);
 
     const got_target = .{ .local = decl.link.macho.local_sym_index };
     const got_index = try self.allocateGotEntry(got_target);
@@ -3676,8 +3682,9 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv
     const tracy = trace(@src());
     defer tracy.end();
 
-    const decl = func.owner_decl;
-    self.freeUnnamedConsts(decl);
+    const decl_index = func.owner_decl;
+    const decl = module.declPtr(decl_index);
+    self.freeUnnamedConsts(decl_index);
 
     // TODO clearing the code and relocs buffer should probably be orchestrated
     // in a different, smarter, more automatic way somewhere else, in a more centralised
@@ -3690,7 +3697,7 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv
     defer code_buffer.deinit();
 
     var decl_state = if (self.d_sym) |*d_sym|
-        try d_sym.dwarf.initDeclState(decl)
+        try d_sym.dwarf.initDeclState(module, decl)
     else
         null;
     defer if (decl_state) |*ds| ds.deinit();
@@ -3708,12 +3715,12 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv
         },
         .fail => |em| {
             decl.analysis = .codegen_failure;
-            try module.failed_decls.put(module.gpa, decl, em);
+            try module.failed_decls.put(module.gpa, decl_index, em);
             return;
         },
     }
 
-    const symbol = try self.placeDecl(decl, decl.link.macho.code.items.len);
+    const symbol = try self.placeDecl(decl_index, decl.link.macho.code.items.len);
 
     if (decl_state) |*ds| {
         try self.d_sym.?.dwarf.commitDeclState(
@@ -3728,22 +3735,23 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv
 
     // Since we updated the vaddr and the size, each corresponding export symbol also
     // needs to be updated.
-    const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
-    try self.updateDeclExports(module, decl, decl_exports);
+    const decl_exports = module.decl_exports.get(decl_index) orelse &[0]*Module.Export{};
+    try self.updateDeclExports(module, decl_index, decl_exports);
 }
 
-pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl: *Module.Decl) !u32 {
+pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Module.Decl.Index) !u32 {
     var code_buffer = std.ArrayList(u8).init(self.base.allocator);
     defer code_buffer.deinit();
 
     const module = self.base.options.module.?;
-    const gop = try self.unnamed_const_atoms.getOrPut(self.base.allocator, decl);
+    const gop = try self.unnamed_const_atoms.getOrPut(self.base.allocator, decl_index);
     if (!gop.found_existing) {
         gop.value_ptr.* = .{};
     }
     const unnamed_consts = gop.value_ptr;
 
-    const decl_name = try decl.getFullyQualifiedName(self.base.allocator);
+    const decl = module.declPtr(decl_index);
+    const decl_name = try decl.getFullyQualifiedName(module);
     defer self.base.allocator.free(decl_name);
 
     const name_str_index = blk: {
@@ -3769,7 +3777,7 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl: *Module.De
         .appended => code_buffer.items,
         .fail => |em| {
             decl.analysis = .codegen_failure;
-            try module.failed_decls.put(module.gpa, decl, em);
+            try module.failed_decls.put(module.gpa, decl_index, em);
             log.err("{s}", .{em.msg});
             return error.AnalysisFail;
         },
@@ -3800,16 +3808,18 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl: *Module.De
     return atom.local_sym_index;
 }
 
-pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
+pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index) !void {
     if (build_options.skip_non_native and builtin.object_format != .macho) {
         @panic("Attempted to compile for object format that was disabled by build configuration");
     }
     if (build_options.have_llvm) {
-        if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(module, decl);
+        if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(module, decl_index);
     }
     const tracy = trace(@src());
     defer tracy.end();
 
+    const decl = module.declPtr(decl_index);
+
     if (decl.val.tag() == .extern_fn) {
         return; // TODO Should we do more when front-end analyzed extern decl?
     }
@@ -3824,7 +3834,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
     defer code_buffer.deinit();
 
     var decl_state: ?Dwarf.DeclState = if (self.d_sym) |*d_sym|
-        try d_sym.dwarf.initDeclState(decl)
+        try d_sym.dwarf.initDeclState(module, decl)
     else
         null;
     defer if (decl_state) |*ds| ds.deinit();
@@ -3862,12 +3872,12 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
             },
             .fail => |em| {
                 decl.analysis = .codegen_failure;
-                try module.failed_decls.put(module.gpa, decl, em);
+                try module.failed_decls.put(module.gpa, decl_index, em);
                 return;
             },
         }
     };
-    const symbol = try self.placeDecl(decl, code.len);
+    const symbol = try self.placeDecl(decl_index, code.len);
 
     if (decl_state) |*ds| {
         try self.d_sym.?.dwarf.commitDeclState(
@@ -3882,13 +3892,13 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
 
     // Since we updated the vaddr and the size, each corresponding export symbol also
     // needs to be updated.
-    const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
-    try self.updateDeclExports(module, decl, decl_exports);
+    const decl_exports = module.decl_exports.get(decl_index) orelse &[0]*Module.Export{};
+    try self.updateDeclExports(module, decl_index, decl_exports);
 }
 
 /// Checks if the value, or any of its embedded values stores a pointer, and thus requires
 /// a rebase opcode for the dynamic linker.
-fn needsPointerRebase(ty: Type, val: Value, target: std.Target) bool {
+fn needsPointerRebase(ty: Type, val: Value, mod: *Module) bool {
     if (ty.zigTypeTag() == .Fn) {
         return false;
     }
@@ -3903,8 +3913,8 @@ fn needsPointerRebase(ty: Type, val: Value, target: std.Target) bool {
             if (ty.arrayLen() == 0) return false;
             const elem_ty = ty.childType();
             var elem_value_buf: Value.ElemValueBuffer = undefined;
-            const elem_val = val.elemValueBuffer(0, &elem_value_buf);
-            return needsPointerRebase(elem_ty, elem_val, target);
+            const elem_val = val.elemValueBuffer(mod, 0, &elem_value_buf);
+            return needsPointerRebase(elem_ty, elem_val, mod);
         },
         .Struct => {
             const fields = ty.structFields().values();
@@ -3912,7 +3922,7 @@ fn needsPointerRebase(ty: Type, val: Value, target: std.Target) bool {
             if (val.castTag(.aggregate)) |payload| {
                 const field_values = payload.data;
                 for (field_values) |field_val, i| {
-                    if (needsPointerRebase(fields[i].ty, field_val, target)) return true;
+                    if (needsPointerRebase(fields[i].ty, field_val, mod)) return true;
                 } else return false;
             } else return false;
         },
@@ -3921,18 +3931,18 @@ fn needsPointerRebase(ty: Type, val: Value, target: std.Target) bool {
                 const sub_val = payload.data;
                 var buffer: Type.Payload.ElemType = undefined;
                 const sub_ty = ty.optionalChild(&buffer);
-                return needsPointerRebase(sub_ty, sub_val, target);
+                return needsPointerRebase(sub_ty, sub_val, mod);
             } else return false;
         },
         .Union => {
             const union_obj = val.cast(Value.Payload.Union).?.data;
-            const active_field_ty = ty.unionFieldType(union_obj.tag, target);
-            return needsPointerRebase(active_field_ty, union_obj.val, target);
+            const active_field_ty = ty.unionFieldType(union_obj.tag, mod);
+            return needsPointerRebase(active_field_ty, union_obj.val, mod);
         },
         .ErrorUnion => {
             if (val.castTag(.eu_payload)) |payload| {
                 const payload_ty = ty.errorUnionPayload();
-                return needsPointerRebase(payload_ty, payload.data, target);
+                return needsPointerRebase(payload_ty, payload.data, mod);
             } else return false;
         },
         else => return false,
@@ -3942,6 +3952,7 @@ fn needsPointerRebase(ty: Type, val: Value, target: std.Target) bool {
 fn getMatchingSectionAtom(self: *MachO, atom: *Atom, name: []const u8, ty: Type, val: Value) !MatchingSection {
     const code = atom.code.items;
     const target = self.base.options.target;
+    const mod = self.base.options.module.?;
     const alignment = ty.abiAlignment(target);
     const align_log_2 = math.log2(alignment);
     const zig_ty = ty.zigTypeTag();
@@ -3969,7 +3980,7 @@ fn getMatchingSectionAtom(self: *MachO, atom: *Atom, name: []const u8, ty: Type,
             };
         }
 
-        if (needsPointerRebase(ty, val, target)) {
+        if (needsPointerRebase(ty, val, mod)) {
             break :blk (try self.getMatchingSection(.{
                 .segname = makeStaticString("__DATA_CONST"),
                 .sectname = makeStaticString("__const"),
@@ -4025,15 +4036,17 @@ fn getMatchingSectionAtom(self: *MachO, atom: *Atom, name: []const u8, ty: Type,
     return match;
 }
 
-fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64 {
+fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !*macho.nlist_64 {
+    const module = self.base.options.module.?;
+    const decl = module.declPtr(decl_index);
     const required_alignment = decl.ty.abiAlignment(self.base.options.target);
     assert(decl.link.macho.local_sym_index != 0); // Caller forgot to call allocateDeclIndexes()
     const symbol = &self.locals.items[decl.link.macho.local_sym_index];
 
-    const sym_name = try decl.getFullyQualifiedName(self.base.allocator);
+    const sym_name = try decl.getFullyQualifiedName(module);
     defer self.base.allocator.free(sym_name);
 
-    const decl_ptr = self.decls.getPtr(decl).?;
+    const decl_ptr = self.decls.getPtr(decl_index).?;
     if (decl_ptr.* == null) {
         decl_ptr.* = try self.getMatchingSectionAtom(&decl.link.macho, sym_name, decl.ty, decl.val);
     }
@@ -4101,19 +4114,20 @@ pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.D
 pub fn updateDeclExports(
     self: *MachO,
     module: *Module,
-    decl: *Module.Decl,
+    decl_index: Module.Decl.Index,
     exports: []const *Module.Export,
 ) !void {
     if (build_options.skip_non_native and builtin.object_format != .macho) {
         @panic("Attempted to compile for object format that was disabled by build configuration");
     }
     if (build_options.have_llvm) {
-        if (self.llvm_object) |llvm_object| return llvm_object.updateDeclExports(module, decl, exports);
+        if (self.llvm_object) |llvm_object| return llvm_object.updateDeclExports(module, decl_index, exports);
     }
     const tracy = trace(@src());
     defer tracy.end();
 
     try self.globals.ensureUnusedCapacity(self.base.allocator, exports.len);
+    const decl = module.declPtr(decl_index);
     if (decl.link.macho.local_sym_index == 0) return;
     const decl_sym = &self.locals.items[decl.link.macho.local_sym_index];
 
@@ -4250,9 +4264,8 @@ pub fn deleteExport(self: *MachO, exp: Export) void {
     global.n_value = 0;
 }
 
-fn freeUnnamedConsts(self: *MachO, decl: *Module.Decl) void {
-    log.debug("freeUnnamedConsts for decl {*}", .{decl});
-    const unnamed_consts = self.unnamed_const_atoms.getPtr(decl) orelse return;
+fn freeUnnamedConsts(self: *MachO, 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, .{
             .seg = self.text_segment_cmd_index.?,
@@ -4267,15 +4280,17 @@ fn freeUnnamedConsts(self: *MachO, decl: *Module.Decl) void {
     unnamed_consts.clearAndFree(self.base.allocator);
 }
 
-pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {
+pub fn freeDecl(self: *MachO, decl_index: Module.Decl.Index) void {
     if (build_options.have_llvm) {
-        if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl);
+        if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index);
     }
+    const mod = self.base.options.module.?;
+    const decl = mod.declPtr(decl_index);
     log.debug("freeDecl {*}", .{decl});
-    const kv = self.decls.fetchSwapRemove(decl);
+    const kv = self.decls.fetchSwapRemove(decl_index);
     if (kv.?.value) |match| {
         self.freeAtom(&decl.link.macho, match, false);
-        self.freeUnnamedConsts(decl);
+        self.freeUnnamedConsts(decl_index);
     }
     // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
     if (decl.link.macho.local_sym_index != 0) {
@@ -4307,7 +4322,10 @@ pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {
     }
 }
 
-pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl, reloc_info: File.RelocInfo) !u64 {
+pub fn getDeclVAddr(self: *MachO, decl_index: Module.Decl.Index, reloc_info: File.RelocInfo) !u64 {
+    const mod = self.base.options.module.?;
+    const decl = mod.declPtr(decl_index);
+
     assert(self.llvm_object == null);
     assert(decl.link.macho.local_sym_index != 0);
 
src/link/NvPtx.zig
@@ -74,27 +74,27 @@ pub fn updateFunc(self: *NvPtx, module: *Module, func: *Module.Fn, air: Air, liv
     try self.llvm_object.updateFunc(module, func, air, liveness);
 }
 
-pub fn updateDecl(self: *NvPtx, module: *Module, decl: *Module.Decl) !void {
+pub fn updateDecl(self: *NvPtx, module: *Module, decl_index: Module.Decl.Index) !void {
     if (!build_options.have_llvm) return;
-    return self.llvm_object.updateDecl(module, decl);
+    return self.llvm_object.updateDecl(module, decl_index);
 }
 
 pub fn updateDeclExports(
     self: *NvPtx,
     module: *Module,
-    decl: *const Module.Decl,
+    decl_index: Module.Decl.Index,
     exports: []const *Module.Export,
 ) !void {
     if (!build_options.have_llvm) return;
     if (build_options.skip_non_native and builtin.object_format != .nvptx) {
         @panic("Attempted to compile for object format that was disabled by build configuration");
     }
-    return self.llvm_object.updateDeclExports(module, decl, exports);
+    return self.llvm_object.updateDeclExports(module, decl_index, exports);
 }
 
-pub fn freeDecl(self: *NvPtx, decl: *Module.Decl) void {
+pub fn freeDecl(self: *NvPtx, decl_index: Module.Decl.Index) void {
     if (!build_options.have_llvm) return;
-    return self.llvm_object.freeDecl(decl);
+    return self.llvm_object.freeDecl(decl_index);
 }
 
 pub fn flush(self: *NvPtx, comp: *Compilation, prog_node: *std.Progress.Node) !void {
src/link/Plan9.zig
@@ -59,9 +59,9 @@ path_arena: std.heap.ArenaAllocator,
 /// If we group the decls by file, it makes it really easy to do this (put the symbol in the correct place)
 fn_decl_table: std.AutoArrayHashMapUnmanaged(
     *Module.File,
-    struct { sym_index: u32, functions: std.AutoArrayHashMapUnmanaged(*Module.Decl, FnDeclOutput) = .{} },
+    struct { sym_index: u32, functions: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, FnDeclOutput) = .{} },
 ) = .{},
-data_decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, []const u8) = .{},
+data_decl_table: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, []const u8) = .{},
 
 hdr: aout.ExecHdr = undefined,
 
@@ -162,11 +162,13 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Plan9 {
     return self;
 }
 
-fn putFn(self: *Plan9, decl: *Module.Decl, out: FnDeclOutput) !void {
+fn putFn(self: *Plan9, decl_index: Module.Decl.Index, out: FnDeclOutput) !void {
     const gpa = self.base.allocator;
+    const mod = self.base.options.module.?;
+    const decl = mod.declPtr(decl_index);
     const fn_map_res = try self.fn_decl_table.getOrPut(gpa, decl.getFileScope());
     if (fn_map_res.found_existing) {
-        try fn_map_res.value_ptr.functions.put(gpa, decl, out);
+        try fn_map_res.value_ptr.functions.put(gpa, decl_index, out);
     } else {
         const file = decl.getFileScope();
         const arena = self.path_arena.allocator();
@@ -178,7 +180,7 @@ fn putFn(self: *Plan9, decl: *Module.Decl, out: FnDeclOutput) !void {
                 break :blk @intCast(u32, self.syms.items.len - 1);
             },
         };
-        try fn_map_res.value_ptr.functions.put(gpa, decl, out);
+        try fn_map_res.value_ptr.functions.put(gpa, decl_index, out);
 
         var a = std.ArrayList(u8).init(arena);
         errdefer a.deinit();
@@ -229,9 +231,10 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv
         @panic("Attempted to compile for object format that was disabled by build configuration");
     }
 
-    const decl = func.owner_decl;
+    const decl_index = func.owner_decl;
+    const decl = module.declPtr(decl_index);
 
-    try self.seeDecl(decl);
+    try self.seeDecl(decl_index);
     log.debug("codegen decl {*} ({s})", .{ decl, decl.name });
 
     var code_buffer = std.ArrayList(u8).init(self.base.allocator);
@@ -262,7 +265,7 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv
         .appended => code_buffer.toOwnedSlice(),
         .fail => |em| {
             decl.analysis = .codegen_failure;
-            try module.failed_decls.put(module.gpa, decl, em);
+            try module.failed_decls.put(module.gpa, decl_index, em);
             return;
         },
     };
@@ -272,19 +275,21 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv
         .start_line = start_line.?,
         .end_line = end_line,
     };
-    try self.putFn(decl, out);
+    try self.putFn(decl_index, out);
     return self.updateFinish(decl);
 }
 
-pub fn lowerUnnamedConst(self: *Plan9, tv: TypedValue, decl: *Module.Decl) !u32 {
+pub fn lowerUnnamedConst(self: *Plan9, tv: TypedValue, decl_index: Module.Decl.Index) !u32 {
     _ = self;
     _ = tv;
-    _ = decl;
+    _ = decl_index;
     log.debug("TODO lowerUnnamedConst for Plan9", .{});
     return error.AnalysisFail;
 }
 
-pub fn updateDecl(self: *Plan9, module: *Module, decl: *Module.Decl) !void {
+pub fn updateDecl(self: *Plan9, module: *Module, decl_index: Module.Decl.Index) !void {
+    const decl = module.declPtr(decl_index);
+
     if (decl.val.tag() == .extern_fn) {
         return; // TODO Should we do more when front-end analyzed extern decl?
     }
@@ -295,7 +300,7 @@ pub fn updateDecl(self: *Plan9, module: *Module, decl: *Module.Decl) !void {
         }
     }
 
-    try self.seeDecl(decl);
+    try self.seeDecl(decl_index);
 
     log.debug("codegen decl {*} ({s})", .{ decl, decl.name });
 
@@ -315,13 +320,13 @@ pub fn updateDecl(self: *Plan9, module: *Module, decl: *Module.Decl) !void {
         .appended => code_buffer.items,
         .fail => |em| {
             decl.analysis = .codegen_failure;
-            try module.failed_decls.put(module.gpa, decl, em);
+            try module.failed_decls.put(module.gpa, decl_index, em);
             return;
         },
     };
     var duped_code = try self.base.allocator.dupe(u8, code);
     errdefer self.base.allocator.free(duped_code);
-    try self.data_decl_table.put(self.base.allocator, decl, duped_code);
+    try self.data_decl_table.put(self.base.allocator, decl_index, duped_code);
     return self.updateFinish(decl);
 }
 /// called at the end of update{Decl,Func}
@@ -435,7 +440,8 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
         while (it_file.next()) |fentry| {
             var it = fentry.value_ptr.functions.iterator();
             while (it.next()) |entry| {
-                const decl = entry.key_ptr.*;
+                const decl_index = entry.key_ptr.*;
+                const decl = mod.declPtr(decl_index);
                 const out = entry.value_ptr.*;
                 log.debug("write text decl {*} ({s}), lines {d} to {d}", .{ decl, decl.name, out.start_line + 1, out.end_line });
                 {
@@ -462,7 +468,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
                     mem.writeInt(u64, got_table[decl.link.plan9.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian());
                 }
                 self.syms.items[decl.link.plan9.sym_index.?].value = off;
-                if (mod.decl_exports.get(decl)) |exports| {
+                if (mod.decl_exports.get(decl_index)) |exports| {
                     try self.addDeclExports(mod, decl, exports);
                 }
             }
@@ -482,7 +488,8 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
     {
         var it = self.data_decl_table.iterator();
         while (it.next()) |entry| {
-            const decl = entry.key_ptr.*;
+            const decl_index = entry.key_ptr.*;
+            const decl = mod.declPtr(decl_index);
             const code = entry.value_ptr.*;
             log.debug("write data decl {*} ({s})", .{ decl, decl.name });
 
@@ -498,7 +505,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
                 mem.writeInt(u64, got_table[decl.link.plan9.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian());
             }
             self.syms.items[decl.link.plan9.sym_index.?].value = off;
-            if (mod.decl_exports.get(decl)) |exports| {
+            if (mod.decl_exports.get(decl_index)) |exports| {
                 try self.addDeclExports(mod, decl, exports);
             }
         }
@@ -564,24 +571,25 @@ fn addDeclExports(
     }
 }
 
-pub fn freeDecl(self: *Plan9, decl: *Module.Decl) void {
+pub fn freeDecl(self: *Plan9, decl_index: Module.Decl.Index) void {
     // TODO audit the lifetimes of decls table entries. It's possible to get
     // allocateDeclIndexes and then freeDecl without any updateDecl in between.
     // However that is planned to change, see the TODO comment in Module.zig
     // in the deleteUnusedDecl function.
+    const mod = self.base.options.module.?;
+    const decl = mod.declPtr(decl_index);
     const is_fn = (decl.val.tag() == .function);
     if (is_fn) {
-        var symidx_and_submap =
-            self.fn_decl_table.get(decl.getFileScope()).?;
+        var symidx_and_submap = self.fn_decl_table.get(decl.getFileScope()).?;
         var submap = symidx_and_submap.functions;
-        _ = submap.swapRemove(decl);
+        _ = submap.swapRemove(decl_index);
         if (submap.count() == 0) {
             self.syms.items[symidx_and_submap.sym_index] = aout.Sym.undefined_symbol;
             self.syms_index_free_list.append(self.base.allocator, symidx_and_submap.sym_index) catch {};
             submap.deinit(self.base.allocator);
         }
     } else {
-        _ = self.data_decl_table.swapRemove(decl);
+        _ = self.data_decl_table.swapRemove(decl_index);
     }
     if (decl.link.plan9.got_index) |i| {
         // TODO: if this catch {} is triggered, an assertion in flushModule will be triggered, because got_index_free_list will have the wrong length
@@ -593,7 +601,9 @@ pub fn freeDecl(self: *Plan9, decl: *Module.Decl) void {
     }
 }
 
-pub fn seeDecl(self: *Plan9, decl: *Module.Decl) !void {
+pub fn seeDecl(self: *Plan9, decl_index: Module.Decl.Index) !void {
+    const mod = self.base.options.module.?;
+    const decl = mod.declPtr(decl_index);
     if (decl.link.plan9.got_index == null) {
         if (self.got_index_free_list.popOrNull()) |i| {
             decl.link.plan9.got_index = i;
@@ -607,14 +617,13 @@ pub fn seeDecl(self: *Plan9, decl: *Module.Decl) !void {
 pub fn updateDeclExports(
     self: *Plan9,
     module: *Module,
-    decl: *Module.Decl,
+    decl_index: Module.Decl.Index,
     exports: []const *Module.Export,
 ) !void {
-    try self.seeDecl(decl);
+    try self.seeDecl(decl_index);
     // we do all the things in flush
     _ = self;
     _ = module;
-    _ = decl;
     _ = exports;
 }
 pub fn deinit(self: *Plan9) void {
@@ -709,14 +718,18 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {
             });
         }
     }
+
+    const mod = self.base.options.module.?;
+
     // write the data symbols
     {
         var it = self.data_decl_table.iterator();
         while (it.next()) |entry| {
-            const decl = entry.key_ptr.*;
+            const decl_index = entry.key_ptr.*;
+            const decl = mod.declPtr(decl_index);
             const sym = self.syms.items[decl.link.plan9.sym_index.?];
             try self.writeSym(writer, sym);
-            if (self.base.options.module.?.decl_exports.get(decl)) |exports| {
+            if (self.base.options.module.?.decl_exports.get(decl_index)) |exports| {
                 for (exports) |e| {
                     try self.writeSym(writer, self.syms.items[e.link.plan9.?]);
                 }
@@ -737,10 +750,11 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {
             // write all the decls come from the file of the z symbol
             var submap_it = symidx_and_submap.functions.iterator();
             while (submap_it.next()) |entry| {
-                const decl = entry.key_ptr.*;
+                const decl_index = entry.key_ptr.*;
+                const decl = mod.declPtr(decl_index);
                 const sym = self.syms.items[decl.link.plan9.sym_index.?];
                 try self.writeSym(writer, sym);
-                if (self.base.options.module.?.decl_exports.get(decl)) |exports| {
+                if (self.base.options.module.?.decl_exports.get(decl_index)) |exports| {
                     for (exports) |e| {
                         const s = self.syms.items[e.link.plan9.?];
                         if (mem.eql(u8, s.name, "_start"))
@@ -754,12 +768,18 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {
 }
 
 /// this will be removed, moved to updateFinish
-pub fn allocateDeclIndexes(self: *Plan9, decl: *Module.Decl) !void {
+pub fn allocateDeclIndexes(self: *Plan9, decl_index: Module.Decl.Index) !void {
     _ = self;
-    _ = decl;
+    _ = decl_index;
 }
-pub fn getDeclVAddr(self: *Plan9, decl: *const Module.Decl, reloc_info: link.File.RelocInfo) !u64 {
+pub fn getDeclVAddr(
+    self: *Plan9,
+    decl_index: Module.Decl.Index,
+    reloc_info: link.File.RelocInfo,
+) !u64 {
     _ = reloc_info;
+    const mod = self.base.options.module.?;
+    const decl = mod.declPtr(decl_index);
     if (decl.ty.zigTypeTag() == .Fn) {
         var start = self.bases.text;
         var it_file = self.fn_decl_table.iterator();
@@ -767,7 +787,7 @@ pub fn getDeclVAddr(self: *Plan9, decl: *const Module.Decl, reloc_info: link.Fil
             var symidx_and_submap = fentry.value_ptr;
             var submap_it = symidx_and_submap.functions.iterator();
             while (submap_it.next()) |entry| {
-                if (entry.key_ptr.* == decl) return start;
+                if (entry.key_ptr.* == decl_index) return start;
                 start += entry.value_ptr.code.len;
             }
         }
@@ -776,7 +796,7 @@ pub fn getDeclVAddr(self: *Plan9, decl: *const Module.Decl, reloc_info: link.Fil
         var start = self.bases.data + self.got_len * if (!self.sixtyfour_bit) @as(u32, 4) else 8;
         var it = self.data_decl_table.iterator();
         while (it.next()) |kv| {
-            if (decl == kv.key_ptr.*) return start;
+            if (decl_index == kv.key_ptr.*) return start;
             start += kv.value_ptr.len;
         }
         unreachable;
src/link/SpirV.zig
@@ -54,7 +54,7 @@ base: link.File,
 /// This linker backend does not try to incrementally link output SPIR-V code.
 /// Instead, it tracks all declarations in this table, and iterates over it
 /// in the flush function.
-decl_table: std.AutoArrayHashMapUnmanaged(*Module.Decl, DeclGenContext) = .{},
+decl_table: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, DeclGenContext) = .{},
 
 const DeclGenContext = struct {
     air: Air,
@@ -145,29 +145,31 @@ pub fn updateFunc(self: *SpirV, module: *Module, func: *Module.Fn, air: Air, liv
     };
 }
 
-pub fn updateDecl(self: *SpirV, module: *Module, decl: *Module.Decl) !void {
+pub fn updateDecl(self: *SpirV, module: *Module, decl_index: Module.Decl.Index) !void {
     if (build_options.skip_non_native) {
         @panic("Attempted to compile for architecture that was disabled by build configuration");
     }
     _ = module;
     // Keep track of all decls so we can iterate over them on flush().
-    _ = try self.decl_table.getOrPut(self.base.allocator, decl);
+    _ = try self.decl_table.getOrPut(self.base.allocator, decl_index);
 }
 
 pub fn updateDeclExports(
     self: *SpirV,
     module: *Module,
-    decl: *const Module.Decl,
+    decl_index: Module.Decl.Index,
     exports: []const *Module.Export,
 ) !void {
     _ = self;
     _ = module;
-    _ = decl;
+    _ = decl_index;
     _ = exports;
 }
 
-pub fn freeDecl(self: *SpirV, decl: *Module.Decl) void {
-    const index = self.decl_table.getIndex(decl).?;
+pub fn freeDecl(self: *SpirV, decl_index: Module.Decl.Index) void {
+    const index = self.decl_table.getIndex(decl_index).?;
+    const module = self.base.options.module.?;
+    const decl = module.declPtr(decl_index);
     if (decl.val.tag() == .function) {
         self.decl_table.values()[index].deinit(self.base.allocator);
     }
@@ -208,7 +210,8 @@ pub fn flushModule(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.No
     // TODO: We're allocating an ID unconditionally now, are there
     // declarations which don't generate a result?
     // TODO: fn_link is used here, but thats probably not the right field. It will work anyway though.
-    for (self.decl_table.keys()) |decl| {
+    for (self.decl_table.keys()) |decl_index| {
+        const decl = module.declPtr(decl_index);
         if (decl.has_tv) {
             decl.fn_link.spirv.id = spv.allocId();
         }
@@ -220,7 +223,8 @@ pub fn flushModule(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.No
 
     var it = self.decl_table.iterator();
     while (it.next()) |entry| {
-        const decl = entry.key_ptr.*;
+        const decl_index = entry.key_ptr.*;
+        const decl = module.declPtr(decl_index);
         if (!decl.has_tv) continue;
 
         const air = entry.value_ptr.air;
@@ -228,7 +232,7 @@ pub fn flushModule(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.No
 
         // Note, if `decl` is not a function, air/liveness may be undefined.
         if (try decl_gen.gen(decl, air, liveness)) |msg| {
-            try module.failed_decls.put(module.gpa, decl, msg);
+            try module.failed_decls.put(module.gpa, decl_index, msg);
             return; // TODO: Attempt to generate more decls?
         }
     }
src/link/Wasm.zig
@@ -48,7 +48,7 @@ host_name: []const u8 = "env",
 /// List of all `Decl` that are currently alive.
 /// This is ment for bookkeeping so we can safely cleanup all codegen memory
 /// when calling `deinit`
-decls: std.AutoHashMapUnmanaged(*Module.Decl, void) = .{},
+decls: std.AutoHashMapUnmanaged(Module.Decl.Index, void) = .{},
 /// List of all symbols generated by Zig code.
 symbols: std.ArrayListUnmanaged(Symbol) = .{},
 /// List of symbol indexes which are free to be used.
@@ -429,9 +429,11 @@ pub fn deinit(self: *Wasm) void {
         if (self.llvm_object) |llvm_object| llvm_object.destroy(gpa);
     }
 
+    const mod = self.base.options.module.?;
     var decl_it = self.decls.keyIterator();
-    while (decl_it.next()) |decl_ptr| {
-        decl_ptr.*.link.wasm.deinit(gpa);
+    while (decl_it.next()) |decl_index_ptr| {
+        const decl = mod.declPtr(decl_index_ptr.*);
+        decl.link.wasm.deinit(gpa);
     }
 
     for (self.func_types.items) |*func_type| {
@@ -476,12 +478,13 @@ pub fn deinit(self: *Wasm) void {
     self.string_table.deinit(gpa);
 }
 
-pub fn allocateDeclIndexes(self: *Wasm, decl: *Module.Decl) !void {
+pub fn allocateDeclIndexes(self: *Wasm, decl_index: Module.Decl.Index) !void {
     if (self.llvm_object) |_| return;
+    const decl = self.base.options.module.?.declPtr(decl_index);
     if (decl.link.wasm.sym_index != 0) return;
 
     try self.symbols.ensureUnusedCapacity(self.base.allocator, 1);
-    try self.decls.putNoClobber(self.base.allocator, decl, {});
+    try self.decls.putNoClobber(self.base.allocator, decl_index, {});
 
     const atom = &decl.link.wasm;
 
@@ -502,14 +505,15 @@ pub fn allocateDeclIndexes(self: *Wasm, decl: *Module.Decl) !void {
     try self.symbol_atom.putNoClobber(self.base.allocator, atom.symbolLoc(), atom);
 }
 
-pub fn updateFunc(self: *Wasm, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
+pub fn updateFunc(self: *Wasm, mod: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
     if (build_options.skip_non_native and builtin.object_format != .wasm) {
         @panic("Attempted to compile for object format that was disabled by build configuration");
     }
     if (build_options.have_llvm) {
-        if (self.llvm_object) |llvm_object| return llvm_object.updateFunc(module, func, air, liveness);
+        if (self.llvm_object) |llvm_object| return llvm_object.updateFunc(mod, func, air, liveness);
     }
-    const decl = func.owner_decl;
+    const decl_index = func.owner_decl;
+    const decl = mod.declPtr(decl_index);
     assert(decl.link.wasm.sym_index != 0); // Must call allocateDeclIndexes()
 
     decl.link.wasm.clear();
@@ -530,7 +534,7 @@ pub fn updateFunc(self: *Wasm, module: *Module, func: *Module.Fn, air: Air, live
         .appended => code_writer.items,
         .fail => |em| {
             decl.analysis = .codegen_failure;
-            try module.failed_decls.put(module.gpa, decl, em);
+            try mod.failed_decls.put(mod.gpa, decl_index, em);
             return;
         },
     };
@@ -540,14 +544,15 @@ pub fn updateFunc(self: *Wasm, module: *Module, func: *Module.Fn, air: Air, live
 
 // Generate code for the Decl, storing it in memory to be later written to
 // the file on flush().
-pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {
+pub fn updateDecl(self: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !void {
     if (build_options.skip_non_native and builtin.object_format != .wasm) {
         @panic("Attempted to compile for object format that was disabled by build configuration");
     }
     if (build_options.have_llvm) {
-        if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(module, decl);
+        if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(mod, decl_index);
     }
 
+    const decl = mod.declPtr(decl_index);
     assert(decl.link.wasm.sym_index != 0); // Must call allocateDeclIndexes()
 
     decl.link.wasm.clear();
@@ -580,7 +585,7 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {
         .appended => code_writer.items,
         .fail => |em| {
             decl.analysis = .codegen_failure;
-            try module.failed_decls.put(module.gpa, decl, em);
+            try mod.failed_decls.put(mod.gpa, decl_index, em);
             return;
         },
     };
@@ -590,12 +595,13 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void {
 
 fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, code: []const u8) !void {
     if (code.len == 0) return;
+    const mod = self.base.options.module.?;
     const atom: *Atom = &decl.link.wasm;
     atom.size = @intCast(u32, code.len);
     atom.alignment = decl.ty.abiAlignment(self.base.options.target);
     const symbol = &self.symbols.items[atom.sym_index];
 
-    const full_name = try decl.getFullyQualifiedName(self.base.allocator);
+    const full_name = try decl.getFullyQualifiedName(mod);
     defer self.base.allocator.free(full_name);
     symbol.name = try self.string_table.put(self.base.allocator, full_name);
     try atom.code.appendSlice(self.base.allocator, code);
@@ -606,12 +612,15 @@ fn finishUpdateDecl(self: *Wasm, decl: *Module.Decl, code: []const u8) !void {
 /// Lowers a constant typed value to a local symbol and atom.
 /// Returns the symbol index of the local
 /// The given `decl` is the parent decl whom owns the constant.
-pub fn lowerUnnamedConst(self: *Wasm, decl: *Module.Decl, tv: TypedValue) !u32 {
+pub fn lowerUnnamedConst(self: *Wasm, tv: TypedValue, decl_index: Module.Decl.Index) !u32 {
     assert(tv.ty.zigTypeTag() != .Fn); // cannot create local symbols for functions
 
+    const mod = self.base.options.module.?;
+    const decl = mod.declPtr(decl_index);
+
     // Create and initialize a new local symbol and atom
     const local_index = decl.link.wasm.locals.items.len;
-    const fqdn = try decl.getFullyQualifiedName(self.base.allocator);
+    const fqdn = try decl.getFullyQualifiedName(mod);
     defer self.base.allocator.free(fqdn);
     const name = try std.fmt.allocPrintZ(self.base.allocator, "__unnamed_{s}_{d}", .{ fqdn, local_index });
     defer self.base.allocator.free(name);
@@ -641,7 +650,6 @@ pub fn lowerUnnamedConst(self: *Wasm, decl: *Module.Decl, tv: TypedValue) !u32 {
     var value_bytes = std.ArrayList(u8).init(self.base.allocator);
     defer value_bytes.deinit();
 
-    const module = self.base.options.module.?;
     const result = try codegen.generateSymbol(
         &self.base,
         decl.srcLoc(),
@@ -658,7 +666,7 @@ pub fn lowerUnnamedConst(self: *Wasm, decl: *Module.Decl, tv: TypedValue) !u32 {
         .appended => value_bytes.items,
         .fail => |em| {
             decl.analysis = .codegen_failure;
-            try module.failed_decls.put(module.gpa, decl, em);
+            try mod.failed_decls.put(mod.gpa, decl_index, em);
             return error.AnalysisFail;
         },
     };
@@ -672,9 +680,11 @@ pub fn lowerUnnamedConst(self: *Wasm, decl: *Module.Decl, tv: TypedValue) !u32 {
 /// Returns the given pointer address
 pub fn getDeclVAddr(
     self: *Wasm,
-    decl: *const Module.Decl,
+    decl_index: Module.Decl.Index,
     reloc_info: link.File.RelocInfo,
 ) !u64 {
+    const mod = self.base.options.module.?;
+    const decl = mod.declPtr(decl_index);
     const target_symbol_index = decl.link.wasm.sym_index;
     assert(target_symbol_index != 0);
     assert(reloc_info.parent_atom_index != 0);
@@ -722,21 +732,23 @@ pub fn deleteExport(self: *Wasm, exp: Export) void {
 
 pub fn updateDeclExports(
     self: *Wasm,
-    module: *Module,
-    decl: *const Module.Decl,
+    mod: *Module,
+    decl_index: Module.Decl.Index,
     exports: []const *Module.Export,
 ) !void {
     if (build_options.skip_non_native and builtin.object_format != .wasm) {
         @panic("Attempted to compile for object format that was disabled by build configuration");
     }
     if (build_options.have_llvm) {
-        if (self.llvm_object) |llvm_object| return llvm_object.updateDeclExports(module, decl, exports);
+        if (self.llvm_object) |llvm_object| return llvm_object.updateDeclExports(mod, decl_index, exports);
     }
 
+    const decl = mod.declPtr(decl_index);
+
     for (exports) |exp| {
         if (exp.options.section) |section| {
-            try module.failed_exports.putNoClobber(module.gpa, exp, try Module.ErrorMsg.create(
-                module.gpa,
+            try mod.failed_exports.putNoClobber(mod.gpa, exp, try Module.ErrorMsg.create(
+                mod.gpa,
                 decl.srcLoc(),
                 "Unimplemented: ExportOptions.section '{s}'",
                 .{section},
@@ -754,8 +766,8 @@ pub fn updateDeclExports(
             // are strong symbols, we have a linker error.
             // In the other case we replace one with the other.
             if (!exp_is_weak and !existing_sym.isWeak()) {
-                try module.failed_exports.put(module.gpa, exp, try Module.ErrorMsg.create(
-                    module.gpa,
+                try mod.failed_exports.put(mod.gpa, exp, try Module.ErrorMsg.create(
+                    mod.gpa,
                     decl.srcLoc(),
                     \\LinkError: symbol '{s}' defined multiple times
                     \\  first definition in '{s}'
@@ -773,8 +785,9 @@ pub fn updateDeclExports(
             }
         }
 
-        const sym_index = exp.exported_decl.link.wasm.sym_index;
-        const sym_loc = exp.exported_decl.link.wasm.symbolLoc();
+        const exported_decl = mod.declPtr(exp.exported_decl);
+        const sym_index = exported_decl.link.wasm.sym_index;
+        const sym_loc = exported_decl.link.wasm.symbolLoc();
         const symbol = sym_loc.getSymbol(self);
         switch (exp.options.linkage) {
             .Internal => {
@@ -786,8 +799,8 @@ pub fn updateDeclExports(
             },
             .Strong => {}, // symbols are strong by default
             .LinkOnce => {
-                try module.failed_exports.putNoClobber(module.gpa, exp, try Module.ErrorMsg.create(
-                    module.gpa,
+                try mod.failed_exports.putNoClobber(mod.gpa, exp, try Module.ErrorMsg.create(
+                    mod.gpa,
                     decl.srcLoc(),
                     "Unimplemented: LinkOnce",
                     .{},
@@ -813,13 +826,15 @@ pub fn updateDeclExports(
     }
 }
 
-pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void {
+pub fn freeDecl(self: *Wasm, decl_index: Module.Decl.Index) void {
     if (build_options.have_llvm) {
-        if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl);
+        if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index);
     }
+    const mod = self.base.options.module.?;
+    const decl = mod.declPtr(decl_index);
     const atom = &decl.link.wasm;
     self.symbols_free_list.append(self.base.allocator, atom.sym_index) catch {};
-    _ = self.decls.remove(decl);
+    _ = self.decls.remove(decl_index);
     self.symbols.items[atom.sym_index].tag = .dead;
     for (atom.locals.items) |local_atom| {
         const local_symbol = &self.symbols.items[local_atom.sym_index];
@@ -1414,8 +1429,8 @@ fn populateErrorNameTable(self: *Wasm) !void {
 
     // Addend for each relocation to the table
     var addend: u32 = 0;
-    const module = self.base.options.module.?;
-    for (module.error_name_list.items) |error_name| {
+    const mod = self.base.options.module.?;
+    for (mod.error_name_list.items) |error_name| {
         const len = @intCast(u32, error_name.len + 1); // names are 0-termianted
 
         const slice_ty = Type.initTag(.const_slice_u8_sentinel_0);
@@ -1456,9 +1471,11 @@ fn resetState(self: *Wasm) void {
     for (self.segment_info.items) |*segment_info| {
         self.base.allocator.free(segment_info.name);
     }
+    const mod = self.base.options.module.?;
     var decl_it = self.decls.keyIterator();
-    while (decl_it.next()) |decl| {
-        const atom = &decl.*.link.wasm;
+    while (decl_it.next()) |decl_index_ptr| {
+        const decl = mod.declPtr(decl_index_ptr.*);
+        const atom = &decl.link.wasm;
         atom.next = null;
         atom.prev = null;
 
@@ -1546,12 +1563,14 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
     defer self.resetState();
     try self.setupStart();
     try self.setupImports();
+    const mod = self.base.options.module.?;
     var decl_it = self.decls.keyIterator();
-    while (decl_it.next()) |decl| {
-        if (decl.*.isExtern()) continue;
+    while (decl_it.next()) |decl_index_ptr| {
+        const decl = mod.declPtr(decl_index_ptr.*);
+        if (decl.isExtern()) continue;
         const atom = &decl.*.link.wasm;
-        if (decl.*.ty.zigTypeTag() == .Fn) {
-            try self.parseAtom(atom, .{ .function = decl.*.fn_link.wasm });
+        if (decl.ty.zigTypeTag() == .Fn) {
+            try self.parseAtom(atom, .{ .function = decl.fn_link.wasm });
         } else {
             try self.parseAtom(atom, .data);
         }
@@ -2045,7 +2064,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
 
     // If there is no Zig code to compile, then we should skip flushing the output file because it
     // will not be part of the linker line anyway.
-    const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: {
+    const module_obj_path: ?[]const u8 = if (self.base.options.module) |mod| blk: {
         const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1;
         if (use_stage1) {
             const obj_basename = try std.zig.binNameAlloc(arena, .{
@@ -2054,7 +2073,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
                 .output_mode = .Obj,
             });
             switch (self.base.options.cache_mode) {
-                .incremental => break :blk try module.zig_cache_artifact_directory.join(
+                .incremental => break :blk try mod.zig_cache_artifact_directory.join(
                     arena,
                     &[_][]const u8{obj_basename},
                 ),
@@ -2253,7 +2272,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
         }
 
         if (auto_export_symbols) {
-            if (self.base.options.module) |module| {
+            if (self.base.options.module) |mod| {
                 // when we use stage1, we use the exports that stage1 provided us.
                 // For stage2, we can directly retrieve them from the module.
                 const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1;
@@ -2264,14 +2283,15 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
                 } else {
                     const skip_export_non_fn = target.os.tag == .wasi and
                         self.base.options.wasi_exec_model == .command;
-                    for (module.decl_exports.values()) |exports| {
+                    for (mod.decl_exports.values()) |exports| {
                         for (exports) |exprt| {
-                            if (skip_export_non_fn and exprt.exported_decl.ty.zigTypeTag() != .Fn) {
+                            const exported_decl = mod.declPtr(exprt.exported_decl);
+                            if (skip_export_non_fn and exported_decl.ty.zigTypeTag() != .Fn) {
                                 // skip exporting symbols when we're building a WASI command
                                 // and the symbol is not a function
                                 continue;
                             }
-                            const symbol_name = exprt.exported_decl.name;
+                            const symbol_name = exported_decl.name;
                             const arg = try std.fmt.allocPrint(arena, "--export={s}", .{symbol_name});
                             try argv.append(arg);
                         }
src/codegen.zig
@@ -347,7 +347,9 @@ pub fn generateSymbol(
 
                 switch (container_ptr.tag()) {
                     .decl_ref => {
-                        const decl = container_ptr.castTag(.decl_ref).?.data;
+                        const decl_index = container_ptr.castTag(.decl_ref).?.data;
+                        const mod = bin_file.options.module.?;
+                        const decl = mod.declPtr(decl_index);
                         const addend = blk: {
                             switch (decl.ty.tag()) {
                                 .@"struct" => {
@@ -364,7 +366,7 @@ pub fn generateSymbol(
                                 },
                             }
                         };
-                        return lowerDeclRef(bin_file, src_loc, typed_value, decl, code, debug_output, .{
+                        return lowerDeclRef(bin_file, src_loc, typed_value, decl_index, code, debug_output, .{
                             .parent_atom_index = reloc_info.parent_atom_index,
                             .addend = (reloc_info.addend orelse 0) + addend,
                         });
@@ -400,8 +402,8 @@ pub fn generateSymbol(
 
                 switch (array_ptr.tag()) {
                     .decl_ref => {
-                        const decl = array_ptr.castTag(.decl_ref).?.data;
-                        return lowerDeclRef(bin_file, src_loc, typed_value, decl, code, debug_output, .{
+                        const decl_index = array_ptr.castTag(.decl_ref).?.data;
+                        return lowerDeclRef(bin_file, src_loc, typed_value, decl_index, code, debug_output, .{
                             .parent_atom_index = reloc_info.parent_atom_index,
                             .addend = (reloc_info.addend orelse 0) + addend,
                         });
@@ -589,7 +591,8 @@ pub fn generateSymbol(
             }
 
             const union_ty = typed_value.ty.cast(Type.Payload.Union).?.data;
-            const field_index = union_ty.tag_ty.enumTagFieldIndex(union_obj.tag, target).?;
+            const mod = bin_file.options.module.?;
+            const field_index = union_ty.tag_ty.enumTagFieldIndex(union_obj.tag, mod).?;
             assert(union_ty.haveFieldTypes());
             const field_ty = union_ty.fields.values()[field_index].ty;
             if (!field_ty.hasRuntimeBits()) {
@@ -772,12 +775,13 @@ fn lowerDeclRef(
     bin_file: *link.File,
     src_loc: Module.SrcLoc,
     typed_value: TypedValue,
-    decl: *Module.Decl,
+    decl_index: Module.Decl.Index,
     code: *std.ArrayList(u8),
     debug_output: DebugInfoOutput,
     reloc_info: RelocInfo,
 ) GenerateSymbolError!Result {
     const target = bin_file.options.target;
+    const module = bin_file.options.module.?;
     if (typed_value.ty.isSlice()) {
         // generate ptr
         var buf: Type.SlicePtrFieldTypeBuffer = undefined;
@@ -796,7 +800,7 @@ fn lowerDeclRef(
         // generate length
         var slice_len: Value.Payload.U64 = .{
             .base = .{ .tag = .int_u64 },
-            .data = typed_value.val.sliceLen(target),
+            .data = typed_value.val.sliceLen(module),
         };
         switch (try generateSymbol(bin_file, src_loc, .{
             .ty = Type.usize,
@@ -813,14 +817,16 @@ fn lowerDeclRef(
     }
 
     const ptr_width = target.cpu.arch.ptrBitWidth();
+    const decl = module.declPtr(decl_index);
     const is_fn_body = decl.ty.zigTypeTag() == .Fn;
     if (!is_fn_body and !decl.ty.hasRuntimeBits()) {
         try code.writer().writeByteNTimes(0xaa, @divExact(ptr_width, 8));
         return Result{ .appended = {} };
     }
 
-    decl.markAlive();
-    const vaddr = try bin_file.getDeclVAddr(decl, .{
+    module.markDeclAlive(decl);
+
+    const vaddr = try bin_file.getDeclVAddr(decl_index, .{
         .parent_atom_index = reloc_info.parent_atom_index,
         .offset = code.items.len,
         .addend = reloc_info.addend orelse 0,
src/Compilation.zig
@@ -191,22 +191,22 @@ pub const CSourceFile = struct {
 
 const Job = union(enum) {
     /// Write the constant value for a Decl to the output file.
-    codegen_decl: *Module.Decl,
+    codegen_decl: Module.Decl.Index,
     /// Write the machine code for a function to the output file.
     codegen_func: *Module.Fn,
     /// Render the .h file snippet for the Decl.
-    emit_h_decl: *Module.Decl,
+    emit_h_decl: Module.Decl.Index,
     /// The Decl needs to be analyzed and possibly export itself.
     /// It may have already be analyzed, or it may have been determined
     /// to be outdated; in this case perform semantic analysis again.
-    analyze_decl: *Module.Decl,
+    analyze_decl: Module.Decl.Index,
     /// The file that was loaded with `@embedFile` has changed on disk
     /// and has been re-loaded into memory. All Decls that depend on it
     /// need to be re-analyzed.
     update_embed_file: *Module.EmbedFile,
     /// The source file containing the Decl has been updated, and so the
     /// Decl may need its line number information updated in the debug info.
-    update_line_number: *Module.Decl,
+    update_line_number: Module.Decl.Index,
     /// The main source file for the package needs to be analyzed.
     analyze_pkg: *Package,
 
@@ -2105,17 +2105,18 @@ pub fn update(comp: *Compilation) !void {
             // deletion set may grow as we call `clearDecl` within this loop,
             // and more unreferenced Decls are revealed.
             while (module.deletion_set.count() != 0) {
-                const decl = module.deletion_set.keys()[0];
+                const decl_index = module.deletion_set.keys()[0];
+                const decl = module.declPtr(decl_index);
                 assert(decl.deletion_flag);
                 assert(decl.dependants.count() == 0);
                 const is_anon = if (decl.zir_decl_index == 0) blk: {
-                    break :blk decl.src_namespace.anon_decls.swapRemove(decl);
+                    break :blk decl.src_namespace.anon_decls.swapRemove(decl_index);
                 } else false;
 
-                try module.clearDecl(decl, null);
+                try module.clearDecl(decl_index, null);
 
                 if (is_anon) {
-                    decl.destroy(module);
+                    module.destroyDecl(decl_index);
                 }
             }
 
@@ -2444,13 +2445,15 @@ pub fn totalErrorCount(self: *Compilation) usize {
         // the previous parse success, including compile errors, but we cannot
         // emit them until the file succeeds parsing.
         for (module.failed_decls.keys()) |key| {
-            if (key.getFileScope().okToReportErrors()) {
+            const decl = module.declPtr(key);
+            if (decl.getFileScope().okToReportErrors()) {
                 total += 1;
             }
         }
         if (module.emit_h) |emit_h| {
             for (emit_h.failed_decls.keys()) |key| {
-                if (key.getFileScope().okToReportErrors()) {
+                const decl = module.declPtr(key);
+                if (decl.getFileScope().okToReportErrors()) {
                     total += 1;
                 }
             }
@@ -2529,9 +2532,10 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {
         {
             var it = module.failed_decls.iterator();
             while (it.next()) |entry| {
+                const decl = module.declPtr(entry.key_ptr.*);
                 // Skip errors for Decls within files that had a parse failure.
                 // We'll try again once parsing succeeds.
-                if (entry.key_ptr.*.getFileScope().okToReportErrors()) {
+                if (decl.getFileScope().okToReportErrors()) {
                     try AllErrors.add(module, &arena, &errors, entry.value_ptr.*.*);
                 }
             }
@@ -2539,9 +2543,10 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {
         if (module.emit_h) |emit_h| {
             var it = emit_h.failed_decls.iterator();
             while (it.next()) |entry| {
+                const decl = module.declPtr(entry.key_ptr.*);
                 // Skip errors for Decls within files that had a parse failure.
                 // We'll try again once parsing succeeds.
-                if (entry.key_ptr.*.getFileScope().okToReportErrors()) {
+                if (decl.getFileScope().okToReportErrors()) {
                     try AllErrors.add(module, &arena, &errors, entry.value_ptr.*.*);
                 }
             }
@@ -2564,7 +2569,8 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {
             const keys = module.compile_log_decls.keys();
             const values = module.compile_log_decls.values();
             // First one will be the error; subsequent ones will be notes.
-            const src_loc = keys[0].nodeOffsetSrcLoc(values[0]);
+            const err_decl = module.declPtr(keys[0]);
+            const src_loc = err_decl.nodeOffsetSrcLoc(values[0]);
             const err_msg = Module.ErrorMsg{
                 .src_loc = src_loc,
                 .msg = "found compile log statement",
@@ -2573,8 +2579,9 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {
             defer self.gpa.free(err_msg.notes);
 
             for (keys[1..]) |key, i| {
+                const note_decl = module.declPtr(key);
                 err_msg.notes[i] = .{
-                    .src_loc = key.nodeOffsetSrcLoc(values[i + 1]),
+                    .src_loc = note_decl.nodeOffsetSrcLoc(values[i + 1]),
                     .msg = "also here",
                 };
             }
@@ -2708,38 +2715,42 @@ pub fn performAllTheWork(
 
 fn processOneJob(comp: *Compilation, job: Job) !void {
     switch (job) {
-        .codegen_decl => |decl| switch (decl.analysis) {
-            .unreferenced => unreachable,
-            .in_progress => unreachable,
-            .outdated => unreachable,
-
-            .file_failure,
-            .sema_failure,
-            .codegen_failure,
-            .dependency_failure,
-            .sema_failure_retryable,
-            => return,
-
-            .complete, .codegen_failure_retryable => {
-                if (build_options.omit_stage2)
-                    @panic("sadly stage2 is omitted from this build to save memory on the CI server");
-
-                const named_frame = tracy.namedFrame("codegen_decl");
-                defer named_frame.end();
-
-                const module = comp.bin_file.options.module.?;
-                assert(decl.has_tv);
-
-                if (decl.alive) {
-                    try module.linkerUpdateDecl(decl);
-                    return;
-                }
+        .codegen_decl => |decl_index| {
+            if (build_options.omit_stage2)
+                @panic("sadly stage2 is omitted from this build to save memory on the CI server");
 
-                // Instead of sending this decl to the linker, we actually will delete it
-                // because we found out that it in fact was never referenced.
-                module.deleteUnusedDecl(decl);
-                return;
-            },
+            const module = comp.bin_file.options.module.?;
+            const decl = module.declPtr(decl_index);
+
+            switch (decl.analysis) {
+                .unreferenced => unreachable,
+                .in_progress => unreachable,
+                .outdated => unreachable,
+
+                .file_failure,
+                .sema_failure,
+                .codegen_failure,
+                .dependency_failure,
+                .sema_failure_retryable,
+                => return,
+
+                .complete, .codegen_failure_retryable => {
+                    const named_frame = tracy.namedFrame("codegen_decl");
+                    defer named_frame.end();
+
+                    assert(decl.has_tv);
+
+                    if (decl.alive) {
+                        try module.linkerUpdateDecl(decl_index);
+                        return;
+                    }
+
+                    // Instead of sending this decl to the linker, we actually will delete it
+                    // because we found out that it in fact was never referenced.
+                    module.deleteUnusedDecl(decl_index);
+                    return;
+                },
+            }
         },
         .codegen_func => |func| {
             if (build_options.omit_stage2)
@@ -2754,68 +2765,73 @@ fn processOneJob(comp: *Compilation, job: Job) !void {
                 error.AnalysisFail => return,
             };
         },
-        .emit_h_decl => |decl| switch (decl.analysis) {
-            .unreferenced => unreachable,
-            .in_progress => unreachable,
-            .outdated => unreachable,
-
-            .file_failure,
-            .sema_failure,
-            .dependency_failure,
-            .sema_failure_retryable,
-            => return,
-
-            // emit-h only requires semantic analysis of the Decl to be complete,
-            // it does not depend on machine code generation to succeed.
-            .codegen_failure, .codegen_failure_retryable, .complete => {
-                if (build_options.omit_stage2)
-                    @panic("sadly stage2 is omitted from this build to save memory on the CI server");
-
-                const named_frame = tracy.namedFrame("emit_h_decl");
-                defer named_frame.end();
-
-                const gpa = comp.gpa;
-                const module = comp.bin_file.options.module.?;
-                const emit_h = module.emit_h.?;
-                _ = try emit_h.decl_table.getOrPut(gpa, decl);
-                const decl_emit_h = decl.getEmitH(module);
-                const fwd_decl = &decl_emit_h.fwd_decl;
-                fwd_decl.shrinkRetainingCapacity(0);
-                var typedefs_arena = std.heap.ArenaAllocator.init(gpa);
-                defer typedefs_arena.deinit();
-
-                var dg: c_codegen.DeclGen = .{
-                    .gpa = gpa,
-                    .module = module,
-                    .error_msg = null,
-                    .decl = decl,
-                    .fwd_decl = fwd_decl.toManaged(gpa),
-                    .typedefs = c_codegen.TypedefMap.initContext(gpa, .{
-                        .target = comp.getTarget(),
-                    }),
-                    .typedefs_arena = typedefs_arena.allocator(),
-                };
-                defer dg.fwd_decl.deinit();
-                defer dg.typedefs.deinit();
+        .emit_h_decl => |decl_index| {
+            if (build_options.omit_stage2)
+                @panic("sadly stage2 is omitted from this build to save memory on the CI server");
 
-                c_codegen.genHeader(&dg) catch |err| switch (err) {
-                    error.AnalysisFail => {
-                        try emit_h.failed_decls.put(gpa, decl, dg.error_msg.?);
-                        return;
-                    },
-                    else => |e| return e,
-                };
+            const module = comp.bin_file.options.module.?;
+            const decl = module.declPtr(decl_index);
+
+            switch (decl.analysis) {
+                .unreferenced => unreachable,
+                .in_progress => unreachable,
+                .outdated => unreachable,
+
+                .file_failure,
+                .sema_failure,
+                .dependency_failure,
+                .sema_failure_retryable,
+                => return,
+
+                // emit-h only requires semantic analysis of the Decl to be complete,
+                // it does not depend on machine code generation to succeed.
+                .codegen_failure, .codegen_failure_retryable, .complete => {
+                    const named_frame = tracy.namedFrame("emit_h_decl");
+                    defer named_frame.end();
+
+                    const gpa = comp.gpa;
+                    const emit_h = module.emit_h.?;
+                    _ = try emit_h.decl_table.getOrPut(gpa, decl_index);
+                    const decl_emit_h = emit_h.declPtr(decl_index);
+                    const fwd_decl = &decl_emit_h.fwd_decl;
+                    fwd_decl.shrinkRetainingCapacity(0);
+                    var typedefs_arena = std.heap.ArenaAllocator.init(gpa);
+                    defer typedefs_arena.deinit();
+
+                    var dg: c_codegen.DeclGen = .{
+                        .gpa = gpa,
+                        .module = module,
+                        .error_msg = null,
+                        .decl_index = decl_index,
+                        .decl = decl,
+                        .fwd_decl = fwd_decl.toManaged(gpa),
+                        .typedefs = c_codegen.TypedefMap.initContext(gpa, .{
+                            .mod = module,
+                        }),
+                        .typedefs_arena = typedefs_arena.allocator(),
+                    };
+                    defer dg.fwd_decl.deinit();
+                    defer dg.typedefs.deinit();
 
-                fwd_decl.* = dg.fwd_decl.moveToUnmanaged();
-                fwd_decl.shrinkAndFree(gpa, fwd_decl.items.len);
-            },
+                    c_codegen.genHeader(&dg) catch |err| switch (err) {
+                        error.AnalysisFail => {
+                            try emit_h.failed_decls.put(gpa, decl_index, dg.error_msg.?);
+                            return;
+                        },
+                        else => |e| return e,
+                    };
+
+                    fwd_decl.* = dg.fwd_decl.moveToUnmanaged();
+                    fwd_decl.shrinkAndFree(gpa, fwd_decl.items.len);
+                },
+            }
         },
-        .analyze_decl => |decl| {
+        .analyze_decl => |decl_index| {
             if (build_options.omit_stage2)
                 @panic("sadly stage2 is omitted from this build to save memory on the CI server");
 
             const module = comp.bin_file.options.module.?;
-            module.ensureDeclAnalyzed(decl) catch |err| switch (err) {
+            module.ensureDeclAnalyzed(decl_index) catch |err| switch (err) {
                 error.OutOfMemory => return error.OutOfMemory,
                 error.AnalysisFail => return,
             };
@@ -2833,7 +2849,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void {
                 error.AnalysisFail => return,
             };
         },
-        .update_line_number => |decl| {
+        .update_line_number => |decl_index| {
             if (build_options.omit_stage2)
                 @panic("sadly stage2 is omitted from this build to save memory on the CI server");
 
@@ -2842,9 +2858,10 @@ fn processOneJob(comp: *Compilation, job: Job) !void {
 
             const gpa = comp.gpa;
             const module = comp.bin_file.options.module.?;
+            const decl = module.declPtr(decl_index);
             comp.bin_file.updateDeclLineNumber(module, decl) catch |err| {
                 try module.failed_decls.ensureUnusedCapacity(gpa, 1);
-                module.failed_decls.putAssumeCapacityNoClobber(decl, try Module.ErrorMsg.create(
+                module.failed_decls.putAssumeCapacityNoClobber(decl_index, try Module.ErrorMsg.create(
                     gpa,
                     decl.srcLoc(),
                     "unable to update line number: {s}",
@@ -3472,7 +3489,7 @@ fn reportRetryableEmbedFileError(
     const mod = comp.bin_file.options.module.?;
     const gpa = mod.gpa;
 
-    const src_loc: Module.SrcLoc = embed_file.owner_decl.srcLoc();
+    const src_loc: Module.SrcLoc = mod.declPtr(embed_file.owner_decl).srcLoc();
 
     const err_msg = if (embed_file.pkg.root_src_directory.path) |dir_path|
         try Module.ErrorMsg.create(
src/crash_report.zig
@@ -90,9 +90,11 @@ fn dumpStatusReport() !void {
 
     const stderr = io.getStdErr().writer();
     const block: *Sema.Block = anal.block;
+    const mod = anal.sema.mod;
+    const block_src_decl = mod.declPtr(block.src_decl);
 
     try stderr.writeAll("Analyzing ");
-    try writeFullyQualifiedDeclWithFile(block.src_decl, stderr);
+    try writeFullyQualifiedDeclWithFile(mod, block_src_decl, stderr);
     try stderr.writeAll("\n");
 
     print_zir.renderInstructionContext(
@@ -100,7 +102,7 @@ fn dumpStatusReport() !void {
         anal.body,
         anal.body_index,
         block.namespace.file_scope,
-        block.src_decl.src_node,
+        block_src_decl.src_node,
         6, // indent
         stderr,
     ) catch |err| switch (err) {
@@ -115,13 +117,14 @@ fn dumpStatusReport() !void {
     while (parent) |curr| {
         fba.reset();
         try stderr.writeAll("  in ");
-        try writeFullyQualifiedDeclWithFile(curr.block.src_decl, stderr);
+        const curr_block_src_decl = mod.declPtr(curr.block.src_decl);
+        try writeFullyQualifiedDeclWithFile(mod, curr_block_src_decl, stderr);
         try stderr.writeAll("\n    > ");
         print_zir.renderSingleInstruction(
             allocator,
             curr.body[curr.body_index],
             curr.block.namespace.file_scope,
-            curr.block.src_decl.src_node,
+            curr_block_src_decl.src_node,
             6, // indent
             stderr,
         ) catch |err| switch (err) {
@@ -146,10 +149,10 @@ fn writeFilePath(file: *Module.File, stream: anytype) !void {
     try stream.writeAll(file.sub_file_path);
 }
 
-fn writeFullyQualifiedDeclWithFile(decl: *Decl, stream: anytype) !void {
+fn writeFullyQualifiedDeclWithFile(mod: *Module, decl: *Decl, stream: anytype) !void {
     try writeFilePath(decl.getFileScope(), stream);
     try stream.writeAll(": ");
-    try decl.renderFullyQualifiedDebugName(stream);
+    try decl.renderFullyQualifiedDebugName(mod, stream);
 }
 
 pub fn compilerPanic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace) noreturn {
src/link.zig
@@ -417,17 +417,18 @@ pub const File = struct {
     /// Called from within the CodeGen to lower a local variable instantion as an unnamed
     /// constant. Returns the symbol index of the lowered constant in the read-only section
     /// of the final binary.
-    pub fn lowerUnnamedConst(base: *File, tv: TypedValue, decl: *Module.Decl) UpdateDeclError!u32 {
+    pub fn lowerUnnamedConst(base: *File, tv: TypedValue, decl_index: Module.Decl.Index) UpdateDeclError!u32 {
+        const decl = base.options.module.?.declPtr(decl_index);
         log.debug("lowerUnnamedConst {*} ({s})", .{ decl, decl.name });
         switch (base.tag) {
             // zig fmt: off
-            .coff  => return @fieldParentPtr(Coff,  "base", base).lowerUnnamedConst(tv, decl),
-            .elf   => return @fieldParentPtr(Elf,   "base", base).lowerUnnamedConst(tv, decl),
-            .macho => return @fieldParentPtr(MachO, "base", base).lowerUnnamedConst(tv, decl),
-            .plan9 => return @fieldParentPtr(Plan9, "base", base).lowerUnnamedConst(tv, decl),
+            .coff  => return @fieldParentPtr(Coff,  "base", base).lowerUnnamedConst(tv, decl_index),
+            .elf   => return @fieldParentPtr(Elf,   "base", base).lowerUnnamedConst(tv, decl_index),
+            .macho => return @fieldParentPtr(MachO, "base", base).lowerUnnamedConst(tv, decl_index),
+            .plan9 => return @fieldParentPtr(Plan9, "base", base).lowerUnnamedConst(tv, decl_index),
             .spirv => unreachable,
             .c     => unreachable,
-            .wasm  => unreachable,
+            .wasm  => return @fieldParentPtr(Wasm,  "base", base).lowerUnnamedConst(tv, decl_index),
             .nvptx => unreachable,
             // zig fmt: on
         }
@@ -435,19 +436,20 @@ pub const File = struct {
 
     /// May be called before or after updateDeclExports but must be called
     /// after allocateDeclIndexes for any given Decl.
-    pub fn updateDecl(base: *File, module: *Module, decl: *Module.Decl) UpdateDeclError!void {
+    pub fn updateDecl(base: *File, module: *Module, decl_index: Module.Decl.Index) UpdateDeclError!void {
+        const decl = module.declPtr(decl_index);
         log.debug("updateDecl {*} ({s}), type={}", .{ decl, decl.name, decl.ty.fmtDebug() });
         assert(decl.has_tv);
         switch (base.tag) {
             // zig fmt: off
-            .coff  => return @fieldParentPtr(Coff,  "base", base).updateDecl(module, decl),
-            .elf   => return @fieldParentPtr(Elf,   "base", base).updateDecl(module, decl),
-            .macho => return @fieldParentPtr(MachO, "base", base).updateDecl(module, decl),
-            .c     => return @fieldParentPtr(C,     "base", base).updateDecl(module, decl),
-            .wasm  => return @fieldParentPtr(Wasm,  "base", base).updateDecl(module, decl),
-            .spirv => return @fieldParentPtr(SpirV, "base", base).updateDecl(module, decl),
-            .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDecl(module, decl),
-            .nvptx => return @fieldParentPtr(NvPtx, "base", base).updateDecl(module, decl),
+            .coff  => return @fieldParentPtr(Coff,  "base", base).updateDecl(module, decl_index),
+            .elf   => return @fieldParentPtr(Elf,   "base", base).updateDecl(module, decl_index),
+            .macho => return @fieldParentPtr(MachO, "base", base).updateDecl(module, decl_index),
+            .c     => return @fieldParentPtr(C,     "base", base).updateDecl(module, decl_index),
+            .wasm  => return @fieldParentPtr(Wasm,  "base", base).updateDecl(module, decl_index),
+            .spirv => return @fieldParentPtr(SpirV, "base", base).updateDecl(module, decl_index),
+            .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDecl(module, decl_index),
+            .nvptx => return @fieldParentPtr(NvPtx, "base", base).updateDecl(module, decl_index),
             // zig fmt: on
         }
     }
@@ -455,8 +457,9 @@ pub const File = struct {
     /// May be called before or after updateDeclExports but must be called
     /// after allocateDeclIndexes for any given Decl.
     pub fn updateFunc(base: *File, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) UpdateDeclError!void {
+        const owner_decl = module.declPtr(func.owner_decl);
         log.debug("updateFunc {*} ({s}), type={}", .{
-            func.owner_decl, func.owner_decl.name, func.owner_decl.ty.fmtDebug(),
+            owner_decl, owner_decl.name, owner_decl.ty.fmtDebug(),
         });
         switch (base.tag) {
             // zig fmt: off
@@ -492,19 +495,20 @@ pub const File = struct {
     /// TODO we're transitioning to deleting this function and instead having
     /// each linker backend notice the first time updateDecl or updateFunc is called, or
     /// a callee referenced from AIR.
-    pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) error{OutOfMemory}!void {
+    pub fn allocateDeclIndexes(base: *File, decl_index: Module.Decl.Index) error{OutOfMemory}!void {
+        const decl = base.options.module.?.declPtr(decl_index);
         log.debug("allocateDeclIndexes {*} ({s})", .{ decl, decl.name });
         switch (base.tag) {
-            .coff => return @fieldParentPtr(Coff, "base", base).allocateDeclIndexes(decl),
-            .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),
-            .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl) catch |err| switch (err) {
+            .coff => return @fieldParentPtr(Coff, "base", base).allocateDeclIndexes(decl_index),
+            .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl_index),
+            .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl_index) catch |err| switch (err) {
                 // remap this error code because we are transitioning away from
                 // `allocateDeclIndexes`.
                 error.Overflow => return error.OutOfMemory,
                 error.OutOfMemory => return error.OutOfMemory,
             },
-            .wasm => return @fieldParentPtr(Wasm, "base", base).allocateDeclIndexes(decl),
-            .plan9 => return @fieldParentPtr(Plan9, "base", base).allocateDeclIndexes(decl),
+            .wasm => return @fieldParentPtr(Wasm, "base", base).allocateDeclIndexes(decl_index),
+            .plan9 => return @fieldParentPtr(Plan9, "base", base).allocateDeclIndexes(decl_index),
             .c, .spirv, .nvptx => {},
         }
     }
@@ -621,17 +625,16 @@ pub const File = struct {
     }
 
     /// Called when a Decl is deleted from the Module.
-    pub fn freeDecl(base: *File, decl: *Module.Decl) void {
-        log.debug("freeDecl {*} ({s})", .{ decl, decl.name });
+    pub fn freeDecl(base: *File, decl_index: Module.Decl.Index) void {
         switch (base.tag) {
-            .coff => @fieldParentPtr(Coff, "base", base).freeDecl(decl),
-            .elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl),
-            .macho => @fieldParentPtr(MachO, "base", base).freeDecl(decl),
-            .c => @fieldParentPtr(C, "base", base).freeDecl(decl),
-            .wasm => @fieldParentPtr(Wasm, "base", base).freeDecl(decl),
-            .spirv => @fieldParentPtr(SpirV, "base", base).freeDecl(decl),
-            .plan9 => @fieldParentPtr(Plan9, "base", base).freeDecl(decl),
-            .nvptx => @fieldParentPtr(NvPtx, "base", base).freeDecl(decl),
+            .coff => @fieldParentPtr(Coff, "base", base).freeDecl(decl_index),
+            .elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl_index),
+            .macho => @fieldParentPtr(MachO, "base", base).freeDecl(decl_index),
+            .c => @fieldParentPtr(C, "base", base).freeDecl(decl_index),
+            .wasm => @fieldParentPtr(Wasm, "base", base).freeDecl(decl_index),
+            .spirv => @fieldParentPtr(SpirV, "base", base).freeDecl(decl_index),
+            .plan9 => @fieldParentPtr(Plan9, "base", base).freeDecl(decl_index),
+            .nvptx => @fieldParentPtr(NvPtx, "base", base).freeDecl(decl_index),
         }
     }
 
@@ -656,20 +659,21 @@ pub const File = struct {
     pub fn updateDeclExports(
         base: *File,
         module: *Module,
-        decl: *Module.Decl,
+        decl_index: Module.Decl.Index,
         exports: []const *Module.Export,
     ) UpdateDeclExportsError!void {
+        const decl = module.declPtr(decl_index);
         log.debug("updateDeclExports {*} ({s})", .{ decl, decl.name });
         assert(decl.has_tv);
         switch (base.tag) {
-            .coff => return @fieldParentPtr(Coff, "base", base).updateDeclExports(module, decl, exports),
-            .elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl, exports),
-            .macho => return @fieldParentPtr(MachO, "base", base).updateDeclExports(module, decl, exports),
-            .c => return @fieldParentPtr(C, "base", base).updateDeclExports(module, decl, exports),
-            .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclExports(module, decl, exports),
-            .spirv => return @fieldParentPtr(SpirV, "base", base).updateDeclExports(module, decl, exports),
-            .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDeclExports(module, decl, exports),
-            .nvptx => return @fieldParentPtr(NvPtx, "base", base).updateDeclExports(module, decl, exports),
+            .coff => return @fieldParentPtr(Coff, "base", base).updateDeclExports(module, decl_index, exports),
+            .elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl_index, exports),
+            .macho => return @fieldParentPtr(MachO, "base", base).updateDeclExports(module, decl_index, exports),
+            .c => return @fieldParentPtr(C, "base", base).updateDeclExports(module, decl_index, exports),
+            .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclExports(module, decl_index, exports),
+            .spirv => return @fieldParentPtr(SpirV, "base", base).updateDeclExports(module, decl_index, exports),
+            .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDeclExports(module, decl_index, exports),
+            .nvptx => return @fieldParentPtr(NvPtx, "base", base).updateDeclExports(module, decl_index, exports),
         }
     }
 
@@ -683,14 +687,14 @@ pub const File = struct {
     /// The linker is passed information about the containing atom, `parent_atom_index`, and offset within it's
     /// memory buffer, `offset`, so that it can make a note of potential relocation sites, should the
     /// `Decl`'s address was not yet resolved, or the containing atom gets moved in virtual memory.
-    pub fn getDeclVAddr(base: *File, decl: *const Module.Decl, reloc_info: RelocInfo) !u64 {
+    pub fn getDeclVAddr(base: *File, decl_index: Module.Decl.Index, reloc_info: RelocInfo) !u64 {
         switch (base.tag) {
-            .coff => return @fieldParentPtr(Coff, "base", base).getDeclVAddr(decl, reloc_info),
-            .elf => return @fieldParentPtr(Elf, "base", base).getDeclVAddr(decl, reloc_info),
-            .macho => return @fieldParentPtr(MachO, "base", base).getDeclVAddr(decl, reloc_info),
-            .plan9 => return @fieldParentPtr(Plan9, "base", base).getDeclVAddr(decl, reloc_info),
+            .coff => return @fieldParentPtr(Coff, "base", base).getDeclVAddr(decl_index, reloc_info),
+            .elf => return @fieldParentPtr(Elf, "base", base).getDeclVAddr(decl_index, reloc_info),
+            .macho => return @fieldParentPtr(MachO, "base", base).getDeclVAddr(decl_index, reloc_info),
+            .plan9 => return @fieldParentPtr(Plan9, "base", base).getDeclVAddr(decl_index, reloc_info),
             .c => unreachable,
-            .wasm => return @fieldParentPtr(Wasm, "base", base).getDeclVAddr(decl, reloc_info),
+            .wasm => return @fieldParentPtr(Wasm, "base", base).getDeclVAddr(decl_index, reloc_info),
             .spirv => unreachable,
             .nvptx => unreachable,
         }
src/main.zig
@@ -3892,7 +3892,7 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void
                 .tree_loaded = true,
                 .zir = undefined,
                 .pkg = undefined,
-                .root_decl = null,
+                .root_decl = .none,
             };
 
             file.pkg = try Package.create(gpa, null, file.sub_file_path);
@@ -4098,7 +4098,7 @@ fn fmtPathFile(
             .tree_loaded = true,
             .zir = undefined,
             .pkg = undefined,
-            .root_decl = null,
+            .root_decl = .none,
         };
 
         file.pkg = try Package.create(fmt.gpa, null, file.sub_file_path);
@@ -4757,7 +4757,7 @@ pub fn cmdAstCheck(
         .tree = undefined,
         .zir = undefined,
         .pkg = undefined,
-        .root_decl = null,
+        .root_decl = .none,
     };
     if (zig_source_file) |file_name| {
         var f = fs.cwd().openFile(file_name, .{}) catch |err| {
@@ -4910,7 +4910,7 @@ pub fn cmdChangelist(
         .tree = undefined,
         .zir = undefined,
         .pkg = undefined,
-        .root_decl = null,
+        .root_decl = .none,
     };
 
     file.pkg = try Package.create(gpa, null, file.sub_file_path);
src/Module.zig
@@ -49,15 +49,15 @@ global_zir_cache: Compilation.Directory,
 /// Used by AstGen worker to load and store ZIR cache.
 local_zir_cache: Compilation.Directory,
 /// It's rare for a decl to be exported, so we save memory by having a sparse
-/// map of Decl pointers to details about them being exported.
+/// map of Decl indexes to details about them being exported.
 /// The Export memory is owned by the `export_owners` table; the slice itself
 /// is owned by this table. The slice is guaranteed to not be empty.
-decl_exports: std.AutoArrayHashMapUnmanaged(*Decl, []*Export) = .{},
+decl_exports: std.AutoArrayHashMapUnmanaged(Decl.Index, []*Export) = .{},
 /// This models the Decls that perform exports, so that `decl_exports` can be updated when a Decl
 /// is modified. Note that the key of this table is not the Decl being exported, but the Decl that
 /// is performing the export of another Decl.
 /// This table owns the Export memory.
-export_owners: std.AutoArrayHashMapUnmanaged(*Decl, []*Export) = .{},
+export_owners: std.AutoArrayHashMapUnmanaged(Decl.Index, []*Export) = .{},
 /// The set of all the Zig source files in the Module. We keep track of this in order
 /// to iterate over it and check which source files have been modified on the file system when
 /// an update is requested, as well as to cache `@import` results.
@@ -89,10 +89,10 @@ align_stack_fns: std.AutoHashMapUnmanaged(*const Fn, SetAlignStack) = .{},
 /// The ErrorMsg memory is owned by the decl, using Module's general purpose allocator.
 /// Note that a Decl can succeed but the Fn it represents can fail. In this case,
 /// a Decl can have a failed_decls entry but have analysis status of success.
-failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *ErrorMsg) = .{},
+failed_decls: std.AutoArrayHashMapUnmanaged(Decl.Index, *ErrorMsg) = .{},
 /// Keep track of one `@compileLog` callsite per owner Decl.
 /// The value is the AST node index offset from the Decl.
-compile_log_decls: std.AutoArrayHashMapUnmanaged(*Decl, i32) = .{},
+compile_log_decls: std.AutoArrayHashMapUnmanaged(Decl.Index, i32) = .{},
 /// Using a map here for consistency with the other fields here.
 /// The ErrorMsg memory is owned by the `File`, using Module's general purpose allocator.
 failed_files: std.AutoArrayHashMapUnmanaged(*File, ?*ErrorMsg) = .{},
@@ -102,11 +102,9 @@ failed_embed_files: std.AutoArrayHashMapUnmanaged(*EmbedFile, *ErrorMsg) = .{},
 /// The ErrorMsg memory is owned by the `Export`, using Module's general purpose allocator.
 failed_exports: std.AutoArrayHashMapUnmanaged(*Export, *ErrorMsg) = .{},
 
-next_anon_name_index: usize = 0,
-
 /// Candidates for deletion. After a semantic analysis update completes, this list
 /// contains Decls that need to be deleted if they end up having no references to them.
-deletion_set: std.AutoArrayHashMapUnmanaged(*Decl, void) = .{},
+deletion_set: std.AutoArrayHashMapUnmanaged(Decl.Index, void) = .{},
 
 /// Error tags and their values, tag names are duped with mod.gpa.
 /// Corresponds with `error_name_list`.
@@ -137,7 +135,21 @@ compile_log_text: ArrayListUnmanaged(u8) = .{},
 
 emit_h: ?*GlobalEmitH,
 
-test_functions: std.AutoArrayHashMapUnmanaged(*Decl, void) = .{},
+test_functions: std.AutoArrayHashMapUnmanaged(Decl.Index, void) = .{},
+
+/// Rather than allocating Decl objects with an Allocator, we instead allocate
+/// them with this SegmentedList. This provides four advantages:
+///  * Stable memory so that one thread can access a Decl object while another
+///    thread allocates additional Decl objects from this list.
+///  * It allows us to use u32 indexes to reference Decl objects rather than
+///    pointers, saving memory in Type, Value, and dependency sets.
+///  * Using integers to reference Decl objects rather than pointers makes
+///    serialization trivial.
+///  * It provides a unique integer to be used for anonymous symbol names, avoiding
+///    multi-threaded contention on an atomic counter.
+allocated_decls: std.SegmentedList(Decl, 0) = .{},
+/// When a Decl object is freed from `allocated_decls`, it is pushed into this stack.
+decls_free_list: std.ArrayListUnmanaged(Decl.Index) = .{},
 
 const MonomorphedFuncsSet = std.HashMapUnmanaged(
     *Fn,
@@ -173,7 +185,7 @@ pub const MemoizedCallSet = std.HashMapUnmanaged(
 );
 
 pub const MemoizedCall = struct {
-    target: std.Target,
+    module: *Module,
 
     pub const Key = struct {
         func: *Fn,
@@ -191,7 +203,7 @@ pub const MemoizedCall = struct {
         assert(a.args.len == b.args.len);
         for (a.args) |a_arg, arg_i| {
             const b_arg = b.args[arg_i];
-            if (!a_arg.eql(b_arg, ctx.target)) {
+            if (!a_arg.eql(b_arg, ctx.module)) {
                 return false;
             }
         }
@@ -210,7 +222,7 @@ pub const MemoizedCall = struct {
         // This logic must be kept in sync with the logic in `analyzeCall` that
         // computes the hash.
         for (key.args) |arg| {
-            arg.hash(&hasher, ctx.target);
+            arg.hash(&hasher, ctx.module);
         }
 
         return hasher.final();
@@ -231,9 +243,17 @@ pub const GlobalEmitH = struct {
     /// When emit_h is non-null, each Decl gets one more compile error slot for
     /// emit-h failing for that Decl. This table is also how we tell if a Decl has
     /// failed emit-h or succeeded.
-    failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *ErrorMsg) = .{},
+    failed_decls: std.AutoArrayHashMapUnmanaged(Decl.Index, *ErrorMsg) = .{},
     /// Tracks all decls in order to iterate over them and emit .h code for them.
-    decl_table: std.AutoArrayHashMapUnmanaged(*Decl, void) = .{},
+    decl_table: std.AutoArrayHashMapUnmanaged(Decl.Index, void) = .{},
+    /// Similar to the allocated_decls field of Module, this is where `EmitH` objects
+    /// are allocated. There will be exactly one EmitH object per Decl object, with
+    /// identical indexes.
+    allocated_emit_h: std.SegmentedList(EmitH, 0) = .{},
+
+    pub fn declPtr(global_emit_h: *GlobalEmitH, decl_index: Decl.Index) *EmitH {
+        return global_emit_h.allocated_emit_h.at(@enumToInt(decl_index));
+    }
 };
 
 pub const ErrorInt = u32;
@@ -244,12 +264,12 @@ pub const Export = struct {
     /// Represents the position of the export, if any, in the output file.
     link: link.File.Export,
     /// The Decl that performs the export. Note that this is *not* the Decl being exported.
-    owner_decl: *Decl,
+    owner_decl: Decl.Index,
     /// The Decl containing the export statement.  Inline function calls
     /// may cause this to be different from the owner_decl.
-    src_decl: *Decl,
+    src_decl: Decl.Index,
     /// The Decl being exported. Note this is *not* the Decl performing the export.
-    exported_decl: *Decl,
+    exported_decl: Decl.Index,
     status: enum {
         in_progress,
         failed,
@@ -259,22 +279,16 @@ pub const Export = struct {
         complete,
     },
 
-    pub fn getSrcLoc(exp: Export) SrcLoc {
+    pub fn getSrcLoc(exp: Export, mod: *Module) SrcLoc {
+        const src_decl = mod.declPtr(exp.src_decl);
         return .{
-            .file_scope = exp.src_decl.getFileScope(),
-            .parent_decl_node = exp.src_decl.src_node,
+            .file_scope = src_decl.getFileScope(),
+            .parent_decl_node = src_decl.src_node,
             .lazy = exp.src,
         };
     }
 };
 
-/// When Module emit_h field is non-null, each Decl is allocated via this struct, so that
-/// there can be EmitH state attached to each Decl.
-pub const DeclPlusEmitH = struct {
-    decl: Decl,
-    emit_h: EmitH,
-};
-
 pub const CaptureScope = struct {
     parent: ?*CaptureScope,
 
@@ -458,36 +472,33 @@ pub const Decl = struct {
     /// typed_value may need to be regenerated.
     dependencies: DepsTable = .{},
 
-    pub const DepsTable = std.AutoArrayHashMapUnmanaged(*Decl, void);
-
-    pub fn clearName(decl: *Decl, gpa: Allocator) void {
-        gpa.free(mem.sliceTo(decl.name, 0));
-        decl.name = undefined;
-    }
+    pub const Index = enum(u32) {
+        _,
 
-    pub fn destroy(decl: *Decl, module: *Module) void {
-        const gpa = module.gpa;
-        log.debug("destroy {*} ({s})", .{ decl, decl.name });
-        _ = module.test_functions.swapRemove(decl);
-        if (decl.deletion_flag) {
-            assert(module.deletion_set.swapRemove(decl));
+        pub fn toOptional(i: Index) OptionalIndex {
+            return @intToEnum(OptionalIndex, @enumToInt(i));
         }
-        if (decl.has_tv) {
-            if (decl.getInnerNamespace()) |namespace| {
-                namespace.destroyDecls(module);
-            }
-            decl.clearValues(gpa);
+    };
+
+    pub const OptionalIndex = enum(u32) {
+        none = std.math.maxInt(u32),
+        _,
+
+        pub fn init(oi: ?Index) OptionalIndex {
+            return oi orelse .none;
         }
-        decl.dependants.deinit(gpa);
-        decl.dependencies.deinit(gpa);
-        decl.clearName(gpa);
-        if (module.emit_h != null) {
-            const decl_plus_emit_h = @fieldParentPtr(DeclPlusEmitH, "decl", decl);
-            decl_plus_emit_h.emit_h.fwd_decl.deinit(gpa);
-            gpa.destroy(decl_plus_emit_h);
-        } else {
-            gpa.destroy(decl);
+
+        pub fn unwrap(oi: OptionalIndex) ?Index {
+            if (oi == .none) return null;
+            return @intToEnum(Index, @enumToInt(oi));
         }
+    };
+
+    pub const DepsTable = std.AutoArrayHashMapUnmanaged(Decl.Index, void);
+
+    pub fn clearName(decl: *Decl, gpa: Allocator) void {
+        gpa.free(mem.sliceTo(decl.name, 0));
+        decl.name = undefined;
     }
 
     pub fn clearValues(decl: *Decl, gpa: Allocator) void {
@@ -573,13 +584,6 @@ pub const Decl = struct {
         return @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
     }
 
-    /// Returns true if and only if the Decl is the top level struct associated with a File.
-    pub fn isRoot(decl: *const Decl) bool {
-        if (decl.src_namespace.parent != null)
-            return false;
-        return decl == decl.src_namespace.getDecl();
-    }
-
     pub fn relativeToLine(decl: Decl, offset: u32) u32 {
         return decl.src_line + offset;
     }
@@ -622,20 +626,20 @@ pub const Decl = struct {
         return tree.tokens.items(.start)[decl.srcToken()];
     }
 
-    pub fn renderFullyQualifiedName(decl: Decl, writer: anytype) !void {
+    pub fn renderFullyQualifiedName(decl: Decl, mod: *Module, writer: anytype) !void {
         const unqualified_name = mem.sliceTo(decl.name, 0);
-        return decl.src_namespace.renderFullyQualifiedName(unqualified_name, writer);
+        return decl.src_namespace.renderFullyQualifiedName(mod, unqualified_name, writer);
     }
 
-    pub fn renderFullyQualifiedDebugName(decl: Decl, writer: anytype) !void {
+    pub fn renderFullyQualifiedDebugName(decl: Decl, mod: *Module, writer: anytype) !void {
         const unqualified_name = mem.sliceTo(decl.name, 0);
-        return decl.src_namespace.renderFullyQualifiedDebugName(unqualified_name, writer);
+        return decl.src_namespace.renderFullyQualifiedDebugName(mod, unqualified_name, writer);
     }
 
-    pub fn getFullyQualifiedName(decl: Decl, gpa: Allocator) ![:0]u8 {
-        var buffer = std.ArrayList(u8).init(gpa);
+    pub fn getFullyQualifiedName(decl: Decl, mod: *Module) ![:0]u8 {
+        var buffer = std.ArrayList(u8).init(mod.gpa);
         defer buffer.deinit();
-        try decl.renderFullyQualifiedName(buffer.writer());
+        try decl.renderFullyQualifiedName(mod, buffer.writer());
         return buffer.toOwnedSliceSentinel(0);
     }
 
@@ -662,7 +666,6 @@ pub const Decl = struct {
         if (!decl.owns_tv) return null;
         const ty = (decl.val.castTag(.ty) orelse return null).data;
         const struct_obj = (ty.castTag(.@"struct") orelse return null).data;
-        assert(struct_obj.owner_decl == decl);
         return struct_obj;
     }
 
@@ -672,7 +675,6 @@ pub const Decl = struct {
         if (!decl.owns_tv) return null;
         const ty = (decl.val.castTag(.ty) orelse return null).data;
         const union_obj = (ty.cast(Type.Payload.Union) orelse return null).data;
-        assert(union_obj.owner_decl == decl);
         return union_obj;
     }
 
@@ -681,7 +683,6 @@ pub const Decl = struct {
     pub fn getFunction(decl: *const Decl) ?*Fn {
         if (!decl.owns_tv) return null;
         const func = (decl.val.castTag(.function) orelse return null).data;
-        assert(func.owner_decl == decl);
         return func;
     }
 
@@ -690,16 +691,14 @@ pub const Decl = struct {
     pub fn getExternFn(decl: *const Decl) ?*ExternFn {
         if (!decl.owns_tv) return null;
         const extern_fn = (decl.val.castTag(.extern_fn) orelse return null).data;
-        assert(extern_fn.owner_decl == decl);
         return extern_fn;
     }
 
     /// If the Decl has a value and it is a variable, returns it,
     /// otherwise null.
-    pub fn getVariable(decl: *Decl) ?*Var {
+    pub fn getVariable(decl: *const Decl) ?*Var {
         if (!decl.owns_tv) return null;
         const variable = (decl.val.castTag(.variable) orelse return null).data;
-        assert(variable.owner_decl == decl);
         return variable;
     }
 
@@ -712,12 +711,10 @@ pub const Decl = struct {
         switch (ty.tag()) {
             .@"struct" => {
                 const struct_obj = ty.castTag(.@"struct").?.data;
-                assert(struct_obj.owner_decl == decl);
                 return &struct_obj.namespace;
             },
             .enum_full, .enum_nonexhaustive => {
                 const enum_obj = ty.cast(Type.Payload.EnumFull).?.data;
-                assert(enum_obj.owner_decl == decl);
                 return &enum_obj.namespace;
             },
             .empty_struct => {
@@ -725,12 +722,10 @@ pub const Decl = struct {
             },
             .@"opaque" => {
                 const opaque_obj = ty.cast(Type.Payload.Opaque).?.data;
-                assert(opaque_obj.owner_decl == decl);
                 return &opaque_obj.namespace;
             },
             .@"union", .union_tagged => {
                 const union_obj = ty.cast(Type.Payload.Union).?.data;
-                assert(union_obj.owner_decl == decl);
                 return &union_obj.namespace;
             },
 
@@ -757,17 +752,11 @@ pub const Decl = struct {
         return decl.src_namespace.file_scope;
     }
 
-    pub fn getEmitH(decl: *Decl, module: *Module) *EmitH {
-        assert(module.emit_h != null);
-        const decl_plus_emit_h = @fieldParentPtr(DeclPlusEmitH, "decl", decl);
-        return &decl_plus_emit_h.emit_h;
-    }
-
-    pub fn removeDependant(decl: *Decl, other: *Decl) void {
+    pub fn removeDependant(decl: *Decl, other: Decl.Index) void {
         assert(decl.dependants.swapRemove(other));
     }
 
-    pub fn removeDependency(decl: *Decl, other: *Decl) void {
+    pub fn removeDependency(decl: *Decl, other: Decl.Index) void {
         assert(decl.dependencies.swapRemove(other));
     }
 
@@ -790,16 +779,6 @@ pub const Decl = struct {
             return decl.ty.abiAlignment(target);
         }
     }
-
-    pub fn markAlive(decl: *Decl) void {
-        if (decl.alive) return;
-        decl.alive = true;
-
-        // This is the first time we are marking this Decl alive. We must
-        // therefore recurse into its value and mark any Decl it references
-        // as also alive, so that any Decl referenced does not get garbage collected.
-        decl.val.markReferencedDeclsAlive();
-    }
 };
 
 /// This state is attached to every Decl when Module emit_h is non-null.
@@ -810,7 +789,7 @@ pub const EmitH = struct {
 /// Represents the data that an explicit error set syntax provides.
 pub const ErrorSet = struct {
     /// The Decl that corresponds to the error set itself.
-    owner_decl: *Decl,
+    owner_decl: Decl.Index,
     /// Offset from Decl node index, points to the error set AST node.
     node_offset: i32,
     /// The string bytes are stored in the owner Decl arena.
@@ -819,10 +798,11 @@ pub const ErrorSet = struct {
 
     pub const NameMap = std.StringArrayHashMapUnmanaged(void);
 
-    pub fn srcLoc(self: ErrorSet) SrcLoc {
+    pub fn srcLoc(self: ErrorSet, mod: *Module) SrcLoc {
+        const owner_decl = mod.declPtr(self.owner_decl);
         return .{
-            .file_scope = self.owner_decl.getFileScope(),
-            .parent_decl_node = self.owner_decl.src_node,
+            .file_scope = owner_decl.getFileScope(),
+            .parent_decl_node = owner_decl.src_node,
             .lazy = .{ .node_offset = self.node_offset },
         };
     }
@@ -844,12 +824,12 @@ pub const PropertyBoolean = enum { no, yes, unknown, wip };
 
 /// Represents the data that a struct declaration provides.
 pub const Struct = struct {
-    /// The Decl that corresponds to the struct itself.
-    owner_decl: *Decl,
     /// Set of field names in declaration order.
     fields: Fields,
     /// Represents the declarations inside this struct.
     namespace: Namespace,
+    /// The Decl that corresponds to the struct itself.
+    owner_decl: Decl.Index,
     /// Offset from `owner_decl`, points to the struct AST node.
     node_offset: i32,
     /// Index of the struct_decl ZIR instruction.
@@ -900,30 +880,32 @@ pub const Struct = struct {
         }
     };
 
-    pub fn getFullyQualifiedName(s: *Struct, gpa: Allocator) ![:0]u8 {
-        return s.owner_decl.getFullyQualifiedName(gpa);
+    pub fn getFullyQualifiedName(s: *Struct, mod: *Module) ![:0]u8 {
+        return mod.declPtr(s.owner_decl).getFullyQualifiedName(mod);
     }
 
-    pub fn srcLoc(s: Struct) SrcLoc {
+    pub fn srcLoc(s: Struct, mod: *Module) SrcLoc {
+        const owner_decl = mod.declPtr(s.owner_decl);
         return .{
-            .file_scope = s.owner_decl.getFileScope(),
-            .parent_decl_node = s.owner_decl.src_node,
+            .file_scope = owner_decl.getFileScope(),
+            .parent_decl_node = owner_decl.src_node,
             .lazy = .{ .node_offset = s.node_offset },
         };
     }
 
-    pub fn fieldSrcLoc(s: Struct, gpa: Allocator, query: FieldSrcQuery) SrcLoc {
+    pub fn fieldSrcLoc(s: Struct, mod: *Module, query: FieldSrcQuery) SrcLoc {
         @setCold(true);
-        const tree = s.owner_decl.getFileScope().getTree(gpa) catch |err| {
+        const owner_decl = mod.declPtr(s.owner_decl);
+        const file = owner_decl.getFileScope();
+        const tree = file.getTree(mod.gpa) catch |err| {
             // In this case we emit a warning + a less precise source location.
             log.warn("unable to load {s}: {s}", .{
-                s.owner_decl.getFileScope().sub_file_path, @errorName(err),
+                file.sub_file_path, @errorName(err),
             });
-            return s.srcLoc();
+            return s.srcLoc(mod);
         };
-        const node = s.owner_decl.relativeToNodeIndex(s.node_offset);
+        const node = owner_decl.relativeToNodeIndex(s.node_offset);
         const node_tags = tree.nodes.items(.tag);
-        const file = s.owner_decl.getFileScope();
         switch (node_tags[node]) {
             .container_decl,
             .container_decl_trailing,
@@ -1013,18 +995,19 @@ pub const Struct = struct {
 /// the number of fields.
 pub const EnumSimple = struct {
     /// The Decl that corresponds to the enum itself.
-    owner_decl: *Decl,
-    /// Set of field names in declaration order.
-    fields: NameMap,
+    owner_decl: Decl.Index,
     /// Offset from `owner_decl`, points to the enum decl AST node.
     node_offset: i32,
+    /// Set of field names in declaration order.
+    fields: NameMap,
 
     pub const NameMap = EnumFull.NameMap;
 
-    pub fn srcLoc(self: EnumSimple) SrcLoc {
+    pub fn srcLoc(self: EnumSimple, mod: *Module) SrcLoc {
+        const owner_decl = mod.declPtr(self.owner_decl);
         return .{
-            .file_scope = self.owner_decl.getFileScope(),
-            .parent_decl_node = self.owner_decl.src_node,
+            .file_scope = owner_decl.getFileScope(),
+            .parent_decl_node = owner_decl.src_node,
             .lazy = .{ .node_offset = self.node_offset },
         };
     }
@@ -1035,7 +1018,9 @@ pub const EnumSimple = struct {
 /// are explicitly provided.
 pub const EnumNumbered = struct {
     /// The Decl that corresponds to the enum itself.
-    owner_decl: *Decl,
+    owner_decl: Decl.Index,
+    /// Offset from `owner_decl`, points to the enum decl AST node.
+    node_offset: i32,
     /// An integer type which is used for the numerical value of the enum.
     /// Whether zig chooses this type or the user specifies it, it is stored here.
     tag_ty: Type,
@@ -1045,16 +1030,15 @@ pub const EnumNumbered = struct {
     /// Entries are in declaration order, same as `fields`.
     /// If this hash map is empty, it means the enum tags are auto-numbered.
     values: ValueMap,
-    /// Offset from `owner_decl`, points to the enum decl AST node.
-    node_offset: i32,
 
     pub const NameMap = EnumFull.NameMap;
     pub const ValueMap = EnumFull.ValueMap;
 
-    pub fn srcLoc(self: EnumNumbered) SrcLoc {
+    pub fn srcLoc(self: EnumNumbered, mod: *Module) SrcLoc {
+        const owner_decl = mod.declPtr(self.owner_decl);
         return .{
-            .file_scope = self.owner_decl.getFileScope(),
-            .parent_decl_node = self.owner_decl.src_node,
+            .file_scope = owner_decl.getFileScope(),
+            .parent_decl_node = owner_decl.src_node,
             .lazy = .{ .node_offset = self.node_offset },
         };
     }
@@ -1064,7 +1048,9 @@ pub const EnumNumbered = struct {
 /// at least one tag value explicitly specified, or at least one declaration.
 pub const EnumFull = struct {
     /// The Decl that corresponds to the enum itself.
-    owner_decl: *Decl,
+    owner_decl: Decl.Index,
+    /// Offset from `owner_decl`, points to the enum decl AST node.
+    node_offset: i32,
     /// An integer type which is used for the numerical value of the enum.
     /// Whether zig chooses this type or the user specifies it, it is stored here.
     tag_ty: Type,
@@ -1076,26 +1062,23 @@ pub const EnumFull = struct {
     values: ValueMap,
     /// Represents the declarations inside this enum.
     namespace: Namespace,
-    /// Offset from `owner_decl`, points to the enum decl AST node.
-    node_offset: i32,
     /// true if zig inferred this tag type, false if user specified it
     tag_ty_inferred: bool,
 
     pub const NameMap = std.StringArrayHashMapUnmanaged(void);
     pub const ValueMap = std.ArrayHashMapUnmanaged(Value, void, Value.ArrayHashContext, false);
 
-    pub fn srcLoc(self: EnumFull) SrcLoc {
+    pub fn srcLoc(self: EnumFull, mod: *Module) SrcLoc {
+        const owner_decl = mod.declPtr(self.owner_decl);
         return .{
-            .file_scope = self.owner_decl.getFileScope(),
-            .parent_decl_node = self.owner_decl.src_node,
+            .file_scope = owner_decl.getFileScope(),
+            .parent_decl_node = owner_decl.src_node,
             .lazy = .{ .node_offset = self.node_offset },
         };
     }
 };
 
 pub const Union = struct {
-    /// The Decl that corresponds to the union itself.
-    owner_decl: *Decl,
     /// An enum type which is used for the tag of the union.
     /// This type is created even for untagged unions, even when the memory
     /// layout does not store the tag.
@@ -1106,6 +1089,8 @@ pub const Union = struct {
     fields: Fields,
     /// Represents the declarations inside this union.
     namespace: Namespace,
+    /// The Decl that corresponds to the union itself.
+    owner_decl: Decl.Index,
     /// Offset from `owner_decl`, points to the union decl AST node.
     node_offset: i32,
     /// Index of the union_decl ZIR instruction.
@@ -1145,30 +1130,32 @@ pub const Union = struct {
 
     pub const Fields = std.StringArrayHashMapUnmanaged(Field);
 
-    pub fn getFullyQualifiedName(s: *Union, gpa: Allocator) ![:0]u8 {
-        return s.owner_decl.getFullyQualifiedName(gpa);
+    pub fn getFullyQualifiedName(s: *Union, mod: *Module) ![:0]u8 {
+        return mod.declPtr(s.owner_decl).getFullyQualifiedName(mod);
     }
 
-    pub fn srcLoc(self: Union) SrcLoc {
+    pub fn srcLoc(self: Union, mod: *Module) SrcLoc {
+        const owner_decl = mod.declPtr(self.owner_decl);
         return .{
-            .file_scope = self.owner_decl.getFileScope(),
-            .parent_decl_node = self.owner_decl.src_node,
+            .file_scope = owner_decl.getFileScope(),
+            .parent_decl_node = owner_decl.src_node,
             .lazy = .{ .node_offset = self.node_offset },
         };
     }
 
-    pub fn fieldSrcLoc(u: Union, gpa: Allocator, query: FieldSrcQuery) SrcLoc {
+    pub fn fieldSrcLoc(u: Union, mod: *Module, query: FieldSrcQuery) SrcLoc {
         @setCold(true);
-        const tree = u.owner_decl.getFileScope().getTree(gpa) catch |err| {
+        const owner_decl = mod.declPtr(u.owner_decl);
+        const file = owner_decl.getFileScope();
+        const tree = file.getTree(mod.gpa) catch |err| {
             // In this case we emit a warning + a less precise source location.
             log.warn("unable to load {s}: {s}", .{
-                u.owner_decl.getFileScope().sub_file_path, @errorName(err),
+                file.sub_file_path, @errorName(err),
             });
-            return u.srcLoc();
+            return u.srcLoc(mod);
         };
-        const node = u.owner_decl.relativeToNodeIndex(u.node_offset);
+        const node = owner_decl.relativeToNodeIndex(u.node_offset);
         const node_tags = tree.nodes.items(.tag);
-        const file = u.owner_decl.getFileScope();
         switch (node_tags[node]) {
             .container_decl,
             .container_decl_trailing,
@@ -1348,22 +1335,23 @@ pub const Union = struct {
 
 pub const Opaque = struct {
     /// The Decl that corresponds to the opaque itself.
-    owner_decl: *Decl,
-    /// Represents the declarations inside this opaque.
-    namespace: Namespace,
+    owner_decl: Decl.Index,
     /// Offset from `owner_decl`, points to the opaque decl AST node.
     node_offset: i32,
+    /// Represents the declarations inside this opaque.
+    namespace: Namespace,
 
-    pub fn srcLoc(self: Opaque) SrcLoc {
+    pub fn srcLoc(self: Opaque, mod: *Module) SrcLoc {
+        const owner_decl = mod.declPtr(self.owner_decl);
         return .{
-            .file_scope = self.owner_decl.getFileScope(),
-            .parent_decl_node = self.owner_decl.src_node,
+            .file_scope = owner_decl.getFileScope(),
+            .parent_decl_node = owner_decl.src_node,
             .lazy = .{ .node_offset = self.node_offset },
         };
     }
 
-    pub fn getFullyQualifiedName(s: *Opaque, gpa: Allocator) ![:0]u8 {
-        return s.owner_decl.getFullyQualifiedName(gpa);
+    pub fn getFullyQualifiedName(s: *Opaque, mod: *Module) ![:0]u8 {
+        return mod.declPtr(s.owner_decl).getFullyQualifiedName(mod);
     }
 };
 
@@ -1371,7 +1359,7 @@ pub const Opaque = struct {
 /// arena allocator.
 pub const ExternFn = struct {
     /// The Decl that corresponds to the function itself.
-    owner_decl: *Decl,
+    owner_decl: Decl.Index,
     /// Library name if specified.
     /// For example `extern "c" fn write(...) usize` would have 'c' as library name.
     /// Allocated with Module's allocator; outlives the ZIR code.
@@ -1389,7 +1377,12 @@ pub const ExternFn = struct {
 /// instead.
 pub const Fn = struct {
     /// The Decl that corresponds to the function itself.
-    owner_decl: *Decl,
+    owner_decl: Decl.Index,
+    /// The ZIR instruction that is a function instruction. Use this to find
+    /// the body. We store this rather than the body directly so that when ZIR
+    /// is regenerated on update(), we can map this to the new corresponding
+    /// ZIR instruction.
+    zir_body_inst: Zir.Inst.Index,
     /// If this is not null, this function is a generic function instantiation, and
     /// there is a `TypedValue` here for each parameter of the function.
     /// Non-comptime parameters are marked with a `generic_poison` for the value.
@@ -1403,11 +1396,6 @@ pub const Fn = struct {
     /// parameter and tells whether it is anytype.
     /// TODO apply the same enhancement for param_names below to this field.
     anytype_args: [*]bool,
-    /// The ZIR instruction that is a function instruction. Use this to find
-    /// the body. We store this rather than the body directly so that when ZIR
-    /// is regenerated on update(), we can map this to the new corresponding
-    /// ZIR instruction.
-    zir_body_inst: Zir.Inst.Index,
 
     /// Prefer to use `getParamName` to access this because of the future improvement
     /// we want to do mentioned in the TODO below.
@@ -1537,8 +1525,9 @@ pub const Fn = struct {
         return func.param_names[index];
     }
 
-    pub fn hasInferredErrorSet(func: Fn) bool {
-        const zir = func.owner_decl.getFileScope().zir;
+    pub fn hasInferredErrorSet(func: Fn, mod: *Module) bool {
+        const owner_decl = mod.declPtr(func.owner_decl);
+        const zir = owner_decl.getFileScope().zir;
         const zir_tags = zir.instructions.items(.tag);
         switch (zir_tags[func.zir_body_inst]) {
             .func => return false,
@@ -1556,7 +1545,7 @@ pub const Fn = struct {
 pub const Var = struct {
     /// if is_extern == true this is undefined
     init: Value,
-    owner_decl: *Decl,
+    owner_decl: Decl.Index,
 
     /// Library name if specified.
     /// For example `extern "c" var stderrp = ...` would have 'c' as library name.
@@ -1576,14 +1565,16 @@ pub const Var = struct {
 };
 
 pub const DeclAdapter = struct {
+    mod: *Module,
+
     pub fn hash(self: @This(), s: []const u8) u32 {
         _ = self;
         return @truncate(u32, std.hash.Wyhash.hash(0, s));
     }
 
-    pub fn eql(self: @This(), a: []const u8, b_decl: *Decl, b_index: usize) bool {
-        _ = self;
+    pub fn eql(self: @This(), a: []const u8, b_decl_index: Decl.Index, b_index: usize) bool {
         _ = b_index;
+        const b_decl = self.mod.declPtr(b_decl_index);
         return mem.eql(u8, a, mem.sliceTo(b_decl.name, 0));
     }
 };
@@ -1599,25 +1590,30 @@ pub const Namespace = struct {
     /// Declaration order is preserved via entry order.
     /// Key memory is owned by `decl.name`.
     /// Anonymous decls are not stored here; they are kept in `anon_decls` instead.
-    decls: std.ArrayHashMapUnmanaged(*Decl, void, DeclContext, true) = .{},
+    decls: std.ArrayHashMapUnmanaged(Decl.Index, void, DeclContext, true) = .{},
 
-    anon_decls: std.AutoArrayHashMapUnmanaged(*Decl, void) = .{},
+    anon_decls: std.AutoArrayHashMapUnmanaged(Decl.Index, void) = .{},
 
     /// Key is usingnamespace Decl itself. To find the namespace being included,
     /// the Decl Value has to be resolved as a Type which has a Namespace.
     /// Value is whether the usingnamespace decl is marked `pub`.
-    usingnamespace_set: std.AutoHashMapUnmanaged(*Decl, bool) = .{},
+    usingnamespace_set: std.AutoHashMapUnmanaged(Decl.Index, bool) = .{},
 
     const DeclContext = struct {
-        pub fn hash(self: @This(), decl: *Decl) u32 {
-            _ = self;
+        module: *Module,
+
+        pub fn hash(ctx: @This(), decl_index: Decl.Index) u32 {
+            const decl = ctx.module.declPtr(decl_index);
             return @truncate(u32, std.hash.Wyhash.hash(0, mem.sliceTo(decl.name, 0)));
         }
 
-        pub fn eql(self: @This(), a: *Decl, b: *Decl, b_index: usize) bool {
-            _ = self;
+        pub fn eql(ctx: @This(), a_decl_index: Decl.Index, b_decl_index: Decl.Index, b_index: usize) bool {
             _ = b_index;
-            return mem.eql(u8, mem.sliceTo(a.name, 0), mem.sliceTo(b.name, 0));
+            const a_decl = ctx.module.declPtr(a_decl_index);
+            const b_decl = ctx.module.declPtr(b_decl_index);
+            const a_name = mem.sliceTo(a_decl.name, 0);
+            const b_name = mem.sliceTo(b_decl.name, 0);
+            return mem.eql(u8, a_name, b_name);
         }
     };
 
@@ -1637,13 +1633,13 @@ pub const Namespace = struct {
         var anon_decls = ns.anon_decls;
         ns.anon_decls = .{};
 
-        for (decls.keys()) |decl| {
-            decl.destroy(mod);
+        for (decls.keys()) |decl_index| {
+            mod.destroyDecl(decl_index);
         }
         decls.deinit(gpa);
 
         for (anon_decls.keys()) |key| {
-            key.destroy(mod);
+            mod.destroyDecl(key);
         }
         anon_decls.deinit(gpa);
         ns.usingnamespace_set.deinit(gpa);
@@ -1652,7 +1648,7 @@ pub const Namespace = struct {
     pub fn deleteAllDecls(
         ns: *Namespace,
         mod: *Module,
-        outdated_decls: ?*std.AutoArrayHashMap(*Decl, void),
+        outdated_decls: ?*std.AutoArrayHashMap(Decl.Index, void),
     ) !void {
         const gpa = mod.gpa;
 
@@ -1669,13 +1665,13 @@ pub const Namespace = struct {
 
         for (decls.keys()) |child_decl| {
             mod.clearDecl(child_decl, outdated_decls) catch @panic("out of memory");
-            child_decl.destroy(mod);
+            mod.destroyDecl(child_decl);
         }
         decls.deinit(gpa);
 
         for (anon_decls.keys()) |child_decl| {
             mod.clearDecl(child_decl, outdated_decls) catch @panic("out of memory");
-            child_decl.destroy(mod);
+            mod.destroyDecl(child_decl);
         }
         anon_decls.deinit(gpa);
 
@@ -1685,12 +1681,14 @@ pub const Namespace = struct {
     // This renders e.g. "std.fs.Dir.OpenOptions"
     pub fn renderFullyQualifiedName(
         ns: Namespace,
+        mod: *Module,
         name: []const u8,
         writer: anytype,
     ) @TypeOf(writer).Error!void {
         if (ns.parent) |parent| {
-            const decl = ns.getDecl();
-            try parent.renderFullyQualifiedName(mem.sliceTo(decl.name, 0), writer);
+            const decl_index = ns.getDeclIndex();
+            const decl = mod.declPtr(decl_index);
+            try parent.renderFullyQualifiedName(mod, mem.sliceTo(decl.name, 0), writer);
         } else {
             try ns.file_scope.renderFullyQualifiedName(writer);
         }
@@ -1703,13 +1701,15 @@ pub const Namespace = struct {
     /// This renders e.g. "std/fs.zig:Dir.OpenOptions"
     pub fn renderFullyQualifiedDebugName(
         ns: Namespace,
+        mod: *Module,
         name: []const u8,
         writer: anytype,
     ) @TypeOf(writer).Error!void {
         var separator_char: u8 = '.';
         if (ns.parent) |parent| {
-            const decl = ns.getDecl();
-            try parent.renderFullyQualifiedDebugName(mem.sliceTo(decl.name, 0), writer);
+            const decl_index = ns.getDeclIndex();
+            const decl = mod.declPtr(decl_index);
+            try parent.renderFullyQualifiedDebugName(mod, mem.sliceTo(decl.name, 0), writer);
         } else {
             try ns.file_scope.renderFullyQualifiedDebugName(writer);
             separator_char = ':';
@@ -1720,12 +1720,14 @@ pub const Namespace = struct {
         }
     }
 
-    pub fn getDecl(ns: Namespace) *Decl {
+    pub fn getDeclIndex(ns: Namespace) Decl.Index {
         return ns.ty.getOwnerDecl();
     }
 };
 
 pub const File = struct {
+    /// The Decl of the struct that represents this File.
+    root_decl: Decl.OptionalIndex,
     status: enum {
         never_loaded,
         retryable_failure,
@@ -1749,16 +1751,14 @@ pub const File = struct {
     zir: Zir,
     /// Package that this file is a part of, managed externally.
     pkg: *Package,
-    /// The Decl of the struct that represents this File.
-    root_decl: ?*Decl,
 
     /// Used by change detection algorithm, after astgen, contains the
     /// set of decls that existed in the previous ZIR but not in the new one.
-    deleted_decls: std.ArrayListUnmanaged(*Decl) = .{},
+    deleted_decls: std.ArrayListUnmanaged(Decl.Index) = .{},
     /// Used by change detection algorithm, after astgen, contains the
     /// set of decls that existed both in the previous ZIR and in the new one,
     /// but their source code has been modified.
-    outdated_decls: std.ArrayListUnmanaged(*Decl) = .{},
+    outdated_decls: std.ArrayListUnmanaged(Decl.Index) = .{},
 
     /// The most recent successful ZIR for this file, with no errors.
     /// This is only populated when a previously successful ZIR
@@ -1798,8 +1798,8 @@ pub const File = struct {
         log.debug("deinit File {s}", .{file.sub_file_path});
         file.deleted_decls.deinit(gpa);
         file.outdated_decls.deinit(gpa);
-        if (file.root_decl) |root_decl| {
-            root_decl.destroy(mod);
+        if (file.root_decl.unwrap()) |root_decl| {
+            mod.destroyDecl(root_decl);
         }
         gpa.free(file.sub_file_path);
         file.unload(gpa);
@@ -1932,7 +1932,7 @@ pub const EmbedFile = struct {
     /// The Decl that was created from the `@embedFile` to own this resource.
     /// This is how zig knows what other Decl objects to invalidate if the file
     /// changes on disk.
-    owner_decl: *Decl,
+    owner_decl: Decl.Index,
 
     fn destroy(embed_file: *EmbedFile, mod: *Module) void {
         const gpa = mod.gpa;
@@ -2776,6 +2776,7 @@ pub fn deinit(mod: *Module) void {
         }
         emit_h.failed_decls.deinit(gpa);
         emit_h.decl_table.deinit(gpa);
+        emit_h.allocated_emit_h.deinit(gpa);
         gpa.destroy(emit_h);
     }
 
@@ -2827,6 +2828,52 @@ pub fn deinit(mod: *Module) void {
         }
         mod.memoized_calls.deinit(gpa);
     }
+
+    mod.decls_free_list.deinit(gpa);
+    mod.allocated_decls.deinit(gpa);
+}
+
+pub fn destroyDecl(mod: *Module, decl_index: Decl.Index) void {
+    const gpa = mod.gpa;
+    {
+        const decl = mod.declPtr(decl_index);
+        log.debug("destroy {*} ({s})", .{ decl, decl.name });
+        _ = mod.test_functions.swapRemove(decl_index);
+        if (decl.deletion_flag) {
+            assert(mod.deletion_set.swapRemove(decl_index));
+        }
+        if (decl.has_tv) {
+            if (decl.getInnerNamespace()) |namespace| {
+                namespace.destroyDecls(mod);
+            }
+            decl.clearValues(gpa);
+        }
+        decl.dependants.deinit(gpa);
+        decl.dependencies.deinit(gpa);
+        decl.clearName(gpa);
+        decl.* = undefined;
+    }
+    mod.decls_free_list.append(gpa, decl_index) catch {
+        // In order to keep `destroyDecl` a non-fallible function, we ignore memory
+        // allocation failures here, instead leaking the Decl until garbage collection.
+    };
+    if (mod.emit_h) |mod_emit_h| {
+        const decl_emit_h = mod_emit_h.declPtr(decl_index);
+        decl_emit_h.fwd_decl.deinit(gpa);
+        decl_emit_h.* = undefined;
+    }
+}
+
+pub fn declPtr(mod: *Module, decl_index: Decl.Index) *Decl {
+    return mod.allocated_decls.at(@enumToInt(decl_index));
+}
+
+/// Returns true if and only if the Decl is the top level struct associated with a File.
+pub fn declIsRoot(mod: *Module, decl_index: Decl.Index) bool {
+    const decl = mod.declPtr(decl_index);
+    if (decl.src_namespace.parent != null)
+        return false;
+    return decl_index == decl.src_namespace.getDeclIndex();
 }
 
 fn freeExportList(gpa: Allocator, export_list: []*Export) void {
@@ -3230,14 +3277,14 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
         // We do not need to hold any locks at this time because all the Decl and Namespace
         // objects being touched are specific to this File, and the only other concurrent
         // tasks are touching other File objects.
-        try updateZirRefs(gpa, file, prev_zir.*);
+        try updateZirRefs(mod, file, prev_zir.*);
         // At this point, `file.outdated_decls` and `file.deleted_decls` are populated,
         // and semantic analysis will deal with them properly.
         // No need to keep previous ZIR.
         prev_zir.deinit(gpa);
         gpa.destroy(prev_zir);
         file.prev_zir = null;
-    } else if (file.root_decl) |root_decl| {
+    } else if (file.root_decl.unwrap()) |root_decl| {
         // This is an update, but it is the first time the File has succeeded
         // ZIR. We must mark it outdated since we have already tried to
         // semantically analyze it.
@@ -3251,7 +3298,8 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
 /// * Decl.zir_index
 /// * Fn.zir_body_inst
 /// * Decl.zir_decl_index
-fn updateZirRefs(gpa: Allocator, file: *File, old_zir: Zir) !void {
+fn updateZirRefs(mod: *Module, file: *File, old_zir: Zir) !void {
+    const gpa = mod.gpa;
     const new_zir = file.zir;
 
     // Maps from old ZIR to new ZIR, struct_decl, enum_decl, etc. Any instruction which
@@ -3268,10 +3316,10 @@ fn updateZirRefs(gpa: Allocator, file: *File, old_zir: Zir) !void {
     // Walk the Decl graph, updating ZIR indexes, strings, and populating
     // the deleted and outdated lists.
 
-    var decl_stack: std.ArrayListUnmanaged(*Decl) = .{};
+    var decl_stack: std.ArrayListUnmanaged(Decl.Index) = .{};
     defer decl_stack.deinit(gpa);
 
-    const root_decl = file.root_decl.?;
+    const root_decl = file.root_decl.unwrap().?;
     try decl_stack.append(gpa, root_decl);
 
     file.deleted_decls.clearRetainingCapacity();
@@ -3281,7 +3329,8 @@ fn updateZirRefs(gpa: Allocator, file: *File, old_zir: Zir) !void {
     // to re-generate ZIR for the File.
     try file.outdated_decls.append(gpa, root_decl);
 
-    while (decl_stack.popOrNull()) |decl| {
+    while (decl_stack.popOrNull()) |decl_index| {
+        const decl = mod.declPtr(decl_index);
         // Anonymous decls and the root decl have this set to 0. We still need
         // to walk them but we do not need to modify this value.
         // Anonymous decls should not be marked outdated. They will be re-generated
@@ -3292,7 +3341,7 @@ fn updateZirRefs(gpa: Allocator, file: *File, old_zir: Zir) !void {
                 log.debug("updateZirRefs {s}: delete {*} ({s})", .{
                     file.sub_file_path, decl, decl.name,
                 });
-                try file.deleted_decls.append(gpa, decl);
+                try file.deleted_decls.append(gpa, decl_index);
                 continue;
             };
             const old_hash = decl.contentsHashZir(old_zir);
@@ -3302,7 +3351,7 @@ fn updateZirRefs(gpa: Allocator, file: *File, old_zir: Zir) !void {
                 log.debug("updateZirRefs {s}: outdated {*} ({s}) {d} => {d}", .{
                     file.sub_file_path, decl, decl.name, old_zir_decl_index, new_zir_decl_index,
                 });
-                try file.outdated_decls.append(gpa, decl);
+                try file.outdated_decls.append(gpa, decl_index);
             } else {
                 log.debug("updateZirRefs {s}: unchanged {*} ({s}) {d} => {d}", .{
                     file.sub_file_path, decl, decl.name, old_zir_decl_index, new_zir_decl_index,
@@ -3314,21 +3363,21 @@ fn updateZirRefs(gpa: Allocator, file: *File, old_zir: Zir) !void {
 
         if (decl.getStruct()) |struct_obj| {
             struct_obj.zir_index = inst_map.get(struct_obj.zir_index) orelse {
-                try file.deleted_decls.append(gpa, decl);
+                try file.deleted_decls.append(gpa, decl_index);
                 continue;
             };
         }
 
         if (decl.getUnion()) |union_obj| {
             union_obj.zir_index = inst_map.get(union_obj.zir_index) orelse {
-                try file.deleted_decls.append(gpa, decl);
+                try file.deleted_decls.append(gpa, decl_index);
                 continue;
             };
         }
 
         if (decl.getFunction()) |func| {
             func.zir_body_inst = inst_map.get(func.zir_body_inst) orelse {
-                try file.deleted_decls.append(gpa, decl);
+                try file.deleted_decls.append(gpa, decl_index);
                 continue;
             };
         }
@@ -3485,10 +3534,12 @@ pub fn mapOldZirToNew(
 /// However the resolution status of the Type may not be fully resolved.
 /// For example an inferred error set is not resolved until after `analyzeFnBody`.
 /// is called.
-pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) SemaError!void {
+pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void {
     const tracy = trace(@src());
     defer tracy.end();
 
+    const decl = mod.declPtr(decl_index);
+
     const subsequent_analysis = switch (decl.analysis) {
         .in_progress => unreachable,
 
@@ -3507,15 +3558,16 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) SemaError!void {
 
             // The exports this Decl performs will be re-discovered, so we remove them here
             // prior to re-analysis.
-            mod.deleteDeclExports(decl);
+            mod.deleteDeclExports(decl_index);
             // Dependencies will be re-discovered, so we remove them here prior to re-analysis.
-            for (decl.dependencies.keys()) |dep| {
-                dep.removeDependant(decl);
+            for (decl.dependencies.keys()) |dep_index| {
+                const dep = mod.declPtr(dep_index);
+                dep.removeDependant(decl_index);
                 if (dep.dependants.count() == 0 and !dep.deletion_flag) {
                     log.debug("insert {*} ({s}) dependant {*} ({s}) into deletion set", .{
                         decl, decl.name, dep, dep.name,
                     });
-                    try mod.markDeclForDeletion(dep);
+                    try mod.markDeclForDeletion(dep_index);
                 }
             }
             decl.dependencies.clearRetainingCapacity();
@@ -3530,7 +3582,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) SemaError!void {
     decl_prog_node.activate();
     defer decl_prog_node.end();
 
-    const type_changed = mod.semaDecl(decl) catch |err| switch (err) {
+    const type_changed = mod.semaDecl(decl_index) catch |err| switch (err) {
         error.AnalysisFail => {
             if (decl.analysis == .in_progress) {
                 // If this decl caused the compile error, the analysis field would
@@ -3545,7 +3597,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) SemaError!void {
         else => |e| {
             decl.analysis = .sema_failure_retryable;
             try mod.failed_decls.ensureUnusedCapacity(mod.gpa, 1);
-            mod.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
+            mod.failed_decls.putAssumeCapacityNoClobber(decl_index, try ErrorMsg.create(
                 mod.gpa,
                 decl.srcLoc(),
                 "unable to analyze: {s}",
@@ -3559,7 +3611,8 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) SemaError!void {
         // We may need to chase the dependants and re-analyze them.
         // However, if the decl is a function, and the type is the same, we do not need to.
         if (type_changed or decl.ty.zigTypeTag() != .Fn) {
-            for (decl.dependants.keys()) |dep| {
+            for (decl.dependants.keys()) |dep_index| {
+                const dep = mod.declPtr(dep_index);
                 switch (dep.analysis) {
                     .unreferenced => unreachable,
                     .in_progress => continue, // already doing analysis, ok
@@ -3573,7 +3626,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) SemaError!void {
                     .codegen_failure_retryable,
                     .complete,
                     => if (dep.generation != mod.generation) {
-                        try mod.markOutdatedDecl(dep);
+                        try mod.markOutdatedDecl(dep_index);
                     },
                 }
             }
@@ -3585,7 +3638,10 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func: *Fn) SemaError!void {
     const tracy = trace(@src());
     defer tracy.end();
 
-    switch (func.owner_decl.analysis) {
+    const decl_index = func.owner_decl;
+    const decl = mod.declPtr(decl_index);
+
+    switch (decl.analysis) {
         .unreferenced => unreachable,
         .in_progress => unreachable,
         .outdated => unreachable,
@@ -3607,13 +3663,12 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func: *Fn) SemaError!void {
             }
 
             const gpa = mod.gpa;
-            const decl = func.owner_decl;
 
             var tmp_arena = std.heap.ArenaAllocator.init(gpa);
             defer tmp_arena.deinit();
             const sema_arena = tmp_arena.allocator();
 
-            var air = mod.analyzeFnBody(decl, func, sema_arena) catch |err| switch (err) {
+            var air = mod.analyzeFnBody(func, sema_arena) catch |err| switch (err) {
                 error.AnalysisFail => {
                     if (func.state == .in_progress) {
                         // If this decl caused the compile error, the analysis field would
@@ -3635,7 +3690,7 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func: *Fn) SemaError!void {
 
             if (builtin.mode == .Debug and mod.comp.verbose_air) {
                 std.debug.print("# Begin Function AIR: {s}:\n", .{decl.name});
-                @import("print_air.zig").dump(gpa, air, liveness);
+                @import("print_air.zig").dump(mod, air, liveness);
                 std.debug.print("# End Function AIR: {s}\n\n", .{decl.name});
             }
 
@@ -3647,7 +3702,7 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func: *Fn) SemaError!void {
                 },
                 else => {
                     try mod.failed_decls.ensureUnusedCapacity(gpa, 1);
-                    mod.failed_decls.putAssumeCapacityNoClobber(decl, try Module.ErrorMsg.create(
+                    mod.failed_decls.putAssumeCapacityNoClobber(decl_index, try Module.ErrorMsg.create(
                         gpa,
                         decl.srcLoc(),
                         "unable to codegen: {s}",
@@ -3668,7 +3723,9 @@ pub fn updateEmbedFile(mod: *Module, embed_file: *EmbedFile) SemaError!void {
 
     // TODO we can potentially relax this if we store some more information along
     // with decl dependency edges
-    for (embed_file.owner_decl.dependants.keys()) |dep| {
+    const owner_decl = mod.declPtr(embed_file.owner_decl);
+    for (owner_decl.dependants.keys()) |dep_index| {
+        const dep = mod.declPtr(dep_index);
         switch (dep.analysis) {
             .unreferenced => unreachable,
             .in_progress => continue, // already doing analysis, ok
@@ -3682,7 +3739,7 @@ pub fn updateEmbedFile(mod: *Module, embed_file: *EmbedFile) SemaError!void {
             .codegen_failure_retryable,
             .complete,
             => if (dep.generation != mod.generation) {
-                try mod.markOutdatedDecl(dep);
+                try mod.markOutdatedDecl(dep_index);
             },
         }
     }
@@ -3699,7 +3756,7 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void {
     const tracy = trace(@src());
     defer tracy.end();
 
-    if (file.root_decl != null) return;
+    if (file.root_decl != .none) return;
 
     const gpa = mod.gpa;
     var new_decl_arena = std.heap.ArenaAllocator.init(gpa);
@@ -3724,10 +3781,11 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void {
             .file_scope = file,
         },
     };
-    const decl_name = try file.fullyQualifiedNameZ(gpa);
-    const new_decl = try mod.allocateNewDecl(decl_name, &struct_obj.namespace, 0, null);
-    file.root_decl = new_decl;
-    struct_obj.owner_decl = new_decl;
+    const new_decl_index = try mod.allocateNewDecl(&struct_obj.namespace, 0, null);
+    const new_decl = mod.declPtr(new_decl_index);
+    file.root_decl = new_decl_index.toOptional();
+    struct_obj.owner_decl = new_decl_index;
+    new_decl.name = try file.fullyQualifiedNameZ(gpa);
     new_decl.src_line = 0;
     new_decl.is_pub = true;
     new_decl.is_exported = false;
@@ -3757,6 +3815,7 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void {
             .perm_arena = new_decl_arena_allocator,
             .code = file.zir,
             .owner_decl = new_decl,
+            .owner_decl_index = new_decl_index,
             .func = null,
             .fn_ret_ty = Type.void,
             .owner_func = null,
@@ -3769,7 +3828,7 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void {
         var block_scope: Sema.Block = .{
             .parent = null,
             .sema = &sema,
-            .src_decl = new_decl,
+            .src_decl = new_decl_index,
             .namespace = &struct_obj.namespace,
             .wip_capture_scope = wip_captures.scope,
             .instructions = .{},
@@ -3808,10 +3867,12 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void {
 /// Returns `true` if the Decl type changed.
 /// Returns `true` if this is the first time analyzing the Decl.
 /// Returns `false` otherwise.
-fn semaDecl(mod: *Module, decl: *Decl) !bool {
+fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
     const tracy = trace(@src());
     defer tracy.end();
 
+    const decl = mod.declPtr(decl_index);
+
     if (decl.getFileScope().status != .success_zir) {
         return error.AnalysisFail;
     }
@@ -3838,13 +3899,14 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
         .perm_arena = decl_arena_allocator,
         .code = zir,
         .owner_decl = decl,
+        .owner_decl_index = decl_index,
         .func = null,
         .fn_ret_ty = Type.void,
         .owner_func = null,
     };
     defer sema.deinit();
 
-    if (decl.isRoot()) {
+    if (mod.declIsRoot(decl_index)) {
         log.debug("semaDecl root {*} ({s})", .{ decl, decl.name });
         const main_struct_inst = Zir.main_struct_inst;
         const struct_obj = decl.getStruct().?;
@@ -3864,7 +3926,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
     var block_scope: Sema.Block = .{
         .parent = null,
         .sema = &sema,
-        .src_decl = decl,
+        .src_decl = decl_index,
         .namespace = decl.src_namespace,
         .wip_capture_scope = wip_captures.scope,
         .instructions = .{},
@@ -3922,15 +3984,15 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
     const decl_arena_state = try decl_arena_allocator.create(std.heap.ArenaAllocator.State);
 
     if (decl.is_usingnamespace) {
-        if (!decl_tv.ty.eql(Type.type, target)) {
+        if (!decl_tv.ty.eql(Type.type, mod)) {
             return sema.fail(&block_scope, src, "expected type, found {}", .{
-                decl_tv.ty.fmt(target),
+                decl_tv.ty.fmt(mod),
             });
         }
         var buffer: Value.ToTypeBuffer = undefined;
         const ty = try decl_tv.val.toType(&buffer).copy(decl_arena_allocator);
         if (ty.getNamespace() == null) {
-            return sema.fail(&block_scope, src, "type {} has no namespace", .{ty.fmt(target)});
+            return sema.fail(&block_scope, src, "type {} has no namespace", .{ty.fmt(mod)});
         }
 
         decl.ty = Type.type;
@@ -3949,7 +4011,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
 
     if (decl_tv.val.castTag(.function)) |fn_payload| {
         const func = fn_payload.data;
-        const owns_tv = func.owner_decl == decl;
+        const owns_tv = func.owner_decl == decl_index;
         if (owns_tv) {
             var prev_type_has_bits = false;
             var prev_is_inline = false;
@@ -3957,7 +4019,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
 
             if (decl.has_tv) {
                 prev_type_has_bits = decl.ty.isFnOrHasRuntimeBits();
-                type_changed = !decl.ty.eql(decl_tv.ty, target);
+                type_changed = !decl.ty.eql(decl_tv.ty, mod);
                 if (decl.getFunction()) |prev_func| {
                     prev_is_inline = prev_func.state == .inline_only;
                 }
@@ -3982,13 +4044,13 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
                 // We don't fully codegen the decl until later, but we do need to reserve a global
                 // offset table index for it. This allows us to codegen decls out of dependency
                 // order, increasing how many computations can be done in parallel.
-                try mod.comp.bin_file.allocateDeclIndexes(decl);
+                try mod.comp.bin_file.allocateDeclIndexes(decl_index);
                 try mod.comp.work_queue.writeItem(.{ .codegen_func = func });
                 if (type_changed and mod.emit_h != null) {
-                    try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl });
+                    try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl_index });
                 }
             } else if (!prev_is_inline and prev_type_has_bits) {
-                mod.comp.bin_file.freeDecl(decl);
+                mod.comp.bin_file.freeDecl(decl_index);
             }
 
             const is_inline = decl.ty.fnCallingConvention() == .Inline;
@@ -3999,14 +4061,14 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
                 }
                 // The scope needs to have the decl in it.
                 const options: std.builtin.ExportOptions = .{ .name = mem.sliceTo(decl.name, 0) };
-                try sema.analyzeExport(&block_scope, export_src, options, decl);
+                try sema.analyzeExport(&block_scope, export_src, options, decl_index);
             }
             return type_changed or is_inline != prev_is_inline;
         }
     }
     var type_changed = true;
     if (decl.has_tv) {
-        type_changed = !decl.ty.eql(decl_tv.ty, target);
+        type_changed = !decl.ty.eql(decl_tv.ty, mod);
         decl.clearValues(gpa);
     }
 
@@ -4016,7 +4078,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
     switch (decl_tv.val.tag()) {
         .variable => {
             const variable = decl_tv.val.castTag(.variable).?.data;
-            if (variable.owner_decl == decl) {
+            if (variable.owner_decl == decl_index) {
                 decl.owns_tv = true;
                 queue_linker_work = true;
 
@@ -4026,7 +4088,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
         },
         .extern_fn => {
             const extern_fn = decl_tv.val.castTag(.extern_fn).?.data;
-            if (extern_fn.owner_decl == decl) {
+            if (extern_fn.owner_decl == decl_index) {
                 decl.owns_tv = true;
                 queue_linker_work = true;
                 is_extern = true;
@@ -4065,11 +4127,11 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
         // codegen backend wants full access to the Decl Type.
         try sema.resolveTypeFully(&block_scope, src, decl.ty);
 
-        try mod.comp.bin_file.allocateDeclIndexes(decl);
-        try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl });
+        try mod.comp.bin_file.allocateDeclIndexes(decl_index);
+        try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl_index });
 
         if (type_changed and mod.emit_h != null) {
-            try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl });
+            try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl_index });
         }
     }
 
@@ -4077,15 +4139,18 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
         const export_src = src; // TODO point to the export token
         // The scope needs to have the decl in it.
         const options: std.builtin.ExportOptions = .{ .name = mem.sliceTo(decl.name, 0) };
-        try sema.analyzeExport(&block_scope, export_src, options, decl);
+        try sema.analyzeExport(&block_scope, export_src, options, decl_index);
     }
 
     return type_changed;
 }
 
 /// Returns the depender's index of the dependee.
-pub fn declareDeclDependency(mod: *Module, depender: *Decl, dependee: *Decl) !void {
-    if (depender == dependee) return;
+pub fn declareDeclDependency(mod: *Module, depender_index: Decl.Index, dependee_index: Decl.Index) !void {
+    if (depender_index == dependee_index) return;
+
+    const depender = mod.declPtr(depender_index);
+    const dependee = mod.declPtr(dependee_index);
 
     log.debug("{*} ({s}) depends on {*} ({s})", .{
         depender, depender.name, dependee, dependee.name,
@@ -4096,11 +4161,11 @@ pub fn declareDeclDependency(mod: *Module, depender: *Decl, dependee: *Decl) !vo
 
     if (dependee.deletion_flag) {
         dependee.deletion_flag = false;
-        assert(mod.deletion_set.swapRemove(dependee));
+        assert(mod.deletion_set.swapRemove(dependee_index));
     }
 
-    dependee.dependants.putAssumeCapacity(depender, {});
-    depender.dependencies.putAssumeCapacity(dependee, {});
+    dependee.dependants.putAssumeCapacity(depender_index, {});
+    depender.dependencies.putAssumeCapacity(dependee_index, {});
 }
 
 pub const ImportFileResult = struct {
@@ -4146,7 +4211,7 @@ pub fn importPkg(mod: *Module, pkg: *Package) !ImportFileResult {
         .zir = undefined,
         .status = .never_loaded,
         .pkg = pkg,
-        .root_decl = null,
+        .root_decl = .none,
     };
     return ImportFileResult{
         .file = new_file,
@@ -4214,7 +4279,7 @@ pub fn importFile(
         .zir = undefined,
         .status = .never_loaded,
         .pkg = cur_file.pkg,
-        .root_decl = null,
+        .root_decl = .none,
     };
     return ImportFileResult{
         .file = new_file,
@@ -4388,8 +4453,8 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi
     const line = iter.parent_decl.relativeToLine(line_off);
     const decl_name_index = zir.extra[decl_sub_index + 5];
     const decl_doccomment_index = zir.extra[decl_sub_index + 7];
-    const decl_index = zir.extra[decl_sub_index + 6];
-    const decl_block_inst_data = zir.instructions.items(.data)[decl_index].pl_node;
+    const decl_zir_index = zir.extra[decl_sub_index + 6];
+    const decl_block_inst_data = zir.instructions.items(.data)[decl_zir_index].pl_node;
     const decl_node = iter.parent_decl.relativeToNodeIndex(decl_block_inst_data.src_node);
 
     // Every Decl needs a name.
@@ -4432,15 +4497,22 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi
     if (is_usingnamespace) try namespace.usingnamespace_set.ensureUnusedCapacity(gpa, 1);
 
     // We create a Decl for it regardless of analysis status.
-    const gop = try namespace.decls.getOrPutAdapted(gpa, @as([]const u8, mem.sliceTo(decl_name, 0)), DeclAdapter{});
+    const gop = try namespace.decls.getOrPutContextAdapted(
+        gpa,
+        @as([]const u8, mem.sliceTo(decl_name, 0)),
+        DeclAdapter{ .mod = mod },
+        Namespace.DeclContext{ .module = mod },
+    );
     if (!gop.found_existing) {
-        const new_decl = try mod.allocateNewDecl(decl_name, namespace, decl_node, iter.parent_decl.src_scope);
+        const new_decl_index = try mod.allocateNewDecl(namespace, decl_node, iter.parent_decl.src_scope);
+        const new_decl = mod.declPtr(new_decl_index);
+        new_decl.name = decl_name;
         if (is_usingnamespace) {
-            namespace.usingnamespace_set.putAssumeCapacity(new_decl, is_pub);
+            namespace.usingnamespace_set.putAssumeCapacity(new_decl_index, is_pub);
         }
         log.debug("scan new {*} ({s}) into {*}", .{ new_decl, decl_name, namespace });
         new_decl.src_line = line;
-        gop.key_ptr.* = new_decl;
+        gop.key_ptr.* = new_decl_index;
         // Exported decls, comptime decls, usingnamespace decls, and
         // test decls if in test mode, get analyzed.
         const decl_pkg = namespace.file_scope.pkg;
@@ -4451,7 +4523,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi
                 // the test name filter.
                 if (!mod.comp.bin_file.options.is_test) break :blk false;
                 if (decl_pkg != mod.main_pkg) break :blk false;
-                try mod.test_functions.put(gpa, new_decl, {});
+                try mod.test_functions.put(gpa, new_decl_index, {});
                 break :blk true;
             },
             else => blk: {
@@ -4459,12 +4531,12 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi
                 if (!mod.comp.bin_file.options.is_test) break :blk false;
                 if (decl_pkg != mod.main_pkg) break :blk false;
                 // TODO check the name against --test-filter
-                try mod.test_functions.put(gpa, new_decl, {});
+                try mod.test_functions.put(gpa, new_decl_index, {});
                 break :blk true;
             },
         };
         if (want_analysis) {
-            mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl });
+            mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl_index });
         }
         new_decl.is_pub = is_pub;
         new_decl.is_exported = is_exported;
@@ -4476,7 +4548,8 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi
         return;
     }
     gpa.free(decl_name);
-    const decl = gop.key_ptr.*;
+    const decl_index = gop.key_ptr.*;
+    const decl = mod.declPtr(decl_index);
     log.debug("scan existing {*} ({s}) of {*}", .{ decl, decl.name, namespace });
     // Update the AST node of the decl; even if its contents are unchanged, it may
     // have been re-ordered.
@@ -4497,17 +4570,17 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi
             .elf => if (decl.fn_link.elf.len != 0) {
                 // TODO Look into detecting when this would be unnecessary by storing enough state
                 // in `Decl` to notice that the line number did not change.
-                mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
+                mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index });
             },
             .macho => if (decl.fn_link.macho.len != 0) {
                 // TODO Look into detecting when this would be unnecessary by storing enough state
                 // in `Decl` to notice that the line number did not change.
-                mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
+                mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index });
             },
             .plan9 => {
                 // TODO Look into detecting when this would be unnecessary by storing enough state
                 // in `Decl` to notice that the line number did not change.
-                mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
+                mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index });
             },
             .c, .wasm, .spirv, .nvptx => {},
         }
@@ -4517,25 +4590,27 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi
 /// Make it as if the semantic analysis for this Decl never happened.
 pub fn clearDecl(
     mod: *Module,
-    decl: *Decl,
-    outdated_decls: ?*std.AutoArrayHashMap(*Decl, void),
+    decl_index: Decl.Index,
+    outdated_decls: ?*std.AutoArrayHashMap(Decl.Index, void),
 ) Allocator.Error!void {
     const tracy = trace(@src());
     defer tracy.end();
 
+    const decl = mod.declPtr(decl_index);
     log.debug("clearing {*} ({s})", .{ decl, decl.name });
 
     const gpa = mod.gpa;
     try mod.deletion_set.ensureUnusedCapacity(gpa, decl.dependencies.count());
 
     if (outdated_decls) |map| {
-        _ = map.swapRemove(decl);
+        _ = map.swapRemove(decl_index);
         try map.ensureUnusedCapacity(decl.dependants.count());
     }
 
     // Remove itself from its dependencies.
-    for (decl.dependencies.keys()) |dep| {
-        dep.removeDependant(decl);
+    for (decl.dependencies.keys()) |dep_index| {
+        const dep = mod.declPtr(dep_index);
+        dep.removeDependant(decl_index);
         if (dep.dependants.count() == 0 and !dep.deletion_flag) {
             log.debug("insert {*} ({s}) dependant {*} ({s}) into deletion set", .{
                 decl, decl.name, dep, dep.name,
@@ -4543,35 +4618,36 @@ pub fn clearDecl(
             // We don't recursively perform a deletion here, because during the update,
             // another reference to it may turn up.
             dep.deletion_flag = true;
-            mod.deletion_set.putAssumeCapacity(dep, {});
+            mod.deletion_set.putAssumeCapacity(dep_index, {});
         }
     }
     decl.dependencies.clearRetainingCapacity();
 
     // Anything that depends on this deleted decl needs to be re-analyzed.
-    for (decl.dependants.keys()) |dep| {
-        dep.removeDependency(decl);
+    for (decl.dependants.keys()) |dep_index| {
+        const dep = mod.declPtr(dep_index);
+        dep.removeDependency(decl_index);
         if (outdated_decls) |map| {
-            map.putAssumeCapacity(dep, {});
+            map.putAssumeCapacity(dep_index, {});
         }
     }
     decl.dependants.clearRetainingCapacity();
 
-    if (mod.failed_decls.fetchSwapRemove(decl)) |kv| {
+    if (mod.failed_decls.fetchSwapRemove(decl_index)) |kv| {
         kv.value.destroy(gpa);
     }
     if (mod.emit_h) |emit_h| {
-        if (emit_h.failed_decls.fetchSwapRemove(decl)) |kv| {
+        if (emit_h.failed_decls.fetchSwapRemove(decl_index)) |kv| {
             kv.value.destroy(gpa);
         }
-        assert(emit_h.decl_table.swapRemove(decl));
+        assert(emit_h.decl_table.swapRemove(decl_index));
     }
-    _ = mod.compile_log_decls.swapRemove(decl);
-    mod.deleteDeclExports(decl);
+    _ = mod.compile_log_decls.swapRemove(decl_index);
+    mod.deleteDeclExports(decl_index);
 
     if (decl.has_tv) {
         if (decl.ty.isFnOrHasRuntimeBits()) {
-            mod.comp.bin_file.freeDecl(decl);
+            mod.comp.bin_file.freeDecl(decl_index);
 
             // TODO instead of a union, put this memory trailing Decl objects,
             // and allow it to be variably sized.
@@ -4604,15 +4680,16 @@ pub fn clearDecl(
 
     if (decl.deletion_flag) {
         decl.deletion_flag = false;
-        assert(mod.deletion_set.swapRemove(decl));
+        assert(mod.deletion_set.swapRemove(decl_index));
     }
 
     decl.analysis = .unreferenced;
 }
 
 /// This function is exclusively called for anonymous decls.
-pub fn deleteUnusedDecl(mod: *Module, decl: *Decl) void {
-    log.debug("deleteUnusedDecl {*} ({s})", .{ decl, decl.name });
+pub fn deleteUnusedDecl(mod: *Module, decl_index: Decl.Index) void {
+    const decl = mod.declPtr(decl_index);
+    log.debug("deleteUnusedDecl {d} ({s})", .{ decl_index, decl.name });
 
     // TODO: remove `allocateDeclIndexes` and make the API that the linker backends
     // are required to notice the first time `updateDecl` happens and keep track
@@ -4626,55 +4703,58 @@ pub fn deleteUnusedDecl(mod: *Module, decl: *Decl) void {
         .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);
+                mod.comp.bin_file.freeDecl(decl_index);
             }
         },
     }
 
-    assert(!decl.isRoot());
-    assert(decl.src_namespace.anon_decls.swapRemove(decl));
+    assert(!mod.declIsRoot(decl_index));
+    assert(decl.src_namespace.anon_decls.swapRemove(decl_index));
 
     const dependants = decl.dependants.keys();
     for (dependants) |dep| {
-        dep.removeDependency(decl);
+        mod.declPtr(dep).removeDependency(decl_index);
     }
 
     for (decl.dependencies.keys()) |dep| {
-        dep.removeDependant(decl);
+        mod.declPtr(dep).removeDependant(decl_index);
     }
-    decl.destroy(mod);
+    mod.destroyDecl(decl_index);
 }
 
 /// We don't perform a deletion here, because this Decl or another one
 /// may end up referencing it before the update is complete.
-fn markDeclForDeletion(mod: *Module, decl: *Decl) !void {
+fn markDeclForDeletion(mod: *Module, decl_index: Decl.Index) !void {
+    const decl = mod.declPtr(decl_index);
     decl.deletion_flag = true;
-    try mod.deletion_set.put(mod.gpa, decl, {});
+    try mod.deletion_set.put(mod.gpa, decl_index, {});
 }
 
 /// Cancel the creation of an anon decl and delete any references to it.
 /// If other decls depend on this decl, they must be aborted first.
-pub fn abortAnonDecl(mod: *Module, decl: *Decl) void {
+pub fn abortAnonDecl(mod: *Module, decl_index: Decl.Index) void {
+    const decl = mod.declPtr(decl_index);
     log.debug("abortAnonDecl {*} ({s})", .{ decl, decl.name });
 
-    assert(!decl.isRoot());
-    assert(decl.src_namespace.anon_decls.swapRemove(decl));
+    assert(!mod.declIsRoot(decl_index));
+    assert(decl.src_namespace.anon_decls.swapRemove(decl_index));
 
     // An aborted decl must not have dependants -- they must have
     // been aborted first and removed from this list.
     assert(decl.dependants.count() == 0);
 
-    for (decl.dependencies.keys()) |dep| {
-        dep.removeDependant(decl);
+    for (decl.dependencies.keys()) |dep_index| {
+        const dep = mod.declPtr(dep_index);
+        dep.removeDependant(decl_index);
     }
 
-    decl.destroy(mod);
+    mod.destroyDecl(decl_index);
 }
 
 /// Delete all the Export objects that are caused by this Decl. Re-analysis of
 /// this Decl will cause them to be re-created (or not).
-fn deleteDeclExports(mod: *Module, decl: *Decl) void {
-    const kv = mod.export_owners.fetchSwapRemove(decl) orelse return;
+fn deleteDeclExports(mod: *Module, decl_index: Decl.Index) void {
+    const kv = mod.export_owners.fetchSwapRemove(decl_index) orelse return;
 
     for (kv.value) |exp| {
         if (mod.decl_exports.getPtr(exp.exported_decl)) |value_ptr| {
@@ -4683,7 +4763,7 @@ fn deleteDeclExports(mod: *Module, decl: *Decl) void {
             var i: usize = 0;
             var new_len = list.len;
             while (i < new_len) {
-                if (list[i].owner_decl == decl) {
+                if (list[i].owner_decl == decl_index) {
                     mem.copyBackwards(*Export, list[i..], list[i + 1 .. new_len]);
                     new_len -= 1;
                 } else {
@@ -4713,11 +4793,13 @@ fn deleteDeclExports(mod: *Module, decl: *Decl) void {
     mod.gpa.free(kv.value);
 }
 
-pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn, arena: Allocator) SemaError!Air {
+pub fn analyzeFnBody(mod: *Module, func: *Fn, arena: Allocator) SemaError!Air {
     const tracy = trace(@src());
     defer tracy.end();
 
     const gpa = mod.gpa;
+    const decl_index = func.owner_decl;
+    const decl = mod.declPtr(decl_index);
 
     // Use the Decl's arena for captured values.
     var decl_arena = decl.value_arena.?.promote(gpa);
@@ -4731,8 +4813,9 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn, arena: Allocator) Sem
         .perm_arena = decl_arena_allocator,
         .code = decl.getFileScope().zir,
         .owner_decl = decl,
+        .owner_decl_index = decl_index,
         .func = func,
-        .fn_ret_ty = func.owner_decl.ty.fnReturnType(),
+        .fn_ret_ty = decl.ty.fnReturnType(),
         .owner_func = func,
     };
     defer sema.deinit();
@@ -4748,7 +4831,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn, arena: Allocator) Sem
     var inner_block: Sema.Block = .{
         .parent = null,
         .sema = &sema,
-        .src_decl = decl,
+        .src_decl = decl_index,
         .namespace = decl.src_namespace,
         .wip_capture_scope = wip_captures.scope,
         .instructions = .{},
@@ -4903,10 +4986,11 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn, arena: Allocator) Sem
     };
 }
 
-fn markOutdatedDecl(mod: *Module, decl: *Decl) !void {
+fn markOutdatedDecl(mod: *Module, decl_index: Decl.Index) !void {
+    const decl = mod.declPtr(decl_index);
     log.debug("mark outdated {*} ({s})", .{ decl, decl.name });
-    try mod.comp.work_queue.writeItem(.{ .analyze_decl = decl });
-    if (mod.failed_decls.fetchSwapRemove(decl)) |kv| {
+    try mod.comp.work_queue.writeItem(.{ .analyze_decl = decl_index });
+    if (mod.failed_decls.fetchSwapRemove(decl_index)) |kv| {
         kv.value.destroy(mod.gpa);
     }
     if (decl.has_tv and decl.owns_tv) {
@@ -4916,33 +5000,43 @@ fn markOutdatedDecl(mod: *Module, decl: *Decl) !void {
         }
     }
     if (mod.emit_h) |emit_h| {
-        if (emit_h.failed_decls.fetchSwapRemove(decl)) |kv| {
+        if (emit_h.failed_decls.fetchSwapRemove(decl_index)) |kv| {
             kv.value.destroy(mod.gpa);
         }
     }
-    _ = mod.compile_log_decls.swapRemove(decl);
+    _ = mod.compile_log_decls.swapRemove(decl_index);
     decl.analysis = .outdated;
 }
 
 pub fn allocateNewDecl(
     mod: *Module,
-    name: [:0]const u8,
     namespace: *Namespace,
     src_node: Ast.Node.Index,
     src_scope: ?*CaptureScope,
-) !*Decl {
-    // If we have emit-h then we must allocate a bigger structure to store the emit-h state.
-    const new_decl: *Decl = if (mod.emit_h != null) blk: {
-        const parent_struct = try mod.gpa.create(DeclPlusEmitH);
-        parent_struct.* = .{
-            .emit_h = .{},
-            .decl = undefined,
+) !Decl.Index {
+    const decl_and_index: struct {
+        new_decl: *Decl,
+        decl_index: Decl.Index,
+    } = if (mod.decls_free_list.popOrNull()) |decl_index| d: {
+        break :d .{
+            .new_decl = mod.declPtr(decl_index),
+            .decl_index = decl_index,
+        };
+    } else d: {
+        const decl = try mod.allocated_decls.addOne(mod.gpa);
+        errdefer mod.allocated_decls.shrinkRetainingCapacity(mod.allocated_decls.len - 1);
+        if (mod.emit_h) |mod_emit_h| {
+            const decl_emit_h = try mod_emit_h.allocated_emit_h.addOne(mod.gpa);
+            decl_emit_h.* = .{};
+        }
+        break :d .{
+            .new_decl = decl,
+            .decl_index = @intToEnum(Decl.Index, mod.allocated_decls.len - 1),
         };
-        break :blk &parent_struct.decl;
-    } else try mod.gpa.create(Decl);
+    };
 
-    new_decl.* = .{
-        .name = name,
+    decl_and_index.new_decl.* = .{
+        .name = undefined,
         .src_namespace = namespace,
         .src_node = src_node,
         .src_line = undefined,
@@ -4986,7 +5080,7 @@ pub fn allocateNewDecl(
         .is_usingnamespace = false,
     };
 
-    return new_decl;
+    return decl_and_index.decl_index;
 }
 
 /// Get error value for error tag `name`.
@@ -5010,18 +5104,9 @@ pub fn getErrorValue(mod: *Module, name: []const u8) !std.StringHashMapUnmanaged
     };
 }
 
-/// Takes ownership of `name` even if it returns an error.
-pub fn createAnonymousDeclNamed(
-    mod: *Module,
-    block: *Sema.Block,
-    typed_value: TypedValue,
-    name: [:0]u8,
-) !*Decl {
-    return mod.createAnonymousDeclFromDeclNamed(block.src_decl, block.namespace, block.wip_capture_scope, typed_value, name);
-}
-
-pub fn createAnonymousDecl(mod: *Module, block: *Sema.Block, typed_value: TypedValue) !*Decl {
-    return mod.createAnonymousDeclFromDecl(block.src_decl, block.namespace, block.wip_capture_scope, typed_value);
+pub fn createAnonymousDecl(mod: *Module, block: *Sema.Block, typed_value: TypedValue) !Decl.Index {
+    const src_decl = mod.declPtr(block.src_decl);
+    return mod.createAnonymousDeclFromDecl(src_decl, block.namespace, block.wip_capture_scope, typed_value);
 }
 
 pub fn createAnonymousDeclFromDecl(
@@ -5030,30 +5115,31 @@ pub fn createAnonymousDeclFromDecl(
     namespace: *Namespace,
     src_scope: ?*CaptureScope,
     tv: TypedValue,
-) !*Decl {
-    const name_index = mod.getNextAnonNameIndex();
+) !Decl.Index {
+    const new_decl_index = try mod.allocateNewDecl(namespace, src_decl.src_node, src_scope);
+    errdefer mod.destroyDecl(new_decl_index);
     const name = try std.fmt.allocPrintZ(mod.gpa, "{s}__anon_{d}", .{
-        src_decl.name, name_index,
+        src_decl.name, @enumToInt(new_decl_index),
     });
-    return mod.createAnonymousDeclFromDeclNamed(src_decl, namespace, src_scope, tv, name);
+    try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, namespace, tv, name);
+    return new_decl_index;
 }
 
 /// Takes ownership of `name` even if it returns an error.
-pub fn createAnonymousDeclFromDeclNamed(
+pub fn initNewAnonDecl(
     mod: *Module,
-    src_decl: *Decl,
+    new_decl_index: Decl.Index,
+    src_line: u32,
     namespace: *Namespace,
-    src_scope: ?*CaptureScope,
     typed_value: TypedValue,
     name: [:0]u8,
-) !*Decl {
+) !void {
     errdefer mod.gpa.free(name);
 
-    try namespace.anon_decls.ensureUnusedCapacity(mod.gpa, 1);
+    const new_decl = mod.declPtr(new_decl_index);
 
-    const new_decl = try mod.allocateNewDecl(name, namespace, src_decl.src_node, src_scope);
-
-    new_decl.src_line = src_decl.src_line;
+    new_decl.name = name;
+    new_decl.src_line = src_line;
     new_decl.ty = typed_value.ty;
     new_decl.val = typed_value.val;
     new_decl.@"align" = 0;
@@ -5062,22 +5148,16 @@ pub fn createAnonymousDeclFromDeclNamed(
     new_decl.analysis = .complete;
     new_decl.generation = mod.generation;
 
-    namespace.anon_decls.putAssumeCapacityNoClobber(new_decl, {});
+    try namespace.anon_decls.putNoClobber(mod.gpa, new_decl_index, {});
 
     // The Decl starts off with alive=false and the codegen backend will set alive=true
     // if the Decl is referenced by an instruction or another constant. Otherwise,
     // the Decl will be garbage collected by the `codegen_decl` task instead of sent
     // to the linker.
     if (typed_value.ty.isFnOrHasRuntimeBits()) {
-        try mod.comp.bin_file.allocateDeclIndexes(new_decl);
-        try mod.comp.anon_work_queue.writeItem(.{ .codegen_decl = new_decl });
+        try mod.comp.bin_file.allocateDeclIndexes(new_decl_index);
+        try mod.comp.anon_work_queue.writeItem(.{ .codegen_decl = new_decl_index });
     }
-
-    return new_decl;
-}
-
-pub fn getNextAnonNameIndex(mod: *Module) usize {
-    return @atomicRmw(usize, &mod.next_anon_name_index, .Add, 1, .Monotonic);
 }
 
 pub fn makeIntType(arena: Allocator, signedness: std.builtin.Signedness, bits: u16) !Type {
@@ -5339,12 +5419,12 @@ pub fn processOutdatedAndDeletedDecls(mod: *Module) !void {
     // for the outdated decls, but we cannot queue up the tasks until after
     // we find out which ones have been deleted, otherwise there would be
     // deleted Decl pointers in the work queue.
-    var outdated_decls = std.AutoArrayHashMap(*Decl, void).init(mod.gpa);
+    var outdated_decls = std.AutoArrayHashMap(Decl.Index, void).init(mod.gpa);
     defer outdated_decls.deinit();
     for (mod.import_table.values()) |file| {
         try outdated_decls.ensureUnusedCapacity(file.outdated_decls.items.len);
-        for (file.outdated_decls.items) |decl| {
-            outdated_decls.putAssumeCapacity(decl, {});
+        for (file.outdated_decls.items) |decl_index| {
+            outdated_decls.putAssumeCapacity(decl_index, {});
         }
         file.outdated_decls.clearRetainingCapacity();
 
@@ -5356,15 +5436,16 @@ pub fn processOutdatedAndDeletedDecls(mod: *Module) !void {
         // it may be both in this `deleted_decls` set, as well as in the
         // `Module.deletion_set`. To avoid deleting it twice, we remove it from the
         // deletion set at this time.
-        for (file.deleted_decls.items) |decl| {
+        for (file.deleted_decls.items) |decl_index| {
+            const decl = mod.declPtr(decl_index);
             log.debug("deleted from source: {*} ({s})", .{ decl, decl.name });
 
             // Remove from the namespace it resides in, preserving declaration order.
             assert(decl.zir_decl_index != 0);
-            _ = decl.src_namespace.decls.orderedRemoveAdapted(@as([]const u8, mem.sliceTo(decl.name, 0)), DeclAdapter{});
+            _ = decl.src_namespace.decls.orderedRemoveAdapted(@as([]const u8, mem.sliceTo(decl.name, 0)), DeclAdapter{ .mod = mod });
 
-            try mod.clearDecl(decl, &outdated_decls);
-            decl.destroy(mod);
+            try mod.clearDecl(decl_index, &outdated_decls);
+            mod.destroyDecl(decl_index);
         }
         file.deleted_decls.clearRetainingCapacity();
     }
@@ -5393,13 +5474,13 @@ pub fn processExports(mod: *Module) !void {
             if (gop.found_existing) {
                 new_export.status = .failed_retryable;
                 try mod.failed_exports.ensureUnusedCapacity(gpa, 1);
-                const src_loc = new_export.getSrcLoc();
+                const src_loc = new_export.getSrcLoc(mod);
                 const msg = try ErrorMsg.create(gpa, src_loc, "exported symbol collision: {s}", .{
                     new_export.options.name,
                 });
                 errdefer msg.destroy(gpa);
                 const other_export = gop.value_ptr.*;
-                const other_src_loc = other_export.getSrcLoc();
+                const other_src_loc = other_export.getSrcLoc(mod);
                 try mod.errNoteNonLazy(other_src_loc, msg, "other symbol here", .{});
                 mod.failed_exports.putAssumeCapacityNoClobber(new_export, msg);
                 new_export.status = .failed;
@@ -5413,7 +5494,7 @@ pub fn processExports(mod: *Module) !void {
                 const new_export = exports[0];
                 new_export.status = .failed_retryable;
                 try mod.failed_exports.ensureUnusedCapacity(gpa, 1);
-                const src_loc = new_export.getSrcLoc();
+                const src_loc = new_export.getSrcLoc(mod);
                 const msg = try ErrorMsg.create(gpa, src_loc, "unable to export: {s}", .{
                     @errorName(err),
                 });
@@ -5427,12 +5508,14 @@ pub fn populateTestFunctions(mod: *Module) !void {
     const gpa = mod.gpa;
     const builtin_pkg = mod.main_pkg.table.get("builtin").?;
     const builtin_file = (mod.importPkg(builtin_pkg) catch unreachable).file;
-    const builtin_namespace = builtin_file.root_decl.?.src_namespace;
-    const decl = builtin_namespace.decls.getKeyAdapted(@as([]const u8, "test_functions"), DeclAdapter{}).?;
+    const root_decl = mod.declPtr(builtin_file.root_decl.unwrap().?);
+    const builtin_namespace = root_decl.src_namespace;
+    const decl_index = builtin_namespace.decls.getKeyAdapted(@as([]const u8, "test_functions"), DeclAdapter{ .mod = mod }).?;
+    const decl = mod.declPtr(decl_index);
     var buf: Type.SlicePtrFieldTypeBuffer = undefined;
     const tmp_test_fn_ty = decl.ty.slicePtrFieldType(&buf).elemType();
 
-    const array_decl = d: {
+    const array_decl_index = d: {
         // Add mod.test_functions to an array decl then make the test_functions
         // decl reference it as a slice.
         var new_decl_arena = std.heap.ArenaAllocator.init(gpa);
@@ -5440,50 +5523,52 @@ pub fn populateTestFunctions(mod: *Module) !void {
         const arena = new_decl_arena.allocator();
 
         const test_fn_vals = try arena.alloc(Value, mod.test_functions.count());
-        const array_decl = try mod.createAnonymousDeclFromDecl(decl, decl.src_namespace, null, .{
+        const array_decl_index = try mod.createAnonymousDeclFromDecl(decl, decl.src_namespace, null, .{
             .ty = try Type.Tag.array.create(arena, .{
                 .len = test_fn_vals.len,
                 .elem_type = try tmp_test_fn_ty.copy(arena),
             }),
             .val = try Value.Tag.aggregate.create(arena, test_fn_vals),
         });
+        const array_decl = mod.declPtr(array_decl_index);
 
         // Add a dependency on each test name and function pointer.
         try array_decl.dependencies.ensureUnusedCapacity(gpa, test_fn_vals.len * 2);
 
-        for (mod.test_functions.keys()) |test_decl, i| {
+        for (mod.test_functions.keys()) |test_decl_index, i| {
+            const test_decl = mod.declPtr(test_decl_index);
             const test_name_slice = mem.sliceTo(test_decl.name, 0);
-            const test_name_decl = n: {
+            const test_name_decl_index = n: {
                 var name_decl_arena = std.heap.ArenaAllocator.init(gpa);
                 errdefer name_decl_arena.deinit();
                 const bytes = try name_decl_arena.allocator().dupe(u8, test_name_slice);
-                const test_name_decl = try mod.createAnonymousDeclFromDecl(array_decl, array_decl.src_namespace, null, .{
+                const test_name_decl_index = try mod.createAnonymousDeclFromDecl(array_decl, array_decl.src_namespace, null, .{
                     .ty = try Type.Tag.array_u8.create(name_decl_arena.allocator(), bytes.len),
                     .val = try Value.Tag.bytes.create(name_decl_arena.allocator(), bytes),
                 });
-                try test_name_decl.finalizeNewArena(&name_decl_arena);
-                break :n test_name_decl;
+                try mod.declPtr(test_name_decl_index).finalizeNewArena(&name_decl_arena);
+                break :n test_name_decl_index;
             };
-            array_decl.dependencies.putAssumeCapacityNoClobber(test_decl, {});
-            array_decl.dependencies.putAssumeCapacityNoClobber(test_name_decl, {});
-            try mod.linkerUpdateDecl(test_name_decl);
+            array_decl.dependencies.putAssumeCapacityNoClobber(test_decl_index, {});
+            array_decl.dependencies.putAssumeCapacityNoClobber(test_name_decl_index, {});
+            try mod.linkerUpdateDecl(test_name_decl_index);
 
             const field_vals = try arena.create([3]Value);
             field_vals.* = .{
                 try Value.Tag.slice.create(arena, .{
-                    .ptr = try Value.Tag.decl_ref.create(arena, test_name_decl),
+                    .ptr = try Value.Tag.decl_ref.create(arena, test_name_decl_index),
                     .len = try Value.Tag.int_u64.create(arena, test_name_slice.len),
                 }), // name
-                try Value.Tag.decl_ref.create(arena, test_decl), // func
+                try Value.Tag.decl_ref.create(arena, test_decl_index), // func
                 Value.initTag(.null_value), // async_frame_size
             };
             test_fn_vals[i] = try Value.Tag.aggregate.create(arena, field_vals);
         }
 
         try array_decl.finalizeNewArena(&new_decl_arena);
-        break :d array_decl;
+        break :d array_decl_index;
     };
-    try mod.linkerUpdateDecl(array_decl);
+    try mod.linkerUpdateDecl(array_decl_index);
 
     {
         var new_decl_arena = std.heap.ArenaAllocator.init(gpa);
@@ -5493,7 +5578,7 @@ pub fn populateTestFunctions(mod: *Module) !void {
         // This copy accesses the old Decl Type/Value so it must be done before `clearValues`.
         const new_ty = try Type.Tag.const_slice.create(arena, try tmp_test_fn_ty.copy(arena));
         const new_val = try Value.Tag.slice.create(arena, .{
-            .ptr = try Value.Tag.decl_ref.create(arena, array_decl),
+            .ptr = try Value.Tag.decl_ref.create(arena, array_decl_index),
             .len = try Value.Tag.int_u64.create(arena, mod.test_functions.count()),
         });
 
@@ -5506,15 +5591,17 @@ pub fn populateTestFunctions(mod: *Module) !void {
 
         try decl.finalizeNewArena(&new_decl_arena);
     }
-    try mod.linkerUpdateDecl(decl);
+    try mod.linkerUpdateDecl(decl_index);
 }
 
-pub fn linkerUpdateDecl(mod: *Module, decl: *Decl) !void {
+pub fn linkerUpdateDecl(mod: *Module, decl_index: Decl.Index) !void {
     const comp = mod.comp;
 
     if (comp.bin_file.options.emit == null) return;
 
-    comp.bin_file.updateDecl(mod, decl) catch |err| switch (err) {
+    const decl = mod.declPtr(decl_index);
+
+    comp.bin_file.updateDecl(mod, decl_index) catch |err| switch (err) {
         error.OutOfMemory => return error.OutOfMemory,
         error.AnalysisFail => {
             decl.analysis = .codegen_failure;
@@ -5523,7 +5610,7 @@ pub fn linkerUpdateDecl(mod: *Module, decl: *Decl) !void {
         else => {
             const gpa = mod.gpa;
             try mod.failed_decls.ensureUnusedCapacity(gpa, 1);
-            mod.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
+            mod.failed_decls.putAssumeCapacityNoClobber(decl_index, try ErrorMsg.create(
                 gpa,
                 decl.srcLoc(),
                 "unable to codegen: {s}",
@@ -5566,3 +5653,64 @@ fn reportRetryableFileError(
     }
     gop.value_ptr.* = err_msg;
 }
+
+pub fn markReferencedDeclsAlive(mod: *Module, val: Value) void {
+    switch (val.tag()) {
+        .decl_ref_mut => return mod.markDeclIndexAlive(val.castTag(.decl_ref_mut).?.data.decl_index),
+        .extern_fn => return mod.markDeclIndexAlive(val.castTag(.extern_fn).?.data.owner_decl),
+        .function => return mod.markDeclIndexAlive(val.castTag(.function).?.data.owner_decl),
+        .variable => return mod.markDeclIndexAlive(val.castTag(.variable).?.data.owner_decl),
+        .decl_ref => return mod.markDeclIndexAlive(val.cast(Value.Payload.Decl).?.data),
+
+        .repeated,
+        .eu_payload,
+        .opt_payload,
+        .empty_array_sentinel,
+        => return mod.markReferencedDeclsAlive(val.cast(Value.Payload.SubValue).?.data),
+
+        .eu_payload_ptr,
+        .opt_payload_ptr,
+        => return mod.markReferencedDeclsAlive(val.cast(Value.Payload.PayloadPtr).?.data.container_ptr),
+
+        .slice => {
+            const slice = val.cast(Value.Payload.Slice).?.data;
+            mod.markReferencedDeclsAlive(slice.ptr);
+            mod.markReferencedDeclsAlive(slice.len);
+        },
+
+        .elem_ptr => {
+            const elem_ptr = val.cast(Value.Payload.ElemPtr).?.data;
+            return mod.markReferencedDeclsAlive(elem_ptr.array_ptr);
+        },
+        .field_ptr => {
+            const field_ptr = val.cast(Value.Payload.FieldPtr).?.data;
+            return mod.markReferencedDeclsAlive(field_ptr.container_ptr);
+        },
+        .aggregate => {
+            for (val.castTag(.aggregate).?.data) |field_val| {
+                mod.markReferencedDeclsAlive(field_val);
+            }
+        },
+        .@"union" => {
+            const data = val.cast(Value.Payload.Union).?.data;
+            mod.markReferencedDeclsAlive(data.tag);
+            mod.markReferencedDeclsAlive(data.val);
+        },
+
+        else => {},
+    }
+}
+
+pub fn markDeclAlive(mod: *Module, decl: *Decl) void {
+    if (decl.alive) return;
+    decl.alive = true;
+
+    // This is the first time we are marking this Decl alive. We must
+    // therefore recurse into its value and mark any Decl it references
+    // as also alive, so that any Decl referenced does not get garbage collected.
+    mod.markReferencedDeclsAlive(decl.val);
+}
+
+fn markDeclIndexAlive(mod: *Module, decl_index: Decl.Index) void {
+    return mod.markDeclAlive(mod.declPtr(decl_index));
+}
src/print_air.zig
@@ -7,7 +7,7 @@ const Value = @import("value.zig").Value;
 const Air = @import("Air.zig");
 const Liveness = @import("Liveness.zig");
 
-pub fn dump(gpa: Allocator, air: Air, liveness: Liveness) void {
+pub fn dump(module: *Module, air: Air, liveness: Liveness) void {
     const instruction_bytes = air.instructions.len *
         // Here we don't use @sizeOf(Air.Inst.Data) because it would include
         // the debug safety tag but we want to measure release size.
@@ -41,11 +41,12 @@ pub fn dump(gpa: Allocator, air: Air, liveness: Liveness) void {
         liveness.special.count(), fmtIntSizeBin(liveness_special_bytes),
     });
     // zig fmt: on
-    var arena = std.heap.ArenaAllocator.init(gpa);
+    var arena = std.heap.ArenaAllocator.init(module.gpa);
     defer arena.deinit();
 
     var writer: Writer = .{
-        .gpa = gpa,
+        .module = module,
+        .gpa = module.gpa,
         .arena = arena.allocator(),
         .air = air,
         .liveness = liveness,
@@ -58,6 +59,7 @@ pub fn dump(gpa: Allocator, air: Air, liveness: Liveness) void {
 }
 
 const Writer = struct {
+    module: *Module,
     gpa: Allocator,
     arena: Allocator,
     air: Air,
@@ -591,7 +593,8 @@ const Writer = struct {
     fn writeDbgInline(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
         const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
         const function = w.air.values[ty_pl.payload].castTag(.function).?.data;
-        try s.print("{s}", .{function.owner_decl.name});
+        const owner_decl = w.module.declPtr(function.owner_decl);
+        try s.print("{s}", .{owner_decl.name});
     }
 
     fn writeDbgVar(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
src/RangeSet.zig
@@ -1,12 +1,14 @@
 const std = @import("std");
 const Order = std.math.Order;
-const Type = @import("type.zig").Type;
-const Value = @import("value.zig").Value;
+
 const RangeSet = @This();
+const Module = @import("Module.zig");
 const SwitchProngSrc = @import("Module.zig").SwitchProngSrc;
+const Type = @import("type.zig").Type;
+const Value = @import("value.zig").Value;
 
 ranges: std.ArrayList(Range),
-target: std.Target,
+module: *Module,
 
 pub const Range = struct {
     first: Value,
@@ -14,10 +16,10 @@ pub const Range = struct {
     src: SwitchProngSrc,
 };
 
-pub fn init(allocator: std.mem.Allocator, target: std.Target) RangeSet {
+pub fn init(allocator: std.mem.Allocator, module: *Module) RangeSet {
     return .{
         .ranges = std.ArrayList(Range).init(allocator),
-        .target = target,
+        .module = module,
     };
 }
 
@@ -32,11 +34,9 @@ pub fn add(
     ty: Type,
     src: SwitchProngSrc,
 ) !?SwitchProngSrc {
-    const target = self.target;
-
     for (self.ranges.items) |range| {
-        if (last.compare(.gte, range.first, ty, target) and
-            first.compare(.lte, range.last, ty, target))
+        if (last.compare(.gte, range.first, ty, self.module) and
+            first.compare(.lte, range.last, ty, self.module))
         {
             return range.src; // They overlap.
         }
@@ -49,26 +49,24 @@ pub fn add(
     return null;
 }
 
-const LessThanContext = struct { ty: Type, target: std.Target };
+const LessThanContext = struct { ty: Type, module: *Module };
 
 /// Assumes a and b do not overlap
 fn lessThan(ctx: LessThanContext, a: Range, b: Range) bool {
-    return a.first.compare(.lt, b.first, ctx.ty, ctx.target);
+    return a.first.compare(.lt, b.first, ctx.ty, ctx.module);
 }
 
 pub fn spans(self: *RangeSet, first: Value, last: Value, ty: Type) !bool {
     if (self.ranges.items.len == 0)
         return false;
 
-    const target = self.target;
-
     std.sort.sort(Range, self.ranges.items, LessThanContext{
         .ty = ty,
-        .target = target,
+        .module = self.module,
     }, lessThan);
 
-    if (!self.ranges.items[0].first.eql(first, ty, target) or
-        !self.ranges.items[self.ranges.items.len - 1].last.eql(last, ty, target))
+    if (!self.ranges.items[0].first.eql(first, ty, self.module) or
+        !self.ranges.items[self.ranges.items.len - 1].last.eql(last, ty, self.module))
     {
         return false;
     }
@@ -78,6 +76,8 @@ pub fn spans(self: *RangeSet, first: Value, last: Value, ty: Type) !bool {
     var counter = try std.math.big.int.Managed.init(self.ranges.allocator);
     defer counter.deinit();
 
+    const target = self.module.getTarget();
+
     // look for gaps
     for (self.ranges.items[1..]) |cur, i| {
         // i starts counting from the second item.
src/Sema.zig
@@ -24,6 +24,7 @@ inst_map: InstMap = .{},
 /// and `src_decl` of `Block` is the `Decl` of the callee.
 /// This `Decl` owns the arena memory of this `Sema`.
 owner_decl: *Decl,
+owner_decl_index: Decl.Index,
 /// For an inline or comptime function call, this will be the root parent function
 /// which contains the callsite. Corresponds to `owner_decl`.
 owner_func: ?*Module.Fn,
@@ -47,7 +48,7 @@ comptime_break_inst: Zir.Inst.Index = undefined,
 /// access to the source location set by the previous instruction which did
 /// contain a mapped source location.
 src: LazySrcLoc = .{ .token_offset = 0 },
-decl_val_table: std.AutoHashMapUnmanaged(*Decl, Air.Inst.Ref) = .{},
+decl_val_table: std.AutoHashMapUnmanaged(Decl.Index, Air.Inst.Ref) = .{},
 /// When doing a generic function instantiation, this array collects a
 /// `Value` object for each parameter that is comptime known and thus elided
 /// from the generated function. This memory is allocated by a parent `Sema` and
@@ -111,10 +112,6 @@ pub const Block = struct {
     parent: ?*Block,
     /// Shared among all child blocks.
     sema: *Sema,
-    /// This Decl is the Decl according to the Zig source code corresponding to this Block.
-    /// This can vary during inline or comptime function calls. See `Sema.owner_decl`
-    /// for the one that will be the same for all Block instances.
-    src_decl: *Decl,
     /// The namespace to use for lookups from this source block
     /// When analyzing fields, this is different from src_decl.src_namepsace.
     namespace: *Namespace,
@@ -130,6 +127,10 @@ pub const Block = struct {
     /// If runtime_index is not 0 then one of these is guaranteed to be non null.
     runtime_cond: ?LazySrcLoc = null,
     runtime_loop: ?LazySrcLoc = null,
+    /// This Decl is the Decl according to the Zig source code corresponding to this Block.
+    /// This can vary during inline or comptime function calls. See `Sema.owner_decl`
+    /// for the one that will be the same for all Block instances.
+    src_decl: Decl.Index,
     /// Non zero if a non-inline loop or a runtime conditional have been encountered.
     /// Stores to to comptime variables are only allowed when var.runtime_index <= runtime_index.
     runtime_index: u32 = 0,
@@ -512,20 +513,21 @@ pub const Block = struct {
         }
 
         /// `alignment` value of 0 means to use ABI alignment.
-        pub fn finish(wad: *WipAnonDecl, ty: Type, val: Value, alignment: u32) !*Decl {
+        pub fn finish(wad: *WipAnonDecl, ty: Type, val: Value, alignment: u32) !Decl.Index {
             const sema = wad.block.sema;
             // Do this ahead of time because `createAnonymousDecl` depends on calling
             // `type.hasRuntimeBits()`.
             _ = try sema.typeHasRuntimeBits(wad.block, wad.src, ty);
-            const new_decl = try sema.mod.createAnonymousDecl(wad.block, .{
+            const new_decl_index = try sema.mod.createAnonymousDecl(wad.block, .{
                 .ty = ty,
                 .val = val,
             });
+            const new_decl = sema.mod.declPtr(new_decl_index);
             new_decl.@"align" = alignment;
-            errdefer sema.mod.abortAnonDecl(new_decl);
+            errdefer sema.mod.abortAnonDecl(new_decl_index);
             try new_decl.finalizeNewArena(&wad.new_decl_arena);
             wad.finished = true;
-            return new_decl;
+            return new_decl_index;
         }
     };
 };
@@ -676,7 +678,7 @@ fn analyzeBodyInner(
         crash_info.setBodyIndex(i);
         const inst = body[i];
         std.log.scoped(.sema_zir).debug("sema ZIR {s} %{d}", .{
-            block.src_decl.src_namespace.file_scope.sub_file_path, inst,
+            sema.mod.declPtr(block.src_decl).src_namespace.file_scope.sub_file_path, inst,
         });
         const air_inst: Air.Inst.Ref = switch (tags[inst]) {
             // zig fmt: off
@@ -1383,8 +1385,7 @@ pub fn resolveConstString(
     const wanted_type = Type.initTag(.const_slice_u8);
     const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src);
     const val = try sema.resolveConstValue(block, src, coerced_inst);
-    const target = sema.mod.getTarget();
-    return val.toAllocatedBytes(wanted_type, sema.arena, target);
+    return val.toAllocatedBytes(wanted_type, sema.arena, sema.mod);
 }
 
 pub fn resolveType(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) !Type {
@@ -1538,28 +1539,24 @@ fn failWithDivideByZero(sema: *Sema, block: *Block, src: LazySrcLoc) CompileErro
 }
 
 fn failWithModRemNegative(sema: *Sema, block: *Block, src: LazySrcLoc, lhs_ty: Type, rhs_ty: Type) CompileError {
-    const target = sema.mod.getTarget();
     return sema.fail(block, src, "remainder division with '{}' and '{}': signed integers and floats must use @rem or @mod", .{
-        lhs_ty.fmt(target), rhs_ty.fmt(target),
+        lhs_ty.fmt(sema.mod), rhs_ty.fmt(sema.mod),
     });
 }
 
 fn failWithExpectedOptionalType(sema: *Sema, block: *Block, src: LazySrcLoc, optional_ty: Type) CompileError {
-    const target = sema.mod.getTarget();
-    return sema.fail(block, src, "expected optional type, found {}", .{optional_ty.fmt(target)});
+    return sema.fail(block, src, "expected optional type, found {}", .{optional_ty.fmt(sema.mod)});
 }
 
 fn failWithArrayInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError {
-    const target = sema.mod.getTarget();
     return sema.fail(block, src, "type '{}' does not support array initialization syntax", .{
-        ty.fmt(target),
+        ty.fmt(sema.mod),
     });
 }
 
 fn failWithStructInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError {
-    const target = sema.mod.getTarget();
     return sema.fail(block, src, "type '{}' does not support struct initialization syntax", .{
-        ty.fmt(target),
+        ty.fmt(sema.mod),
     });
 }
 
@@ -1570,9 +1567,8 @@ fn failWithErrorSetCodeMissing(
     dest_err_set_ty: Type,
     src_err_set_ty: Type,
 ) CompileError {
-    const target = sema.mod.getTarget();
     return sema.fail(block, src, "expected type '{}', found type '{}'", .{
-        dest_err_set_ty.fmt(target), src_err_set_ty.fmt(target),
+        dest_err_set_ty.fmt(sema.mod), src_err_set_ty.fmt(sema.mod),
     });
 }
 
@@ -1586,7 +1582,9 @@ fn errNote(
     comptime format: []const u8,
     args: anytype,
 ) error{OutOfMemory}!void {
-    return sema.mod.errNoteNonLazy(src.toSrcLoc(block.src_decl), parent, format, args);
+    const mod = sema.mod;
+    const src_decl = mod.declPtr(block.src_decl);
+    return mod.errNoteNonLazy(src.toSrcLoc(src_decl), parent, format, args);
 }
 
 fn addFieldErrNote(
@@ -1598,10 +1596,12 @@ fn addFieldErrNote(
     comptime format: []const u8,
     args: anytype,
 ) !void {
-    const decl = container_ty.getOwnerDecl();
+    const mod = sema.mod;
+    const decl_index = container_ty.getOwnerDecl();
+    const decl = mod.declPtr(decl_index);
     const tree = try sema.getAstTree(block);
     const field_src = enumFieldSrcLoc(decl, tree.*, container_ty.getNodeOffset(), field_index);
-    try sema.mod.errNoteNonLazy(field_src.toSrcLoc(decl), parent, format, args);
+    try mod.errNoteNonLazy(field_src.toSrcLoc(decl), parent, format, args);
 }
 
 fn errMsg(
@@ -1611,7 +1611,9 @@ fn errMsg(
     comptime format: []const u8,
     args: anytype,
 ) error{OutOfMemory}!*Module.ErrorMsg {
-    return Module.ErrorMsg.create(sema.gpa, src.toSrcLoc(block.src_decl), format, args);
+    const mod = sema.mod;
+    const src_decl = mod.declPtr(block.src_decl);
+    return Module.ErrorMsg.create(sema.gpa, src.toSrcLoc(src_decl), format, args);
 }
 
 pub fn fail(
@@ -1654,7 +1656,7 @@ fn failWithOwnedErrorMsg(sema: *Sema, block: *Block, err_msg: *Module.ErrorMsg)
         sema.owner_decl.analysis = .sema_failure;
         sema.owner_decl.generation = mod.generation;
     }
-    const gop = mod.failed_decls.getOrPutAssumeCapacity(sema.owner_decl);
+    const gop = mod.failed_decls.getOrPutAssumeCapacity(sema.owner_decl_index);
     if (gop.found_existing) {
         // If there are multiple errors for the same Decl, prefer the first one added.
         err_msg.destroy(mod.gpa);
@@ -1756,7 +1758,7 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
                     try inferred_alloc.stored_inst_list.append(sema.arena, operand);
 
                     try sema.requireRuntimeBlock(block, src);
-                    const ptr_ty = try Type.ptr(sema.arena, target, .{
+                    const ptr_ty = try Type.ptr(sema.arena, sema.mod, .{
                         .pointee_type = pointee_ty,
                         .@"align" = inferred_alloc.alignment,
                         .@"addrspace" = addr_space,
@@ -1770,7 +1772,7 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
                     // The alloc will turn into a Decl.
                     var anon_decl = try block.startAnonDecl(src);
                     defer anon_decl.deinit();
-                    iac.data.decl = try anon_decl.finish(
+                    iac.data.decl_index = try anon_decl.finish(
                         try pointee_ty.copy(anon_decl.arena()),
                         Value.undef,
                         iac.data.alignment,
@@ -1778,7 +1780,7 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
                     if (iac.data.alignment != 0) {
                         try sema.resolveTypeLayout(block, src, pointee_ty);
                     }
-                    const ptr_ty = try Type.ptr(sema.arena, target, .{
+                    const ptr_ty = try Type.ptr(sema.arena, sema.mod, .{
                         .pointee_type = pointee_ty,
                         .@"align" = iac.data.alignment,
                         .@"addrspace" = addr_space,
@@ -1786,7 +1788,7 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
                     return sema.addConstant(
                         ptr_ty,
                         try Value.Tag.decl_ref_mut.create(sema.arena, .{
-                            .decl = iac.data.decl,
+                            .decl_index = iac.data.decl_index,
                             .runtime_index = block.runtime_index,
                         }),
                     );
@@ -1827,7 +1829,7 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
         }
     }
 
-    const ptr_ty = try Type.ptr(sema.arena, target, .{
+    const ptr_ty = try Type.ptr(sema.arena, sema.mod, .{
         .pointee_type = pointee_ty,
         .@"addrspace" = addr_space,
     });
@@ -1848,7 +1850,7 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
                 }
                 const ty_op = air_datas[trash_inst].ty_op;
                 const operand_ty = sema.typeOf(ty_op.operand);
-                const ptr_operand_ty = try Type.ptr(sema.arena, target, .{
+                const ptr_operand_ty = try Type.ptr(sema.arena, sema.mod, .{
                     .pointee_type = operand_ty,
                     .@"addrspace" = addr_space,
                 });
@@ -1924,18 +1926,19 @@ fn zirStructDecl(
     errdefer new_decl_arena.deinit();
     const new_decl_arena_allocator = new_decl_arena.allocator();
 
+    const mod = sema.mod;
     const struct_obj = try new_decl_arena_allocator.create(Module.Struct);
     const struct_ty = try Type.Tag.@"struct".create(new_decl_arena_allocator, struct_obj);
     const struct_val = try Value.Tag.ty.create(new_decl_arena_allocator, struct_ty);
-    const type_name = try sema.createTypeName(block, small.name_strategy, "struct");
-    const new_decl = try sema.mod.createAnonymousDeclNamed(block, .{
+    const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{
         .ty = Type.type,
         .val = struct_val,
-    }, type_name);
+    }, small.name_strategy, "struct");
+    const new_decl = mod.declPtr(new_decl_index);
     new_decl.owns_tv = true;
-    errdefer sema.mod.abortAnonDecl(new_decl);
+    errdefer mod.abortAnonDecl(new_decl_index);
     struct_obj.* = .{
-        .owner_decl = new_decl,
+        .owner_decl = new_decl_index,
         .fields = .{},
         .node_offset = src.node_offset,
         .zir_index = inst,
@@ -1953,15 +1956,23 @@ fn zirStructDecl(
     });
     try sema.analyzeStructDecl(new_decl, inst, struct_obj);
     try new_decl.finalizeNewArena(&new_decl_arena);
-    return sema.analyzeDeclVal(block, src, new_decl);
+    return sema.analyzeDeclVal(block, src, new_decl_index);
 }
 
-fn createTypeName(
+fn createAnonymousDeclTypeNamed(
     sema: *Sema,
     block: *Block,
+    typed_value: TypedValue,
     name_strategy: Zir.Inst.NameStrategy,
     anon_prefix: []const u8,
-) ![:0]u8 {
+) !Decl.Index {
+    const mod = sema.mod;
+    const namespace = block.namespace;
+    const src_scope = block.wip_capture_scope;
+    const src_decl = mod.declPtr(block.src_decl);
+    const new_decl_index = try mod.allocateNewDecl(namespace, src_decl.src_node, src_scope);
+    errdefer mod.destroyDecl(new_decl_index);
+
     switch (name_strategy) {
         .anon => {
             // It would be neat to have "struct:line:column" but this name has
@@ -1970,20 +1981,24 @@ fn createTypeName(
             // semantically analyzed.
             // This name is also used as the key in the parent namespace so it cannot be
             // renamed.
-            const name_index = sema.mod.getNextAnonNameIndex();
-            return std.fmt.allocPrintZ(sema.gpa, "{s}__{s}_{d}", .{
-                block.src_decl.name, anon_prefix, name_index,
+            const name = try std.fmt.allocPrintZ(sema.gpa, "{s}__{s}_{d}", .{
+                src_decl.name, anon_prefix, @enumToInt(new_decl_index),
             });
+            try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, namespace, typed_value, name);
+            return new_decl_index;
+        },
+        .parent => {
+            const name = try sema.gpa.dupeZ(u8, mem.sliceTo(sema.mod.declPtr(block.src_decl).name, 0));
+            try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, namespace, typed_value, name);
+            return new_decl_index;
         },
-        .parent => return sema.gpa.dupeZ(u8, mem.sliceTo(block.src_decl.name, 0)),
         .func => {
-            const target = sema.mod.getTarget();
             const fn_info = sema.code.getFnInfo(sema.func.?.zir_body_inst);
             const zir_tags = sema.code.instructions.items(.tag);
 
             var buf = std.ArrayList(u8).init(sema.gpa);
             defer buf.deinit();
-            try buf.appendSlice(mem.sliceTo(block.src_decl.name, 0));
+            try buf.appendSlice(mem.sliceTo(sema.mod.declPtr(block.src_decl).name, 0));
             try buf.appendSlice("(");
 
             var arg_i: usize = 0;
@@ -1995,7 +2010,7 @@ fn createTypeName(
                     const arg_val = sema.resolveConstMaybeUndefVal(block, .unneeded, arg) catch unreachable;
 
                     if (arg_i != 0) try buf.appendSlice(",");
-                    try buf.writer().print("{}", .{arg_val.fmtValue(sema.typeOf(arg), target)});
+                    try buf.writer().print("{}", .{arg_val.fmtValue(sema.typeOf(arg), sema.mod)});
 
                     arg_i += 1;
                     continue;
@@ -2004,7 +2019,9 @@ fn createTypeName(
             };
 
             try buf.appendSlice(")");
-            return buf.toOwnedSliceSentinel(0);
+            const name = try buf.toOwnedSliceSentinel(0);
+            try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, namespace, typed_value, name);
+            return new_decl_index;
         },
     }
 }
@@ -2064,16 +2081,16 @@ fn zirEnumDecl(
     };
     const enum_ty = Type.initPayload(&enum_ty_payload.base);
     const enum_val = try Value.Tag.ty.create(new_decl_arena_allocator, enum_ty);
-    const type_name = try sema.createTypeName(block, small.name_strategy, "enum");
-    const new_decl = try mod.createAnonymousDeclNamed(block, .{
+    const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{
         .ty = Type.type,
         .val = enum_val,
-    }, type_name);
+    }, small.name_strategy, "enum");
+    const new_decl = mod.declPtr(new_decl_index);
     new_decl.owns_tv = true;
-    errdefer mod.abortAnonDecl(new_decl);
+    errdefer mod.abortAnonDecl(new_decl_index);
 
     enum_obj.* = .{
-        .owner_decl = new_decl,
+        .owner_decl = new_decl_index,
         .tag_ty = Type.@"null",
         .tag_ty_inferred = true,
         .fields = .{},
@@ -2101,7 +2118,7 @@ fn zirEnumDecl(
             enum_obj.tag_ty_inferred = false;
         }
         try new_decl.finalizeNewArena(&new_decl_arena);
-        return sema.analyzeDeclVal(block, src, new_decl);
+        return sema.analyzeDeclVal(block, src, new_decl_index);
     }
     extra_index += body.len;
 
@@ -2116,8 +2133,13 @@ fn zirEnumDecl(
         // should be the enum itself.
 
         const prev_owner_decl = sema.owner_decl;
+        const prev_owner_decl_index = sema.owner_decl_index;
         sema.owner_decl = new_decl;
-        defer sema.owner_decl = prev_owner_decl;
+        sema.owner_decl_index = new_decl_index;
+        defer {
+            sema.owner_decl = prev_owner_decl;
+            sema.owner_decl_index = prev_owner_decl_index;
+        }
 
         const prev_owner_func = sema.owner_func;
         sema.owner_func = null;
@@ -2133,7 +2155,7 @@ fn zirEnumDecl(
         var enum_block: Block = .{
             .parent = null,
             .sema = sema,
-            .src_decl = new_decl,
+            .src_decl = new_decl_index,
             .namespace = &enum_obj.namespace,
             .wip_capture_scope = wip_captures.scope,
             .instructions = .{},
@@ -2168,7 +2190,7 @@ fn zirEnumDecl(
     if (any_values) {
         try enum_obj.values.ensureTotalCapacityContext(new_decl_arena_allocator, fields_len, .{
             .ty = enum_obj.tag_ty,
-            .target = target,
+            .mod = mod,
         });
     }
 
@@ -2196,8 +2218,8 @@ fn zirEnumDecl(
         const gop = enum_obj.fields.getOrPutAssumeCapacity(field_name);
         if (gop.found_existing) {
             const tree = try sema.getAstTree(block);
-            const field_src = enumFieldSrcLoc(block.src_decl, tree.*, src.node_offset, field_i);
-            const other_tag_src = enumFieldSrcLoc(block.src_decl, tree.*, src.node_offset, gop.index);
+            const field_src = enumFieldSrcLoc(sema.mod.declPtr(block.src_decl), tree.*, src.node_offset, field_i);
+            const other_tag_src = enumFieldSrcLoc(sema.mod.declPtr(block.src_decl), tree.*, src.node_offset, gop.index);
             const msg = msg: {
                 const msg = try sema.errMsg(block, field_src, "duplicate enum tag", .{});
                 errdefer msg.destroy(gpa);
@@ -2218,7 +2240,7 @@ fn zirEnumDecl(
             const copied_tag_val = try tag_val.copy(new_decl_arena_allocator);
             enum_obj.values.putAssumeCapacityNoClobberContext(copied_tag_val, {}, .{
                 .ty = enum_obj.tag_ty,
-                .target = target,
+                .mod = mod,
             });
         } else if (any_values) {
             const tag_val = if (last_tag_val) |val|
@@ -2229,13 +2251,13 @@ fn zirEnumDecl(
             const copied_tag_val = try tag_val.copy(new_decl_arena_allocator);
             enum_obj.values.putAssumeCapacityNoClobberContext(copied_tag_val, {}, .{
                 .ty = enum_obj.tag_ty,
-                .target = target,
+                .mod = mod,
             });
         }
     }
 
     try new_decl.finalizeNewArena(&new_decl_arena);
-    return sema.analyzeDeclVal(block, src, new_decl);
+    return sema.analyzeDeclVal(block, src, new_decl_index);
 }
 
 fn zirUnionDecl(
@@ -2279,15 +2301,16 @@ fn zirUnionDecl(
     };
     const union_ty = Type.initPayload(&union_payload.base);
     const union_val = try Value.Tag.ty.create(new_decl_arena_allocator, union_ty);
-    const type_name = try sema.createTypeName(block, small.name_strategy, "union");
-    const new_decl = try sema.mod.createAnonymousDeclNamed(block, .{
+    const mod = sema.mod;
+    const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{
         .ty = Type.type,
         .val = union_val,
-    }, type_name);
+    }, small.name_strategy, "union");
+    const new_decl = mod.declPtr(new_decl_index);
     new_decl.owns_tv = true;
-    errdefer sema.mod.abortAnonDecl(new_decl);
+    errdefer mod.abortAnonDecl(new_decl_index);
     union_obj.* = .{
-        .owner_decl = new_decl,
+        .owner_decl = new_decl_index,
         .tag_ty = Type.initTag(.@"null"),
         .fields = .{},
         .node_offset = src.node_offset,
@@ -2304,10 +2327,10 @@ fn zirUnionDecl(
         &union_obj.namespace, new_decl, new_decl.name,
     });
 
-    _ = try sema.mod.scanNamespace(&union_obj.namespace, extra_index, decls_len, new_decl);
+    _ = try mod.scanNamespace(&union_obj.namespace, extra_index, decls_len, new_decl);
 
     try new_decl.finalizeNewArena(&new_decl_arena);
-    return sema.analyzeDeclVal(block, src, new_decl);
+    return sema.analyzeDeclVal(block, src, new_decl_index);
 }
 
 fn zirOpaqueDecl(
@@ -2347,16 +2370,16 @@ fn zirOpaqueDecl(
     };
     const opaque_ty = Type.initPayload(&opaque_ty_payload.base);
     const opaque_val = try Value.Tag.ty.create(new_decl_arena_allocator, opaque_ty);
-    const type_name = try sema.createTypeName(block, small.name_strategy, "opaque");
-    const new_decl = try mod.createAnonymousDeclNamed(block, .{
+    const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{
         .ty = Type.type,
         .val = opaque_val,
-    }, type_name);
+    }, small.name_strategy, "opaque");
+    const new_decl = mod.declPtr(new_decl_index);
     new_decl.owns_tv = true;
-    errdefer mod.abortAnonDecl(new_decl);
+    errdefer mod.abortAnonDecl(new_decl_index);
 
     opaque_obj.* = .{
-        .owner_decl = new_decl,
+        .owner_decl = new_decl_index,
         .node_offset = src.node_offset,
         .namespace = .{
             .parent = block.namespace,
@@ -2371,7 +2394,7 @@ fn zirOpaqueDecl(
     extra_index = try mod.scanNamespace(&opaque_obj.namespace, extra_index, decls_len, new_decl);
 
     try new_decl.finalizeNewArena(&new_decl_arena);
-    return sema.analyzeDeclVal(block, src, new_decl);
+    return sema.analyzeDeclVal(block, src, new_decl_index);
 }
 
 fn zirErrorSetDecl(
@@ -2395,13 +2418,14 @@ fn zirErrorSetDecl(
     const error_set = try new_decl_arena_allocator.create(Module.ErrorSet);
     const error_set_ty = try Type.Tag.error_set.create(new_decl_arena_allocator, error_set);
     const error_set_val = try Value.Tag.ty.create(new_decl_arena_allocator, error_set_ty);
-    const type_name = try sema.createTypeName(block, name_strategy, "error");
-    const new_decl = try sema.mod.createAnonymousDeclNamed(block, .{
+    const mod = sema.mod;
+    const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{
         .ty = Type.type,
         .val = error_set_val,
-    }, type_name);
+    }, name_strategy, "error");
+    const new_decl = mod.declPtr(new_decl_index);
     new_decl.owns_tv = true;
-    errdefer sema.mod.abortAnonDecl(new_decl);
+    errdefer mod.abortAnonDecl(new_decl_index);
 
     var names = Module.ErrorSet.NameMap{};
     try names.ensureUnusedCapacity(new_decl_arena_allocator, extra.data.fields_len);
@@ -2410,7 +2434,7 @@ fn zirErrorSetDecl(
     const extra_index_end = extra_index + (extra.data.fields_len * 2);
     while (extra_index < extra_index_end) : (extra_index += 2) { // +2 to skip over doc_string
         const str_index = sema.code.extra[extra_index];
-        const kv = try sema.mod.getErrorValue(sema.code.nullTerminatedString(str_index));
+        const kv = try mod.getErrorValue(sema.code.nullTerminatedString(str_index));
         const result = names.getOrPutAssumeCapacity(kv.key);
         assert(!result.found_existing); // verified in AstGen
     }
@@ -2419,12 +2443,12 @@ fn zirErrorSetDecl(
     Module.ErrorSet.sortNames(&names);
 
     error_set.* = .{
-        .owner_decl = new_decl,
+        .owner_decl = new_decl_index,
         .node_offset = inst_data.src_node,
         .names = names,
     };
     try new_decl.finalizeNewArena(&new_decl_arena);
-    return sema.analyzeDeclVal(block, src, new_decl);
+    return sema.analyzeDeclVal(block, src, new_decl_index);
 }
 
 fn zirRetPtr(
@@ -2444,7 +2468,7 @@ fn zirRetPtr(
     }
 
     const target = sema.mod.getTarget();
-    const ptr_type = try Type.ptr(sema.arena, target, .{
+    const ptr_type = try Type.ptr(sema.arena, sema.mod, .{
         .pointee_type = sema.fn_ret_ty,
         .@"addrspace" = target_util.defaultAddressSpace(target, .local),
     });
@@ -2535,14 +2559,13 @@ fn zirIndexablePtrLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
     else
         object_ty;
 
-    const target = sema.mod.getTarget();
     if (!array_ty.isIndexable()) {
         const msg = msg: {
             const msg = try sema.errMsg(
                 block,
                 src,
                 "type '{}' does not support indexing",
-                .{array_ty.fmt(target)},
+                .{array_ty.fmt(sema.mod)},
             );
             errdefer msg.destroy(sema.gpa);
             try sema.errNote(
@@ -2598,7 +2621,7 @@ fn zirAllocExtended(
             return sema.addConstant(
                 inferred_alloc_ty,
                 try Value.Tag.inferred_alloc_comptime.create(sema.arena, .{
-                    .decl = undefined,
+                    .decl_index = undefined,
                     .alignment = alignment,
                 }),
             );
@@ -2612,7 +2635,7 @@ fn zirAllocExtended(
         const target = sema.mod.getTarget();
         try sema.requireRuntimeBlock(block, src);
         try sema.resolveTypeLayout(block, src, var_ty);
-        const ptr_type = try Type.ptr(sema.arena, target, .{
+        const ptr_type = try Type.ptr(sema.arena, sema.mod, .{
             .pointee_type = var_ty,
             .@"align" = alignment,
             .@"addrspace" = target_util.defaultAddressSpace(target, .local),
@@ -2649,7 +2672,7 @@ fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro
     const ptr_ty = sema.typeOf(ptr);
     var ptr_info = ptr_ty.ptrInfo().data;
     ptr_info.mutable = false;
-    const const_ptr_ty = try Type.ptr(sema.arena, sema.mod.getTarget(), ptr_info);
+    const const_ptr_ty = try Type.ptr(sema.arena, sema.mod, ptr_info);
 
     if (try sema.resolveMaybeUndefVal(block, inst_data.src(), ptr)) |val| {
         return sema.addConstant(const_ptr_ty, val);
@@ -2669,7 +2692,7 @@ fn zirAllocInferredComptime(
     return sema.addConstant(
         inferred_alloc_ty,
         try Value.Tag.inferred_alloc_comptime.create(sema.arena, .{
-            .decl = undefined,
+            .decl_index = undefined,
             .alignment = 0,
         }),
     );
@@ -2687,7 +2710,7 @@ fn zirAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
         return sema.analyzeComptimeAlloc(block, var_ty, 0, ty_src);
     }
     const target = sema.mod.getTarget();
-    const ptr_type = try Type.ptr(sema.arena, target, .{
+    const ptr_type = try Type.ptr(sema.arena, sema.mod, .{
         .pointee_type = var_ty,
         .@"addrspace" = target_util.defaultAddressSpace(target, .local),
     });
@@ -2709,7 +2732,7 @@ fn zirAllocMut(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
     }
     try sema.validateVarType(block, ty_src, var_ty, false);
     const target = sema.mod.getTarget();
-    const ptr_type = try Type.ptr(sema.arena, target, .{
+    const ptr_type = try Type.ptr(sema.arena, sema.mod, .{
         .pointee_type = var_ty,
         .@"addrspace" = target_util.defaultAddressSpace(target, .local),
     });
@@ -2735,7 +2758,7 @@ fn zirAllocInferred(
         return sema.addConstant(
             inferred_alloc_ty,
             try Value.Tag.inferred_alloc_comptime.create(sema.arena, .{
-                .decl = undefined,
+                .decl_index = undefined,
                 .alignment = 0,
             }),
         );
@@ -2776,11 +2799,12 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
     switch (ptr_val.tag()) {
         .inferred_alloc_comptime => {
             const iac = ptr_val.castTag(.inferred_alloc_comptime).?;
-            const decl = iac.data.decl;
-            try sema.mod.declareDeclDependency(sema.owner_decl, decl);
+            const decl_index = iac.data.decl_index;
+            try sema.mod.declareDeclDependency(sema.owner_decl_index, decl_index);
 
+            const decl = sema.mod.declPtr(decl_index);
             const final_elem_ty = try decl.ty.copy(sema.arena);
-            const final_ptr_ty = try Type.ptr(sema.arena, target, .{
+            const final_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{
                 .pointee_type = final_elem_ty,
                 .mutable = var_is_mut,
                 .@"align" = iac.data.alignment,
@@ -2791,11 +2815,11 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
 
             if (var_is_mut) {
                 sema.air_values.items[value_index] = try Value.Tag.decl_ref_mut.create(sema.arena, .{
-                    .decl = decl,
+                    .decl_index = decl_index,
                     .runtime_index = block.runtime_index,
                 });
             } else {
-                sema.air_values.items[value_index] = try Value.Tag.decl_ref.create(sema.arena, decl);
+                sema.air_values.items[value_index] = try Value.Tag.decl_ref.create(sema.arena, decl_index);
             }
         },
         .inferred_alloc => {
@@ -2803,7 +2827,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
             const peer_inst_list = inferred_alloc.data.stored_inst_list.items;
             const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_inst_list, .none);
 
-            const final_ptr_ty = try Type.ptr(sema.arena, target, .{
+            const final_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{
                 .pointee_type = final_elem_ty,
                 .mutable = var_is_mut,
                 .@"align" = inferred_alloc.data.alignment,
@@ -2873,22 +2897,22 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
                 if (store_op.lhs != Air.indexToRef(bitcast_inst)) break :ct;
                 if (air_datas[bitcast_inst].ty_op.operand != Air.indexToRef(const_inst)) break :ct;
 
-                const new_decl = d: {
+                const new_decl_index = d: {
                     var anon_decl = try block.startAnonDecl(src);
                     defer anon_decl.deinit();
-                    const new_decl = try anon_decl.finish(
+                    const new_decl_index = try anon_decl.finish(
                         try final_elem_ty.copy(anon_decl.arena()),
                         try store_val.copy(anon_decl.arena()),
                         inferred_alloc.data.alignment,
                     );
-                    break :d new_decl;
+                    break :d new_decl_index;
                 };
-                try sema.mod.declareDeclDependency(sema.owner_decl, new_decl);
+                try sema.mod.declareDeclDependency(sema.owner_decl_index, new_decl_index);
 
                 // Even though we reuse the constant instruction, we still remove it from the
                 // block so that codegen does not see it.
                 block.instructions.shrinkRetainingCapacity(block.instructions.items.len - 3);
-                sema.air_values.items[value_index] = try Value.Tag.decl_ref.create(sema.arena, new_decl);
+                sema.air_values.items[value_index] = try Value.Tag.decl_ref.create(sema.arena, new_decl_index);
                 // if bitcast ty ref needs to be made const, make_ptr_const
                 // ZIR handles it later, so we can just use the ty ref here.
                 air_datas[ptr_inst].ty_pl.ty = air_datas[bitcast_inst].ty_op.ty;
@@ -3218,10 +3242,11 @@ fn validateStructInit(
         }
 
         if (root_msg) |msg| {
-            const fqn = try struct_obj.getFullyQualifiedName(gpa);
+            const mod = sema.mod;
+            const fqn = try struct_obj.getFullyQualifiedName(mod);
             defer gpa.free(fqn);
-            try sema.mod.errNoteNonLazy(
-                struct_obj.srcLoc(),
+            try mod.errNoteNonLazy(
+                struct_obj.srcLoc(mod),
                 msg,
                 "struct '{s}' declared here",
                 .{fqn},
@@ -3325,10 +3350,10 @@ fn validateStructInit(
     }
 
     if (root_msg) |msg| {
-        const fqn = try struct_obj.getFullyQualifiedName(gpa);
+        const fqn = try struct_obj.getFullyQualifiedName(sema.mod);
         defer gpa.free(fqn);
         try sema.mod.errNoteNonLazy(
-            struct_obj.srcLoc(),
+            struct_obj.srcLoc(sema.mod),
             msg,
             "struct '{s}' declared here",
             .{fqn},
@@ -3497,9 +3522,8 @@ fn failWithBadMemberAccess(
         else => unreachable,
     };
     const msg = msg: {
-        const target = sema.mod.getTarget();
         const msg = try sema.errMsg(block, field_src, "{s} '{}' has no member named '{s}'", .{
-            kw_name, agg_ty.fmt(target), field_name,
+            kw_name, agg_ty.fmt(sema.mod), field_name,
         });
         errdefer msg.destroy(sema.gpa);
         try sema.addDeclaredHereNote(msg, agg_ty);
@@ -3517,7 +3541,7 @@ fn failWithBadStructFieldAccess(
 ) CompileError {
     const gpa = sema.gpa;
 
-    const fqn = try struct_obj.getFullyQualifiedName(gpa);
+    const fqn = try struct_obj.getFullyQualifiedName(sema.mod);
     defer gpa.free(fqn);
 
     const msg = msg: {
@@ -3528,7 +3552,7 @@ fn failWithBadStructFieldAccess(
             .{ field_name, fqn },
         );
         errdefer msg.destroy(gpa);
-        try sema.mod.errNoteNonLazy(struct_obj.srcLoc(), msg, "struct declared here", .{});
+        try sema.mod.errNoteNonLazy(struct_obj.srcLoc(sema.mod), msg, "struct declared here", .{});
         break :msg msg;
     };
     return sema.failWithOwnedErrorMsg(block, msg);
@@ -3543,7 +3567,7 @@ fn failWithBadUnionFieldAccess(
 ) CompileError {
     const gpa = sema.gpa;
 
-    const fqn = try union_obj.getFullyQualifiedName(gpa);
+    const fqn = try union_obj.getFullyQualifiedName(sema.mod);
     defer gpa.free(fqn);
 
     const msg = msg: {
@@ -3554,14 +3578,14 @@ fn failWithBadUnionFieldAccess(
             .{ field_name, fqn },
         );
         errdefer msg.destroy(gpa);
-        try sema.mod.errNoteNonLazy(union_obj.srcLoc(), msg, "union declared here", .{});
+        try sema.mod.errNoteNonLazy(union_obj.srcLoc(sema.mod), msg, "union declared here", .{});
         break :msg msg;
     };
     return sema.failWithOwnedErrorMsg(block, msg);
 }
 
 fn addDeclaredHereNote(sema: *Sema, parent: *Module.ErrorMsg, decl_ty: Type) !void {
-    const src_loc = decl_ty.declSrcLocOrNull() orelse return;
+    const src_loc = decl_ty.declSrcLocOrNull(sema.mod) orelse return;
     const category = switch (decl_ty.zigTypeTag()) {
         .Union => "union",
         .Struct => "struct",
@@ -3645,7 +3669,7 @@ fn storeToInferredAlloc(
     try inferred_alloc.data.stored_inst_list.append(sema.arena, operand);
     // Create a runtime bitcast instruction with exactly the type the pointer wants.
     const target = sema.mod.getTarget();
-    const ptr_ty = try Type.ptr(sema.arena, target, .{
+    const ptr_ty = try Type.ptr(sema.arena, sema.mod, .{
         .pointee_type = operand_ty,
         .@"align" = inferred_alloc.data.alignment,
         .@"addrspace" = target_util.defaultAddressSpace(target, .local),
@@ -3670,7 +3694,7 @@ fn storeToInferredAllocComptime(
         }
         var anon_decl = try block.startAnonDecl(src);
         defer anon_decl.deinit();
-        iac.data.decl = try anon_decl.finish(
+        iac.data.decl_index = try anon_decl.finish(
             try operand_ty.copy(anon_decl.arena()),
             try operand_val.copy(anon_decl.arena()),
             iac.data.alignment,
@@ -3869,7 +3893,6 @@ fn zirCompileLog(
     const src_node = extra.data.src_node;
     const src: LazySrcLoc = .{ .node_offset = src_node };
     const args = sema.code.refSlice(extra.end, extended.small);
-    const target = sema.mod.getTarget();
 
     for (args) |arg_ref, i| {
         if (i != 0) try writer.print(", ", .{});
@@ -3878,15 +3901,15 @@ fn zirCompileLog(
         const arg_ty = sema.typeOf(arg);
         if (try sema.resolveMaybeUndefVal(block, src, arg)) |val| {
             try writer.print("@as({}, {})", .{
-                arg_ty.fmt(target), val.fmtValue(arg_ty, target),
+                arg_ty.fmt(sema.mod), val.fmtValue(arg_ty, sema.mod),
             });
         } else {
-            try writer.print("@as({}, [runtime value])", .{arg_ty.fmt(target)});
+            try writer.print("@as({}, [runtime value])", .{arg_ty.fmt(sema.mod)});
         }
     }
     try writer.print("\n", .{});
 
-    const gop = try sema.mod.compile_log_decls.getOrPut(sema.gpa, sema.owner_decl);
+    const gop = try sema.mod.compile_log_decls.getOrPut(sema.gpa, sema.owner_decl_index);
     if (!gop.found_existing) {
         gop.value_ptr.* = src_node;
     }
@@ -3996,7 +4019,8 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr
     // Ignore the result, all the relevant operations have written to c_import_buf already.
     _ = try sema.analyzeBodyBreak(&child_block, body);
 
-    const c_import_res = sema.mod.comp.cImport(c_import_buf.items) catch |err|
+    const mod = sema.mod;
+    const c_import_res = mod.comp.cImport(c_import_buf.items) catch |err|
         return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)});
 
     if (c_import_res.errors.len != 0) {
@@ -4004,12 +4028,12 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr
             const msg = try sema.errMsg(&child_block, src, "C import failed", .{});
             errdefer msg.destroy(sema.gpa);
 
-            if (!sema.mod.comp.bin_file.options.link_libc)
+            if (!mod.comp.bin_file.options.link_libc)
                 try sema.errNote(&child_block, src, msg, "libc headers not available; compilation does not link against libc", .{});
 
             for (c_import_res.errors) |_| {
                 // TODO integrate with LazySrcLoc
-                // try sema.mod.errNoteNonLazy(.{}, msg, "{s}", .{clang_err.msg_ptr[0..clang_err.msg_len]});
+                // try mod.errNoteNonLazy(.{}, msg, "{s}", .{clang_err.msg_ptr[0..clang_err.msg_len]});
                 // if (clang_err.filename_ptr) |p| p[0..clang_err.filename_len] else "(no file)",
                 // clang_err.line + 1,
                 // clang_err.column + 1,
@@ -4027,20 +4051,21 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr
         error.OutOfMemory => return error.OutOfMemory,
         else => unreachable, // we pass null for root_src_dir_path
     };
-    const std_pkg = sema.mod.main_pkg.table.get("std").?;
-    const builtin_pkg = sema.mod.main_pkg.table.get("builtin").?;
+    const std_pkg = mod.main_pkg.table.get("std").?;
+    const builtin_pkg = mod.main_pkg.table.get("builtin").?;
     try c_import_pkg.add(sema.gpa, "builtin", builtin_pkg);
     try c_import_pkg.add(sema.gpa, "std", std_pkg);
 
-    const result = sema.mod.importPkg(c_import_pkg) catch |err|
+    const result = mod.importPkg(c_import_pkg) catch |err|
         return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)});
 
-    sema.mod.astGenFile(result.file) catch |err|
+    mod.astGenFile(result.file) catch |err|
         return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)});
 
-    try sema.mod.semaFile(result.file);
-    const file_root_decl = result.file.root_decl.?;
-    try sema.mod.declareDeclDependency(sema.owner_decl, file_root_decl);
+    try mod.semaFile(result.file);
+    const file_root_decl_index = result.file.root_decl.unwrap().?;
+    const file_root_decl = mod.declPtr(file_root_decl_index);
+    try mod.declareDeclDependency(sema.owner_decl_index, file_root_decl_index);
     return sema.addConstant(file_root_decl.ty, file_root_decl.val);
 }
 
@@ -4139,6 +4164,7 @@ fn analyzeBlockBody(
     defer tracy.end();
 
     const gpa = sema.gpa;
+    const mod = sema.mod;
 
     // Blocks must terminate with noreturn instruction.
     assert(child_block.instructions.items.len != 0);
@@ -4173,16 +4199,16 @@ fn analyzeBlockBody(
 
     const type_src = src; // TODO: better source location
     const valid_rt = try sema.validateRunTimeType(child_block, type_src, resolved_ty, false);
-    const target = sema.mod.getTarget();
     if (!valid_rt) {
         const msg = msg: {
-            const msg = try sema.errMsg(child_block, type_src, "value with comptime only type '{}' depends on runtime control flow", .{resolved_ty.fmt(target)});
+            const msg = try sema.errMsg(child_block, type_src, "value with comptime only type '{}' depends on runtime control flow", .{resolved_ty.fmt(mod)});
             errdefer msg.destroy(sema.gpa);
 
             const runtime_src = child_block.runtime_cond orelse child_block.runtime_loop.?;
             try sema.errNote(child_block, runtime_src, msg, "runtime control flow here", .{});
 
-            try sema.explainWhyTypeIsComptime(child_block, type_src, msg, type_src.toSrcLoc(child_block.src_decl), resolved_ty);
+            const child_src_decl = mod.declPtr(child_block.src_decl);
+            try sema.explainWhyTypeIsComptime(child_block, type_src, msg, type_src.toSrcLoc(child_src_decl), resolved_ty);
 
             break :msg msg;
         };
@@ -4204,7 +4230,7 @@ fn analyzeBlockBody(
         const br_operand = sema.air_instructions.items(.data)[br].br.operand;
         const br_operand_src = src;
         const br_operand_ty = sema.typeOf(br_operand);
-        if (br_operand_ty.eql(resolved_ty, target)) {
+        if (br_operand_ty.eql(resolved_ty, mod)) {
             // No type coercion needed.
             continue;
         }
@@ -4262,9 +4288,9 @@ fn zirExport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
     if (extra.namespace != .none) {
         return sema.fail(block, src, "TODO: implement exporting with field access", .{});
     }
-    const decl = try sema.lookupIdentifier(block, operand_src, decl_name);
+    const decl_index = try sema.lookupIdentifier(block, operand_src, decl_name);
     const options = try sema.resolveExportOptions(block, options_src, extra.options);
-    try sema.analyzeExport(block, src, options, decl);
+    try sema.analyzeExport(block, src, options, decl_index);
 }
 
 fn zirExportValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
@@ -4278,11 +4304,11 @@ fn zirExportValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
     const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
     const operand = try sema.resolveInstConst(block, operand_src, extra.operand);
     const options = try sema.resolveExportOptions(block, options_src, extra.options);
-    const decl = switch (operand.val.tag()) {
+    const decl_index = switch (operand.val.tag()) {
         .function => operand.val.castTag(.function).?.data.owner_decl,
         else => return sema.fail(block, operand_src, "TODO implement exporting arbitrary Value objects", .{}), // TODO put this Value into an anonymous Decl and then export it.
     };
-    try sema.analyzeExport(block, src, options, decl);
+    try sema.analyzeExport(block, src, options, decl_index);
 }
 
 pub fn analyzeExport(
@@ -4290,18 +4316,18 @@ pub fn analyzeExport(
     block: *Block,
     src: LazySrcLoc,
     borrowed_options: std.builtin.ExportOptions,
-    exported_decl: *Decl,
+    exported_decl_index: Decl.Index,
 ) !void {
     const Export = Module.Export;
     const mod = sema.mod;
-    const target = mod.getTarget();
 
-    try mod.ensureDeclAnalyzed(exported_decl);
+    try mod.ensureDeclAnalyzed(exported_decl_index);
+    const exported_decl = mod.declPtr(exported_decl_index);
     // TODO run the same checks as we do for C ABI struct fields
     switch (exported_decl.ty.zigTypeTag()) {
         .Fn, .Int, .Enum, .Struct, .Union, .Array, .Float => {},
         else => return sema.fail(block, src, "unable to export type '{}'", .{
-            exported_decl.ty.fmt(target),
+            exported_decl.ty.fmt(sema.mod),
         }),
     }
 
@@ -4319,13 +4345,6 @@ pub fn analyzeExport(
     const section: ?[]const u8 = if (borrowed_options.section) |s| try gpa.dupe(u8, s) else null;
     errdefer if (section) |s| gpa.free(s);
 
-    const src_decl = block.src_decl;
-    const owner_decl = sema.owner_decl;
-
-    log.debug("exporting Decl '{s}' as symbol '{s}' from Decl '{s}'", .{
-        exported_decl.name, symbol_name, owner_decl.name,
-    });
-
     new_export.* = .{
         .options = .{
             .name = symbol_name,
@@ -4343,14 +4362,14 @@ pub fn analyzeExport(
             .spirv => .{ .spirv = {} },
             .nvptx => .{ .nvptx = {} },
         },
-        .owner_decl = owner_decl,
-        .src_decl = src_decl,
-        .exported_decl = exported_decl,
+        .owner_decl = sema.owner_decl_index,
+        .src_decl = block.src_decl,
+        .exported_decl = exported_decl_index,
         .status = .in_progress,
     };
 
     // Add to export_owners table.
-    const eo_gop = mod.export_owners.getOrPutAssumeCapacity(owner_decl);
+    const eo_gop = mod.export_owners.getOrPutAssumeCapacity(sema.owner_decl_index);
     if (!eo_gop.found_existing) {
         eo_gop.value_ptr.* = &[0]*Export{};
     }
@@ -4359,7 +4378,7 @@ pub fn analyzeExport(
     errdefer eo_gop.value_ptr.* = gpa.shrink(eo_gop.value_ptr.*, eo_gop.value_ptr.len - 1);
 
     // Add to exported_decl table.
-    const de_gop = mod.decl_exports.getOrPutAssumeCapacity(exported_decl);
+    const de_gop = mod.decl_exports.getOrPutAssumeCapacity(exported_decl_index);
     if (!de_gop.found_existing) {
         de_gop.value_ptr.* = &[0]*Export{};
     }
@@ -4381,7 +4400,8 @@ fn zirSetAlignStack(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
     const func = sema.owner_func orelse
         return sema.fail(block, src, "@setAlignStack outside function body", .{});
 
-    switch (func.owner_decl.ty.fnCallingConvention()) {
+    const fn_owner_decl = sema.mod.declPtr(func.owner_decl);
+    switch (fn_owner_decl.ty.fnCallingConvention()) {
         .Naked => return sema.fail(block, src, "@setAlignStack in naked function", .{}),
         .Inline => return sema.fail(block, src, "@setAlignStack in inline function", .{}),
         else => {},
@@ -4561,8 +4581,8 @@ fn zirDeclRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
     const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
     const src = inst_data.src();
     const decl_name = inst_data.get(sema.code);
-    const decl = try sema.lookupIdentifier(block, src, decl_name);
-    return sema.analyzeDeclRef(decl);
+    const decl_index = try sema.lookupIdentifier(block, src, decl_name);
+    return sema.analyzeDeclRef(decl_index);
 }
 
 fn zirDeclVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -4573,11 +4593,11 @@ fn zirDeclVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
     return sema.analyzeDeclVal(block, src, decl);
 }
 
-fn lookupIdentifier(sema: *Sema, block: *Block, src: LazySrcLoc, name: []const u8) !*Decl {
+fn lookupIdentifier(sema: *Sema, block: *Block, src: LazySrcLoc, name: []const u8) !Decl.Index {
     var namespace = block.namespace;
     while (true) {
-        if (try sema.lookupInNamespace(block, src, namespace, name, false)) |decl| {
-            return decl;
+        if (try sema.lookupInNamespace(block, src, namespace, name, false)) |decl_index| {
+            return decl_index;
         }
         namespace = namespace.parent orelse break;
     }
@@ -4593,12 +4613,13 @@ fn lookupInNamespace(
     namespace: *Namespace,
     ident_name: []const u8,
     observe_usingnamespace: bool,
-) CompileError!?*Decl {
+) CompileError!?Decl.Index {
     const mod = sema.mod;
 
-    const namespace_decl = namespace.getDecl();
+    const namespace_decl_index = namespace.getDeclIndex();
+    const namespace_decl = sema.mod.declPtr(namespace_decl_index);
     if (namespace_decl.analysis == .file_failure) {
-        try mod.declareDeclDependency(sema.owner_decl, namespace_decl);
+        try mod.declareDeclDependency(sema.owner_decl_index, namespace_decl_index);
         return error.AnalysisFail;
     }
 
@@ -4610,7 +4631,7 @@ fn lookupInNamespace(
         defer checked_namespaces.deinit(gpa);
 
         // Keep track of name conflicts for error notes.
-        var candidates: std.ArrayListUnmanaged(*Decl) = .{};
+        var candidates: std.ArrayListUnmanaged(Decl.Index) = .{};
         defer candidates.deinit(gpa);
 
         try checked_namespaces.put(gpa, namespace, {});
@@ -4618,23 +4639,25 @@ fn lookupInNamespace(
 
         while (check_i < checked_namespaces.count()) : (check_i += 1) {
             const check_ns = checked_namespaces.keys()[check_i];
-            if (check_ns.decls.getKeyAdapted(ident_name, Module.DeclAdapter{})) |decl| {
+            if (check_ns.decls.getKeyAdapted(ident_name, Module.DeclAdapter{ .mod = mod })) |decl_index| {
                 // Skip decls which are not marked pub, which are in a different
                 // file than the `a.b`/`@hasDecl` syntax.
+                const decl = mod.declPtr(decl_index);
                 if (decl.is_pub or src_file == decl.getFileScope()) {
-                    try candidates.append(gpa, decl);
+                    try candidates.append(gpa, decl_index);
                 }
             }
             var it = check_ns.usingnamespace_set.iterator();
             while (it.next()) |entry| {
-                const sub_usingnamespace_decl = entry.key_ptr.*;
+                const sub_usingnamespace_decl_index = entry.key_ptr.*;
+                const sub_usingnamespace_decl = mod.declPtr(sub_usingnamespace_decl_index);
                 const sub_is_pub = entry.value_ptr.*;
                 if (!sub_is_pub and src_file != sub_usingnamespace_decl.getFileScope()) {
                     // Skip usingnamespace decls which are not marked pub, which are in
                     // a different file than the `a.b`/`@hasDecl` syntax.
                     continue;
                 }
-                try sema.ensureDeclAnalyzed(sub_usingnamespace_decl);
+                try sema.ensureDeclAnalyzed(sub_usingnamespace_decl_index);
                 const ns_ty = sub_usingnamespace_decl.val.castTag(.ty).?.data;
                 const sub_ns = ns_ty.getNamespace().?;
                 try checked_namespaces.put(gpa, sub_ns, {});
@@ -4644,15 +4667,16 @@ fn lookupInNamespace(
         switch (candidates.items.len) {
             0 => {},
             1 => {
-                const decl = candidates.items[0];
-                try mod.declareDeclDependency(sema.owner_decl, decl);
-                return decl;
+                const decl_index = candidates.items[0];
+                try mod.declareDeclDependency(sema.owner_decl_index, decl_index);
+                return decl_index;
             },
             else => {
                 const msg = msg: {
                     const msg = try sema.errMsg(block, src, "ambiguous reference", .{});
                     errdefer msg.destroy(gpa);
-                    for (candidates.items) |candidate| {
+                    for (candidates.items) |candidate_index| {
+                        const candidate = mod.declPtr(candidate_index);
                         const src_loc = candidate.srcLoc();
                         try mod.errNoteNonLazy(src_loc, msg, "declared here", .{});
                     }
@@ -4661,9 +4685,9 @@ fn lookupInNamespace(
                 return sema.failWithOwnedErrorMsg(block, msg);
             },
         }
-    } else if (namespace.decls.getKeyAdapted(ident_name, Module.DeclAdapter{})) |decl| {
-        try mod.declareDeclDependency(sema.owner_decl, decl);
-        return decl;
+    } else if (namespace.decls.getKeyAdapted(ident_name, Module.DeclAdapter{ .mod = mod })) |decl_index| {
+        try mod.declareDeclDependency(sema.owner_decl_index, decl_index);
+        return decl_index;
     }
 
     log.debug("{*} ({s}) depends on non-existence of '{s}' in {*} ({s})", .{
@@ -4672,7 +4696,7 @@ fn lookupInNamespace(
     // TODO This dependency is too strong. Really, it should only be a dependency
     // on the non-existence of `ident_name` in the namespace. We can lessen the number of
     // outdated declarations by making this dependency more sophisticated.
-    try mod.declareDeclDependency(sema.owner_decl, namespace_decl);
+    try mod.declareDeclDependency(sema.owner_decl_index, namespace_decl_index);
     return null;
 }
 
@@ -4725,13 +4749,14 @@ const GenericCallAdapter = struct {
     /// Unlike comptime_args, the Type here is not always present.
     /// .generic_poison is used to communicate non-anytype parameters.
     comptime_tvs: []const TypedValue,
-    target: std.Target,
+    module: *Module,
 
     pub fn eql(ctx: @This(), adapted_key: void, other_key: *Module.Fn) bool {
         _ = adapted_key;
         // The generic function Decl is guaranteed to be the first dependency
         // of each of its instantiations.
-        const generic_owner_decl = other_key.owner_decl.dependencies.keys()[0];
+        const other_owner_decl = ctx.module.declPtr(other_key.owner_decl);
+        const generic_owner_decl = other_owner_decl.dependencies.keys()[0];
         if (ctx.generic_fn.owner_decl != generic_owner_decl) return false;
 
         const other_comptime_args = other_key.comptime_args.?;
@@ -4747,18 +4772,18 @@ const GenericCallAdapter = struct {
 
             if (this_is_anytype) {
                 // Both are anytype parameters.
-                if (!this_arg.ty.eql(other_arg.ty, ctx.target)) {
+                if (!this_arg.ty.eql(other_arg.ty, ctx.module)) {
                     return false;
                 }
                 if (this_is_comptime) {
                     // Both are comptime and anytype parameters with matching types.
-                    if (!this_arg.val.eql(other_arg.val, other_arg.ty, ctx.target)) {
+                    if (!this_arg.val.eql(other_arg.val, other_arg.ty, ctx.module)) {
                         return false;
                     }
                 }
             } else if (this_is_comptime) {
                 // Both are comptime parameters but not anytype parameters.
-                if (!this_arg.val.eql(other_arg.val, other_arg.ty, ctx.target)) {
+                if (!this_arg.val.eql(other_arg.val, other_arg.ty, ctx.module)) {
                     return false;
                 }
             }
@@ -4787,7 +4812,6 @@ fn analyzeCall(
     const mod = sema.mod;
 
     const callee_ty = sema.typeOf(func);
-    const target = sema.mod.getTarget();
     const func_ty = func_ty: {
         switch (callee_ty.zigTypeTag()) {
             .Fn => break :func_ty callee_ty,
@@ -4799,7 +4823,7 @@ fn analyzeCall(
             },
             else => {},
         }
-        return sema.fail(block, func_src, "type '{}' not a function", .{callee_ty.fmt(target)});
+        return sema.fail(block, func_src, "type '{}' not a function", .{callee_ty.fmt(sema.mod)});
     };
 
     const func_ty_info = func_ty.fnInfo();
@@ -4891,7 +4915,7 @@ fn analyzeCall(
     const result: Air.Inst.Ref = if (is_inline_call) res: {
         const func_val = try sema.resolveConstValue(block, func_src, func);
         const module_fn = switch (func_val.tag()) {
-            .decl_ref => func_val.castTag(.decl_ref).?.data.val.castTag(.function).?.data,
+            .decl_ref => mod.declPtr(func_val.castTag(.decl_ref).?.data).val.castTag(.function).?.data,
             .function => func_val.castTag(.function).?.data,
             .extern_fn => return sema.fail(block, call_src, "{s} call of extern function", .{
                 @as([]const u8, if (is_comptime_call) "comptime" else "inline"),
@@ -4922,7 +4946,8 @@ fn analyzeCall(
         // In order to save a bit of stack space, directly modify Sema rather
         // than create a child one.
         const parent_zir = sema.code;
-        sema.code = module_fn.owner_decl.getFileScope().zir;
+        const fn_owner_decl = mod.declPtr(module_fn.owner_decl);
+        sema.code = fn_owner_decl.getFileScope().zir;
         defer sema.code = parent_zir;
 
         const parent_inst_map = sema.inst_map;
@@ -4936,14 +4961,14 @@ fn analyzeCall(
         sema.func = module_fn;
         defer sema.func = parent_func;
 
-        var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, module_fn.owner_decl.src_scope);
+        var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, fn_owner_decl.src_scope);
         defer wip_captures.deinit();
 
         var child_block: Block = .{
             .parent = null,
             .sema = sema,
             .src_decl = module_fn.owner_decl,
-            .namespace = module_fn.owner_decl.src_namespace,
+            .namespace = fn_owner_decl.src_namespace,
             .wip_capture_scope = wip_captures.scope,
             .instructions = .{},
             .label = null,
@@ -4976,7 +5001,7 @@ fn analyzeCall(
         // comptime state.
         var should_memoize = true;
 
-        var new_fn_info = module_fn.owner_decl.ty.fnInfo();
+        var new_fn_info = fn_owner_decl.ty.fnInfo();
         new_fn_info.param_types = try sema.arena.alloc(Type, new_fn_info.param_types.len);
         new_fn_info.comptime_params = (try sema.arena.alloc(bool, new_fn_info.param_types.len)).ptr;
 
@@ -5073,7 +5098,7 @@ fn analyzeCall(
         const bare_return_type = try sema.analyzeAsType(&child_block, ret_ty_src, ret_ty_inst);
         // Create a fresh inferred error set type for inline/comptime calls.
         const fn_ret_ty = blk: {
-            if (module_fn.hasInferredErrorSet()) {
+            if (module_fn.hasInferredErrorSet(mod)) {
                 const node = try sema.gpa.create(Module.Fn.InferredErrorSetListNode);
                 node.data = .{ .func = module_fn };
                 if (parent_func) |some| {
@@ -5097,7 +5122,7 @@ fn analyzeCall(
         // bug generating invalid LLVM IR.
         const res2: Air.Inst.Ref = res2: {
             if (should_memoize and is_comptime_call) {
-                if (mod.memoized_calls.getContext(memoized_call_key, .{ .target = target })) |result| {
+                if (mod.memoized_calls.getContext(memoized_call_key, .{ .module = mod })) |result| {
                     const ty_inst = try sema.addType(fn_ret_ty);
                     try sema.air_values.append(gpa, result.val);
                     sema.air_instructions.set(block_inst, .{
@@ -5150,7 +5175,13 @@ fn analyzeCall(
             };
 
             if (!is_comptime_call) {
-                try sema.emitDbgInline(block, module_fn, parent_func.?, parent_func.?.owner_decl.ty, .dbg_inline_end);
+                try sema.emitDbgInline(
+                    block,
+                    module_fn,
+                    parent_func.?,
+                    mod.declPtr(parent_func.?.owner_decl).ty,
+                    .dbg_inline_end,
+                );
             }
 
             if (should_memoize and is_comptime_call) {
@@ -5172,7 +5203,7 @@ fn analyzeCall(
                     try mod.memoized_calls.putContext(gpa, memoized_call_key, .{
                         .val = try result_val.copy(arena),
                         .arena = arena_allocator.state,
-                    }, .{ .target = sema.mod.getTarget() });
+                    }, .{ .module = mod });
                     delete_memoized_call_key = false;
                 }
             }
@@ -5239,13 +5270,14 @@ fn instantiateGenericCall(
     const func_val = try sema.resolveConstValue(block, func_src, func);
     const module_fn = switch (func_val.tag()) {
         .function => func_val.castTag(.function).?.data,
-        .decl_ref => func_val.castTag(.decl_ref).?.data.val.castTag(.function).?.data,
+        .decl_ref => mod.declPtr(func_val.castTag(.decl_ref).?.data).val.castTag(.function).?.data,
         else => unreachable,
     };
     // Check the Module's generic function map with an adapted context, so that we
     // can match against `uncasted_args` rather than doing the work below to create a
     // generic Scope only to junk it if it matches an existing instantiation.
-    const namespace = module_fn.owner_decl.src_namespace;
+    const fn_owner_decl = mod.declPtr(module_fn.owner_decl);
+    const namespace = fn_owner_decl.src_namespace;
     const fn_zir = namespace.file_scope.zir;
     const fn_info = fn_zir.getFnInfo(module_fn.zir_body_inst);
     const zir_tags = fn_zir.instructions.items(.tag);
@@ -5261,7 +5293,6 @@ fn instantiateGenericCall(
     std.hash.autoHash(&hasher, @ptrToInt(module_fn));
 
     const comptime_tvs = try sema.arena.alloc(TypedValue, func_ty_info.param_types.len);
-    const target = sema.mod.getTarget();
 
     {
         var i: usize = 0;
@@ -5290,9 +5321,9 @@ fn instantiateGenericCall(
                 const arg_src = call_src; // TODO better source location
                 const arg_ty = sema.typeOf(uncasted_args[i]);
                 const arg_val = try sema.resolveValue(block, arg_src, uncasted_args[i]);
-                arg_val.hash(arg_ty, &hasher, target);
+                arg_val.hash(arg_ty, &hasher, mod);
                 if (is_anytype) {
-                    arg_ty.hashWithHasher(&hasher, target);
+                    arg_ty.hashWithHasher(&hasher, mod);
                     comptime_tvs[i] = .{
                         .ty = arg_ty,
                         .val = arg_val,
@@ -5305,7 +5336,7 @@ fn instantiateGenericCall(
                 }
             } else if (is_anytype) {
                 const arg_ty = sema.typeOf(uncasted_args[i]);
-                arg_ty.hashWithHasher(&hasher, target);
+                arg_ty.hashWithHasher(&hasher, mod);
                 comptime_tvs[i] = .{
                     .ty = arg_ty,
                     .val = Value.initTag(.generic_poison),
@@ -5328,7 +5359,7 @@ fn instantiateGenericCall(
         .precomputed_hash = precomputed_hash,
         .func_ty_info = func_ty_info,
         .comptime_tvs = comptime_tvs,
-        .target = target,
+        .module = mod,
     };
     const gop = try mod.monomorphed_funcs.getOrPutAdapted(gpa, {}, adapter);
     const callee = if (!gop.found_existing) callee: {
@@ -5343,37 +5374,40 @@ fn instantiateGenericCall(
         try namespace.anon_decls.ensureUnusedCapacity(gpa, 1);
 
         // Create a Decl for the new function.
-        const src_decl = namespace.getDecl();
+        const src_decl_index = namespace.getDeclIndex();
+        const src_decl = mod.declPtr(src_decl_index);
+        const new_decl_index = try mod.allocateNewDecl(namespace, fn_owner_decl.src_node, src_decl.src_scope);
+        errdefer mod.destroyDecl(new_decl_index);
+        const new_decl = mod.declPtr(new_decl_index);
         // TODO better names for generic function instantiations
-        const name_index = mod.getNextAnonNameIndex();
         const decl_name = try std.fmt.allocPrintZ(gpa, "{s}__anon_{d}", .{
-            module_fn.owner_decl.name, name_index,
+            fn_owner_decl.name, @enumToInt(new_decl_index),
         });
-        const new_decl = try mod.allocateNewDecl(decl_name, namespace, module_fn.owner_decl.src_node, src_decl.src_scope);
-        errdefer new_decl.destroy(mod);
-        new_decl.src_line = module_fn.owner_decl.src_line;
-        new_decl.is_pub = module_fn.owner_decl.is_pub;
-        new_decl.is_exported = module_fn.owner_decl.is_exported;
-        new_decl.has_align = module_fn.owner_decl.has_align;
-        new_decl.has_linksection_or_addrspace = module_fn.owner_decl.has_linksection_or_addrspace;
-        new_decl.@"addrspace" = module_fn.owner_decl.@"addrspace";
-        new_decl.zir_decl_index = module_fn.owner_decl.zir_decl_index;
+        new_decl.name = decl_name;
+        new_decl.src_line = fn_owner_decl.src_line;
+        new_decl.is_pub = fn_owner_decl.is_pub;
+        new_decl.is_exported = fn_owner_decl.is_exported;
+        new_decl.has_align = fn_owner_decl.has_align;
+        new_decl.has_linksection_or_addrspace = fn_owner_decl.has_linksection_or_addrspace;
+        new_decl.@"addrspace" = fn_owner_decl.@"addrspace";
+        new_decl.zir_decl_index = fn_owner_decl.zir_decl_index;
         new_decl.alive = true; // This Decl is called at runtime.
         new_decl.analysis = .in_progress;
         new_decl.generation = mod.generation;
 
-        namespace.anon_decls.putAssumeCapacityNoClobber(new_decl, {});
-        errdefer assert(namespace.anon_decls.orderedRemove(new_decl));
+        namespace.anon_decls.putAssumeCapacityNoClobber(new_decl_index, {});
+        errdefer assert(namespace.anon_decls.orderedRemove(new_decl_index));
 
         // The generic function Decl is guaranteed to be the first dependency
         // of each of its instantiations.
         assert(new_decl.dependencies.keys().len == 0);
-        try mod.declareDeclDependency(new_decl, module_fn.owner_decl);
+        try mod.declareDeclDependency(new_decl_index, module_fn.owner_decl);
         // Resolving the new function type below will possibly declare more decl dependencies
         // and so we remove them all here in case of error.
         errdefer {
-            for (new_decl.dependencies.keys()) |dep| {
-                dep.removeDependant(new_decl);
+            for (new_decl.dependencies.keys()) |dep_index| {
+                const dep = mod.declPtr(dep_index);
+                dep.removeDependant(new_decl_index);
             }
         }
 
@@ -5392,6 +5426,7 @@ fn instantiateGenericCall(
             .perm_arena = new_decl_arena_allocator,
             .code = fn_zir,
             .owner_decl = new_decl,
+            .owner_decl_index = new_decl_index,
             .func = null,
             .fn_ret_ty = Type.void,
             .owner_func = null,
@@ -5407,7 +5442,7 @@ fn instantiateGenericCall(
         var child_block: Block = .{
             .parent = null,
             .sema = &child_sema,
-            .src_decl = new_decl,
+            .src_decl = new_decl_index,
             .namespace = namespace,
             .wip_capture_scope = wip_captures.scope,
             .instructions = .{},
@@ -5564,7 +5599,7 @@ fn instantiateGenericCall(
         // Queue up a `codegen_func` work item for the new Fn. The `comptime_args` field
         // will be populated, ensuring it will have `analyzeBody` called with the ZIR
         // parameters mapped appropriately.
-        try mod.comp.bin_file.allocateDeclIndexes(new_decl);
+        try mod.comp.bin_file.allocateDeclIndexes(new_decl_index);
         try mod.comp.work_queue.writeItem(.{ .codegen_func = new_func });
 
         try new_decl.finalizeNewArena(&new_decl_arena);
@@ -5577,7 +5612,7 @@ fn instantiateGenericCall(
     try sema.requireRuntimeBlock(block, call_src);
 
     const comptime_args = callee.comptime_args.?;
-    const new_fn_info = callee.owner_decl.ty.fnInfo();
+    const new_fn_info = mod.declPtr(callee.owner_decl).ty.fnInfo();
     const runtime_args_len = @intCast(u32, new_fn_info.param_types.len);
     const runtime_args = try sema.arena.alloc(Air.Inst.Ref, runtime_args_len);
     {
@@ -5700,8 +5735,7 @@ fn zirArrayType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
     const bin_inst = sema.code.instructions.items(.data)[inst].bin;
     const len = try sema.resolveInt(block, .unneeded, bin_inst.lhs, Type.usize);
     const elem_type = try sema.resolveType(block, .unneeded, bin_inst.rhs);
-    const target = sema.mod.getTarget();
-    const array_ty = try Type.array(sema.arena, len, null, elem_type, target);
+    const array_ty = try Type.array(sema.arena, len, null, elem_type, sema.mod);
 
     return sema.addType(array_ty);
 }
@@ -5720,8 +5754,7 @@ fn zirArrayTypeSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compil
     const uncasted_sentinel = sema.resolveInst(extra.sentinel);
     const sentinel = try sema.coerce(block, elem_type, uncasted_sentinel, sentinel_src);
     const sentinel_val = try sema.resolveConstValue(block, sentinel_src, sentinel);
-    const target = sema.mod.getTarget();
-    const array_ty = try Type.array(sema.arena, len, sentinel_val, elem_type, target);
+    const array_ty = try Type.array(sema.arena, len, sentinel_val, elem_type, sema.mod);
 
     return sema.addType(array_ty);
 }
@@ -5748,14 +5781,13 @@ fn zirErrorUnionType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
     const error_set = try sema.resolveType(block, lhs_src, extra.lhs);
     const payload = try sema.resolveType(block, rhs_src, extra.rhs);
-    const target = sema.mod.getTarget();
 
     if (error_set.zigTypeTag() != .ErrorSet) {
         return sema.fail(block, lhs_src, "expected error set type, found {}", .{
-            error_set.fmt(target),
+            error_set.fmt(sema.mod),
         });
     }
-    const err_union_ty = try Type.errorUnion(sema.arena, error_set, payload, target);
+    const err_union_ty = try Type.errorUnion(sema.arena, error_set, payload, sema.mod);
     return sema.addType(err_union_ty);
 }
 
@@ -5862,11 +5894,10 @@ fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
     }
     const lhs_ty = try sema.analyzeAsType(block, lhs_src, lhs);
     const rhs_ty = try sema.analyzeAsType(block, rhs_src, rhs);
-    const target = sema.mod.getTarget();
     if (lhs_ty.zigTypeTag() != .ErrorSet)
-        return sema.fail(block, lhs_src, "expected error set type, found {}", .{lhs_ty.fmt(target)});
+        return sema.fail(block, lhs_src, "expected error set type, found {}", .{lhs_ty.fmt(sema.mod)});
     if (rhs_ty.zigTypeTag() != .ErrorSet)
-        return sema.fail(block, rhs_src, "expected error set type, found {}", .{rhs_ty.fmt(target)});
+        return sema.fail(block, rhs_src, "expected error set type, found {}", .{rhs_ty.fmt(sema.mod)});
 
     // Anything merged with anyerror is anyerror.
     if (lhs_ty.tag() == .anyerror or rhs_ty.tag() == .anyerror) {
@@ -5912,7 +5943,6 @@ fn zirEnumToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
     const operand = sema.resolveInst(inst_data.operand);
     const operand_ty = sema.typeOf(operand);
-    const target = sema.mod.getTarget();
 
     const enum_tag: Air.Inst.Ref = switch (operand_ty.zigTypeTag()) {
         .Enum => operand,
@@ -5929,7 +5959,7 @@ fn zirEnumToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
         },
         else => {
             return sema.fail(block, operand_src, "expected enum or tagged union, found {}", .{
-                operand_ty.fmt(target),
+                operand_ty.fmt(sema.mod),
             });
         },
     };
@@ -5953,7 +5983,6 @@ fn zirEnumToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
 }
 
 fn zirIntToEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
-    const target = sema.mod.getTarget();
     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
     const src = inst_data.src();
@@ -5963,7 +5992,7 @@ fn zirIntToEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
     const operand = sema.resolveInst(extra.rhs);
 
     if (dest_ty.zigTypeTag() != .Enum) {
-        return sema.fail(block, dest_ty_src, "expected enum, found {}", .{dest_ty.fmt(target)});
+        return sema.fail(block, dest_ty_src, "expected enum, found {}", .{dest_ty.fmt(sema.mod)});
     }
 
     if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |int_val| {
@@ -5973,17 +6002,17 @@ fn zirIntToEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
         if (int_val.isUndef()) {
             return sema.failWithUseOfUndef(block, operand_src);
         }
-        if (!dest_ty.enumHasInt(int_val, target)) {
+        if (!dest_ty.enumHasInt(int_val, sema.mod)) {
             const msg = msg: {
                 const msg = try sema.errMsg(
                     block,
                     src,
                     "enum '{}' has no tag with value {}",
-                    .{ dest_ty.fmt(target), int_val.fmtValue(sema.typeOf(operand), target) },
+                    .{ dest_ty.fmt(sema.mod), int_val.fmtValue(sema.typeOf(operand), sema.mod) },
                 );
                 errdefer msg.destroy(sema.gpa);
                 try sema.mod.errNoteNonLazy(
-                    dest_ty.declSrcLoc(),
+                    dest_ty.declSrcLoc(sema.mod),
                     msg,
                     "enum declared here",
                     .{},
@@ -6028,14 +6057,13 @@ fn analyzeOptionalPayloadPtr(
     const optional_ptr_ty = sema.typeOf(optional_ptr);
     assert(optional_ptr_ty.zigTypeTag() == .Pointer);
 
-    const target = sema.mod.getTarget();
     const opt_type = optional_ptr_ty.elemType();
     if (opt_type.zigTypeTag() != .Optional) {
-        return sema.fail(block, src, "expected optional type, found {}", .{opt_type.fmt(target)});
+        return sema.fail(block, src, "expected optional type, found {}", .{opt_type.fmt(sema.mod)});
     }
 
     const child_type = try opt_type.optionalChildAlloc(sema.arena);
-    const child_pointer = try Type.ptr(sema.arena, target, .{
+    const child_pointer = try Type.ptr(sema.arena, sema.mod, .{
         .pointee_type = child_type,
         .mutable = !optional_ptr_ty.isConstPtr(),
         .@"addrspace" = optional_ptr_ty.ptrAddressSpace(),
@@ -6106,8 +6134,7 @@ fn zirOptionalPayload(
                 return sema.failWithExpectedOptionalType(block, src, operand_ty);
             }
             const ptr_info = operand_ty.ptrInfo().data;
-            const target = sema.mod.getTarget();
-            break :t try Type.ptr(sema.arena, target, .{
+            break :t try Type.ptr(sema.arena, sema.mod, .{
                 .pointee_type = try ptr_info.pointee_type.copy(sema.arena),
                 .@"align" = ptr_info.@"align",
                 .@"addrspace" = ptr_info.@"addrspace",
@@ -6154,9 +6181,8 @@ fn zirErrUnionPayload(
     const operand_src = src;
     const operand_ty = sema.typeOf(operand);
     if (operand_ty.zigTypeTag() != .ErrorUnion) {
-        const target = sema.mod.getTarget();
         return sema.fail(block, operand_src, "expected error union type, found '{}'", .{
-            operand_ty.fmt(target),
+            operand_ty.fmt(sema.mod),
         });
     }
 
@@ -6205,15 +6231,14 @@ fn analyzeErrUnionPayloadPtr(
     const operand_ty = sema.typeOf(operand);
     assert(operand_ty.zigTypeTag() == .Pointer);
 
-    const target = sema.mod.getTarget();
     if (operand_ty.elemType().zigTypeTag() != .ErrorUnion) {
         return sema.fail(block, src, "expected error union type, found {}", .{
-            operand_ty.elemType().fmt(target),
+            operand_ty.elemType().fmt(sema.mod),
         });
     }
 
     const payload_ty = operand_ty.elemType().errorUnionPayload();
-    const operand_pointer_ty = try Type.ptr(sema.arena, target, .{
+    const operand_pointer_ty = try Type.ptr(sema.arena, sema.mod, .{
         .pointee_type = payload_ty,
         .mutable = !operand_ty.isConstPtr(),
         .@"addrspace" = operand_ty.ptrAddressSpace(),
@@ -6272,10 +6297,9 @@ fn zirErrUnionCode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro
     const src = inst_data.src();
     const operand = sema.resolveInst(inst_data.operand);
     const operand_ty = sema.typeOf(operand);
-    const target = sema.mod.getTarget();
     if (operand_ty.zigTypeTag() != .ErrorUnion) {
         return sema.fail(block, src, "expected error union type, found '{}'", .{
-            operand_ty.fmt(target),
+            operand_ty.fmt(sema.mod),
         });
     }
 
@@ -6302,9 +6326,8 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
     assert(operand_ty.zigTypeTag() == .Pointer);
 
     if (operand_ty.elemType().zigTypeTag() != .ErrorUnion) {
-        const target = sema.mod.getTarget();
         return sema.fail(block, src, "expected error union type, found {}", .{
-            operand_ty.elemType().fmt(target),
+            operand_ty.elemType().fmt(sema.mod),
         });
     }
 
@@ -6329,10 +6352,9 @@ fn zirEnsureErrPayloadVoid(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
     const src = inst_data.src();
     const operand = sema.resolveInst(inst_data.operand);
     const operand_ty = sema.typeOf(operand);
-    const target = sema.mod.getTarget();
     if (operand_ty.zigTypeTag() != .ErrorUnion) {
         return sema.fail(block, src, "expected error union type, found '{}'", .{
-            operand_ty.fmt(target),
+            operand_ty.fmt(sema.mod),
         });
     }
     if (operand_ty.errorUnionPayload().zigTypeTag() != .Void) {
@@ -6606,7 +6628,7 @@ fn funcCommon(
         errdefer sema.gpa.destroy(new_extern_fn);
 
         new_extern_fn.* = Module.ExternFn{
-            .owner_decl = sema.owner_decl,
+            .owner_decl = sema.owner_decl_index,
             .lib_name = null,
         };
 
@@ -6645,7 +6667,7 @@ fn funcCommon(
     new_func.* = .{
         .state = anal_state,
         .zir_body_inst = func_inst,
-        .owner_decl = sema.owner_decl,
+        .owner_decl = sema.owner_decl_index,
         .comptime_args = comptime_args,
         .anytype_args = undefined,
         .hash = hash,
@@ -6838,8 +6860,7 @@ fn zirPtrToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
     const ptr = sema.resolveInst(inst_data.operand);
     const ptr_ty = sema.typeOf(ptr);
     if (!ptr_ty.isPtrAtRuntime()) {
-        const target = sema.mod.getTarget();
-        return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty.fmt(target)});
+        return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty.fmt(sema.mod)});
     }
     if (try sema.resolveMaybeUndefVal(block, ptr_src, ptr)) |ptr_val| {
         return sema.addConstant(Type.usize, ptr_val);
@@ -7018,7 +7039,6 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
     const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
-    const target = sema.mod.getTarget();
 
     const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs);
     switch (dest_ty.zigTypeTag()) {
@@ -7038,10 +7058,10 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
         .Type,
         .Undefined,
         .Void,
-        => return sema.fail(block, dest_ty_src, "invalid type '{}' for @bitCast", .{dest_ty.fmt(target)}),
+        => return sema.fail(block, dest_ty_src, "invalid type '{}' for @bitCast", .{dest_ty.fmt(sema.mod)}),
 
         .Pointer => return sema.fail(block, dest_ty_src, "cannot @bitCast to '{}', use @ptrCast to cast to a pointer", .{
-            dest_ty.fmt(target),
+            dest_ty.fmt(sema.mod),
         }),
         .Struct, .Union => if (dest_ty.containerLayout() == .Auto) {
             const container = switch (dest_ty.zigTypeTag()) {
@@ -7050,7 +7070,7 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
                 else => unreachable,
             };
             return sema.fail(block, dest_ty_src, "cannot @bitCast to '{}', {s} does not have a guaranteed in-memory layout", .{
-                dest_ty.fmt(target), container,
+                dest_ty.fmt(sema.mod), container,
             });
         },
         .BoundFn => @panic("TODO remove this type from the language and compiler"),
@@ -7088,7 +7108,7 @@ fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
             block,
             dest_ty_src,
             "expected float type, found '{}'",
-            .{dest_ty.fmt(target)},
+            .{dest_ty.fmt(sema.mod)},
         ),
     };
 
@@ -7099,7 +7119,7 @@ fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
             block,
             operand_src,
             "expected float type, found '{}'",
-            .{operand_ty.fmt(target)},
+            .{operand_ty.fmt(sema.mod)},
         ),
     }
 
@@ -7241,7 +7261,6 @@ fn zirSwitchCapture(
     const operand_ptr = sema.resolveInst(cond_info.operand);
     const operand_ptr_ty = sema.typeOf(operand_ptr);
     const operand_ty = if (operand_is_ref) operand_ptr_ty.childType() else operand_ptr_ty;
-    const target = sema.mod.getTarget();
 
     const operand = if (operand_is_ref)
         try sema.analyzeLoad(block, operand_src, operand_ptr, operand_src)
@@ -7277,7 +7296,7 @@ fn zirSwitchCapture(
             // Previous switch validation ensured this will succeed
             const first_item_val = sema.resolveConstValue(block, .unneeded, first_item) catch unreachable;
 
-            const first_field_index = @intCast(u32, enum_ty.enumTagFieldIndex(first_item_val, target).?);
+            const first_field_index = @intCast(u32, enum_ty.enumTagFieldIndex(first_item_val, sema.mod).?);
             const first_field = union_obj.fields.values()[first_field_index];
 
             for (items[1..]) |item| {
@@ -7285,16 +7304,16 @@ fn zirSwitchCapture(
                 // Previous switch validation ensured this will succeed
                 const item_val = sema.resolveConstValue(block, .unneeded, item_ref) catch unreachable;
 
-                const field_index = enum_ty.enumTagFieldIndex(item_val, target).?;
+                const field_index = enum_ty.enumTagFieldIndex(item_val, sema.mod).?;
                 const field = union_obj.fields.values()[field_index];
-                if (!field.ty.eql(first_field.ty, target)) {
+                if (!field.ty.eql(first_field.ty, sema.mod)) {
                     const first_item_src = switch_src; // TODO better source location
                     const item_src = switch_src;
                     const msg = msg: {
                         const msg = try sema.errMsg(block, switch_src, "capture group with incompatible types", .{});
                         errdefer msg.destroy(sema.gpa);
-                        try sema.errNote(block, first_item_src, msg, "type '{}' here", .{first_field.ty.fmt(target)});
-                        try sema.errNote(block, item_src, msg, "type '{}' here", .{field.ty.fmt(target)});
+                        try sema.errNote(block, first_item_src, msg, "type '{}' here", .{first_field.ty.fmt(sema.mod)});
+                        try sema.errNote(block, item_src, msg, "type '{}' here", .{field.ty.fmt(sema.mod)});
                         break :msg msg;
                     };
                     return sema.failWithOwnedErrorMsg(block, msg);
@@ -7304,7 +7323,7 @@ fn zirSwitchCapture(
             if (is_ref) {
                 assert(operand_is_ref);
 
-                const field_ty_ptr = try Type.ptr(sema.arena, target, .{
+                const field_ty_ptr = try Type.ptr(sema.arena, sema.mod, .{
                     .pointee_type = first_field.ty,
                     .@"addrspace" = .generic,
                     .mutable = operand_ptr_ty.ptrIsMutable(),
@@ -7388,7 +7407,6 @@ fn zirSwitchCond(
     else
         operand_ptr;
     const operand_ty = sema.typeOf(operand);
-    const target = sema.mod.getTarget();
 
     switch (operand_ty.zigTypeTag()) {
         .Type,
@@ -7436,7 +7454,7 @@ fn zirSwitchCond(
         .Vector,
         .Frame,
         .AnyFrame,
-        => return sema.fail(block, src, "switch on type '{}'", .{operand_ty.fmt(target)}),
+        => return sema.fail(block, src, "switch on type '{}'", .{operand_ty.fmt(sema.mod)}),
     }
 }
 
@@ -7588,10 +7606,10 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
                                 );
                             }
                             try sema.mod.errNoteNonLazy(
-                                operand_ty.declSrcLoc(),
+                                operand_ty.declSrcLoc(sema.mod),
                                 msg,
                                 "enum '{}' declared here",
-                                .{operand_ty.fmt(target)},
+                                .{operand_ty.fmt(sema.mod)},
                             );
                             break :msg msg;
                         };
@@ -7705,10 +7723,10 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
 
                 if (maybe_msg) |msg| {
                     try sema.mod.errNoteNonLazy(
-                        operand_ty.declSrcLoc(),
+                        operand_ty.declSrcLoc(sema.mod),
                         msg,
                         "error set '{}' declared here",
-                        .{operand_ty.fmt(target)},
+                        .{operand_ty.fmt(sema.mod)},
                     );
                     return sema.failWithOwnedErrorMsg(block, msg);
                 }
@@ -7738,7 +7756,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
         },
         .Union => return sema.fail(block, src, "TODO validate switch .Union", .{}),
         .Int, .ComptimeInt => {
-            var range_set = RangeSet.init(gpa, target);
+            var range_set = RangeSet.init(gpa, sema.mod);
             defer range_set.deinit();
 
             var extra_index: usize = special.end;
@@ -7914,13 +7932,13 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
                     block,
                     src,
                     "else prong required when switching on type '{}'",
-                    .{operand_ty.fmt(target)},
+                    .{operand_ty.fmt(sema.mod)},
                 );
             }
 
             var seen_values = ValueSrcMap.initContext(gpa, .{
                 .ty = operand_ty,
-                .target = target,
+                .mod = sema.mod,
             });
             defer seen_values.deinit();
 
@@ -7985,7 +8003,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
         .ComptimeFloat,
         .Float,
         => return sema.fail(block, operand_src, "invalid switch operand type '{}'", .{
-            operand_ty.fmt(target),
+            operand_ty.fmt(sema.mod),
         }),
     }
 
@@ -8035,7 +8053,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
                 const item = sema.resolveInst(item_ref);
                 // Validation above ensured these will succeed.
                 const item_val = sema.resolveConstValue(&child_block, .unneeded, item) catch unreachable;
-                if (operand_val.eql(item_val, operand_ty, target)) {
+                if (operand_val.eql(item_val, operand_ty, sema.mod)) {
                     return sema.resolveBlockBody(block, src, &child_block, body, inst, merges);
                 }
             }
@@ -8057,7 +8075,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
                     const item = sema.resolveInst(item_ref);
                     // Validation above ensured these will succeed.
                     const item_val = sema.resolveConstValue(&child_block, .unneeded, item) catch unreachable;
-                    if (operand_val.eql(item_val, operand_ty, target)) {
+                    if (operand_val.eql(item_val, operand_ty, sema.mod)) {
                         return sema.resolveBlockBody(block, src, &child_block, body, inst, merges);
                     }
                 }
@@ -8072,8 +8090,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
                     // Validation above ensured these will succeed.
                     const first_tv = sema.resolveInstConst(&child_block, .unneeded, item_first) catch unreachable;
                     const last_tv = sema.resolveInstConst(&child_block, .unneeded, item_last) catch unreachable;
-                    if (Value.compare(operand_val, .gte, first_tv.val, operand_ty, target) and
-                        Value.compare(operand_val, .lte, last_tv.val, operand_ty, target))
+                    if (Value.compare(operand_val, .gte, first_tv.val, operand_ty, sema.mod) and
+                        Value.compare(operand_val, .lte, last_tv.val, operand_ty, sema.mod))
                     {
                         return sema.resolveBlockBody(block, src, &child_block, body, inst, merges);
                     }
@@ -8385,7 +8403,7 @@ fn resolveSwitchItemVal(
         return TypedValue{ .ty = item_ty, .val = val };
     } else |err| switch (err) {
         error.NeededSourceLocation => {
-            const src = switch_prong_src.resolve(sema.gpa, block.src_decl, switch_node_offset, range_expand);
+            const src = switch_prong_src.resolve(sema.gpa, sema.mod.declPtr(block.src_decl), switch_node_offset, range_expand);
             return TypedValue{
                 .ty = item_ty,
                 .val = try sema.resolveConstValue(block, src, item),
@@ -8434,19 +8452,18 @@ fn validateSwitchItemEnum(
     switch_prong_src: Module.SwitchProngSrc,
 ) CompileError!void {
     const item_tv = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none);
-    const target = sema.mod.getTarget();
-    const field_index = item_tv.ty.enumTagFieldIndex(item_tv.val, target) orelse {
+    const field_index = item_tv.ty.enumTagFieldIndex(item_tv.val, sema.mod) orelse {
         const msg = msg: {
-            const src = switch_prong_src.resolve(sema.gpa, block.src_decl, src_node_offset, .none);
+            const src = switch_prong_src.resolve(sema.gpa, sema.mod.declPtr(block.src_decl), src_node_offset, .none);
             const msg = try sema.errMsg(
                 block,
                 src,
                 "enum '{}' has no tag with value '{}'",
-                .{ item_tv.ty.fmt(target), item_tv.val.fmtValue(item_tv.ty, target) },
+                .{ item_tv.ty.fmt(sema.mod), item_tv.val.fmtValue(item_tv.ty, sema.mod) },
             );
             errdefer msg.destroy(sema.gpa);
             try sema.mod.errNoteNonLazy(
-                item_tv.ty.declSrcLoc(),
+                item_tv.ty.declSrcLoc(sema.mod),
                 msg,
                 "enum declared here",
                 .{},
@@ -8487,8 +8504,9 @@ fn validateSwitchDupe(
 ) CompileError!void {
     const prev_prong_src = maybe_prev_src orelse return;
     const gpa = sema.gpa;
-    const src = switch_prong_src.resolve(gpa, block.src_decl, src_node_offset, .none);
-    const prev_src = prev_prong_src.resolve(gpa, block.src_decl, src_node_offset, .none);
+    const block_src_decl = sema.mod.declPtr(block.src_decl);
+    const src = switch_prong_src.resolve(gpa, block_src_decl, src_node_offset, .none);
+    const prev_src = prev_prong_src.resolve(gpa, block_src_decl, src_node_offset, .none);
     const msg = msg: {
         const msg = try sema.errMsg(
             block,
@@ -8525,7 +8543,8 @@ fn validateSwitchItemBool(
         false_count.* += 1;
     }
     if (true_count.* + false_count.* > 2) {
-        const src = switch_prong_src.resolve(sema.gpa, block.src_decl, src_node_offset, .none);
+        const block_src_decl = sema.mod.declPtr(block.src_decl);
+        const src = switch_prong_src.resolve(sema.gpa, block_src_decl, src_node_offset, .none);
         return sema.fail(block, src, "duplicate switch value", .{});
     }
 }
@@ -8558,13 +8577,12 @@ fn validateSwitchNoRange(
     const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = src_node_offset };
     const range_src: LazySrcLoc = .{ .node_offset_switch_range = src_node_offset };
 
-    const target = sema.mod.getTarget();
     const msg = msg: {
         const msg = try sema.errMsg(
             block,
             operand_src,
             "ranges not allowed when switching on type '{}'",
-            .{operand_ty.fmt(target)},
+            .{operand_ty.fmt(sema.mod)},
         );
         errdefer msg.destroy(sema.gpa);
         try sema.errNote(
@@ -8587,7 +8605,6 @@ fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
     const unresolved_ty = try sema.resolveType(block, ty_src, extra.lhs);
     const field_name = try sema.resolveConstString(block, name_src, extra.rhs);
     const ty = try sema.resolveTypeFields(block, ty_src, unresolved_ty);
-    const target = sema.mod.getTarget();
 
     const has_field = hf: {
         if (ty.isSlice()) {
@@ -8610,7 +8627,7 @@ fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
             .Enum => ty.enumFields().contains(field_name),
             .Array => mem.eql(u8, field_name, "len"),
             else => return sema.fail(block, ty_src, "type '{}' does not support '@hasField'", .{
-                ty.fmt(target),
+                ty.fmt(sema.mod),
             }),
         };
     };
@@ -8633,7 +8650,8 @@ fn zirHasDecl(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
     try checkNamespaceType(sema, block, lhs_src, container_type);
 
     const namespace = container_type.getNamespace() orelse return Air.Inst.Ref.bool_false;
-    if (try sema.lookupInNamespace(block, src, namespace, decl_name, true)) |decl| {
+    if (try sema.lookupInNamespace(block, src, namespace, decl_name, true)) |decl_index| {
+        const decl = sema.mod.declPtr(decl_index);
         if (decl.is_pub or decl.getFileScope() == block.getFileScope()) {
             return Air.Inst.Ref.bool_true;
         }
@@ -8661,8 +8679,9 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
         },
     };
     try mod.semaFile(result.file);
-    const file_root_decl = result.file.root_decl.?;
-    try mod.declareDeclDependency(sema.owner_decl, file_root_decl);
+    const file_root_decl_index = result.file.root_decl.unwrap().?;
+    const file_root_decl = mod.declPtr(file_root_decl_index);
+    try mod.declareDeclDependency(sema.owner_decl_index, file_root_decl_index);
     return sema.addConstant(file_root_decl.ty, file_root_decl.val);
 }
 
@@ -8763,7 +8782,7 @@ fn zirShl(
                 }
                 const int_info = scalar_ty.intInfo(target);
                 const truncated = try shifted.intTrunc(lhs_ty, sema.arena, int_info.signedness, int_info.bits, target);
-                if (truncated.compare(.eq, shifted, lhs_ty, target)) {
+                if (truncated.compare(.eq, shifted, lhs_ty, sema.mod)) {
                     break :val shifted;
                 }
                 return sema.addConstUndef(lhs_ty);
@@ -8927,7 +8946,7 @@ fn zirBitNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
 
     if (scalar_type.zigTypeTag() != .Int) {
         return sema.fail(block, src, "unable to perform binary not operation on type '{}'", .{
-            operand_type.fmt(target),
+            operand_type.fmt(sema.mod),
         });
     }
 
@@ -8939,7 +8958,7 @@ fn zirBitNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
             var elem_val_buf: Value.ElemValueBuffer = undefined;
             const elems = try sema.arena.alloc(Value, vec_len);
             for (elems) |*elem, i| {
-                const elem_val = val.elemValueBuffer(i, &elem_val_buf);
+                const elem_val = val.elemValueBuffer(sema.mod, i, &elem_val_buf);
                 elem.* = try elem_val.bitwiseNot(scalar_type, sema.arena, target);
             }
             return sema.addConstant(
@@ -9047,14 +9066,13 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
 
-    const target = sema.mod.getTarget();
     const lhs_info = (try sema.getArrayCatInfo(block, lhs_src, lhs)) orelse
-        return sema.fail(block, lhs_src, "expected array, found '{}'", .{lhs_ty.fmt(target)});
+        return sema.fail(block, lhs_src, "expected array, found '{}'", .{lhs_ty.fmt(sema.mod)});
     const rhs_info = (try sema.getArrayCatInfo(block, rhs_src, rhs)) orelse
-        return sema.fail(block, rhs_src, "expected array, found '{}'", .{rhs_ty.fmt(target)});
-    if (!lhs_info.elem_type.eql(rhs_info.elem_type, target)) {
+        return sema.fail(block, rhs_src, "expected array, found '{}'", .{rhs_ty.fmt(sema.mod)});
+    if (!lhs_info.elem_type.eql(rhs_info.elem_type, sema.mod)) {
         return sema.fail(block, rhs_src, "expected array of type '{}', found '{}'", .{
-            lhs_info.elem_type.fmt(target), rhs_ty.fmt(target),
+            lhs_info.elem_type.fmt(sema.mod), rhs_ty.fmt(sema.mod),
         });
     }
 
@@ -9062,7 +9080,7 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
     // will catch this if it is a problem.
     var res_sent: ?Value = null;
     if (rhs_info.sentinel != null and lhs_info.sentinel != null) {
-        if (rhs_info.sentinel.?.eql(lhs_info.sentinel.?, lhs_info.elem_type, target)) {
+        if (rhs_info.sentinel.?.eql(lhs_info.sentinel.?, lhs_info.elem_type, sema.mod)) {
             res_sent = lhs_info.sentinel.?;
         }
     }
@@ -9084,14 +9102,14 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
             {
                 var i: usize = 0;
                 while (i < lhs_len) : (i += 1) {
-                    const val = try lhs_sub_val.elemValue(sema.arena, i);
+                    const val = try lhs_sub_val.elemValue(sema.mod, sema.arena, i);
                     buf[i] = try val.copy(anon_decl.arena());
                 }
             }
             {
                 var i: usize = 0;
                 while (i < rhs_len) : (i += 1) {
-                    const val = try rhs_sub_val.elemValue(sema.arena, i);
+                    const val = try rhs_sub_val.elemValue(sema.mod, sema.arena, i);
                     buf[lhs_len + i] = try val.copy(anon_decl.arena());
                 }
             }
@@ -9123,7 +9141,6 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
 
 fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, inst: Air.Inst.Ref) !?Type.ArrayInfo {
     const t = sema.typeOf(inst);
-    const target = sema.mod.getTarget();
     return switch (t.zigTypeTag()) {
         .Array => t.arrayInfo(),
         .Pointer => blk: {
@@ -9133,7 +9150,7 @@ fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, inst: Air.Inst.R
                 return Type.ArrayInfo{
                     .elem_type = t.childType(),
                     .sentinel = t.sentinel(),
-                    .len = val.sliceLen(target),
+                    .len = val.sliceLen(sema.mod),
                 };
             }
             if (ptrinfo.pointee_type.zigTypeTag() != .Array) return null;
@@ -9229,10 +9246,9 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
     if (lhs_ty.isTuple()) {
         return sema.analyzeTupleMul(block, inst_data.src_node, lhs, factor);
     }
-    const target = sema.mod.getTarget();
 
     const mulinfo = (try sema.getArrayCatInfo(block, lhs_src, lhs)) orelse
-        return sema.fail(block, lhs_src, "expected array, found '{}'", .{lhs_ty.fmt(target)});
+        return sema.fail(block, lhs_src, "expected array, found '{}'", .{lhs_ty.fmt(sema.mod)});
 
     const final_len_u64 = std.math.mul(u64, mulinfo.len, factor) catch
         return sema.fail(block, rhs_src, "operation results in overflow", .{});
@@ -9264,7 +9280,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
         // Optimization for the common pattern of a single element repeated N times, such
         // as zero-filling a byte array.
         const val = if (lhs_len == 1) blk: {
-            const elem_val = try lhs_sub_val.elemValue(sema.arena, 0);
+            const elem_val = try lhs_sub_val.elemValue(sema.mod, sema.arena, 0);
             const copied_val = try elem_val.copy(anon_decl.arena());
             break :blk try Value.Tag.repeated.create(anon_decl.arena(), copied_val);
         } else blk: {
@@ -9273,7 +9289,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
             while (i < factor) : (i += 1) {
                 var j: usize = 0;
                 while (j < lhs_len) : (j += 1) {
-                    const val = try lhs_sub_val.elemValue(sema.arena, j);
+                    const val = try lhs_sub_val.elemValue(sema.mod, sema.arena, j);
                     buf[lhs_len * i + j] = try val.copy(anon_decl.arena());
                 }
             }
@@ -9310,9 +9326,8 @@ fn zirNegate(
     const rhs_ty = sema.typeOf(rhs);
     const rhs_scalar_ty = rhs_ty.scalarType();
 
-    const target = sema.mod.getTarget();
     if (tag_override == .sub and rhs_scalar_ty.isUnsignedInt()) {
-        return sema.fail(block, src, "negation of type '{}'", .{rhs_ty.fmt(target)});
+        return sema.fail(block, src, "negation of type '{}'", .{rhs_ty.fmt(sema.mod)});
     }
 
     const lhs = if (rhs_ty.zigTypeTag() == .Vector)
@@ -9364,12 +9379,13 @@ fn zirOverflowArithmetic(
     const ptr = sema.resolveInst(extra.ptr);
 
     const lhs_ty = sema.typeOf(lhs);
-    const target = sema.mod.getTarget();
+    const mod = sema.mod;
+    const target = mod.getTarget();
 
     // Note, the types of lhs/rhs (also for shifting)/ptr are already correct as ensured by astgen.
     const dest_ty = lhs_ty;
     if (dest_ty.zigTypeTag() != .Int) {
-        return sema.fail(block, src, "expected integer type, found '{}'", .{dest_ty.fmt(target)});
+        return sema.fail(block, src, "expected integer type, found '{}'", .{dest_ty.fmt(mod)});
     }
 
     const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs);
@@ -9445,7 +9461,7 @@ fn zirOverflowArithmetic(
                     if (!lhs_val.isUndef()) {
                         if (lhs_val.compareWithZero(.eq)) {
                             break :result .{ .overflowed = .no, .wrapped = lhs };
-                        } else if (lhs_val.compare(.eq, Value.one, dest_ty, target)) {
+                        } else if (lhs_val.compare(.eq, Value.one, dest_ty, mod)) {
                             break :result .{ .overflowed = .no, .wrapped = rhs };
                         }
                     }
@@ -9455,7 +9471,7 @@ fn zirOverflowArithmetic(
                     if (!rhs_val.isUndef()) {
                         if (rhs_val.compareWithZero(.eq)) {
                             break :result .{ .overflowed = .no, .wrapped = rhs };
-                        } else if (rhs_val.compare(.eq, Value.one, dest_ty, target)) {
+                        } else if (rhs_val.compare(.eq, Value.one, dest_ty, mod)) {
                             break :result .{ .overflowed = .no, .wrapped = lhs };
                         }
                     }
@@ -9596,7 +9612,8 @@ fn analyzeArithmetic(
         });
     }
 
-    const target = sema.mod.getTarget();
+    const mod = sema.mod;
+    const target = mod.getTarget();
     const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs);
     const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs);
     const rs: struct { src: LazySrcLoc, air_tag: Air.Inst.Tag } = rs: {
@@ -9834,7 +9851,7 @@ fn analyzeArithmetic(
                     if (lhs_val.isUndef()) {
                         if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) {
                             if (maybe_rhs_val) |rhs_val| {
-                                if (rhs_val.compare(.neq, Value.negative_one, resolved_type, target)) {
+                                if (rhs_val.compare(.neq, Value.negative_one, resolved_type, mod)) {
                                     return sema.addConstUndef(resolved_type);
                                 }
                             }
@@ -9909,7 +9926,7 @@ fn analyzeArithmetic(
                     if (lhs_val.isUndef()) {
                         if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) {
                             if (maybe_rhs_val) |rhs_val| {
-                                if (rhs_val.compare(.neq, Value.negative_one, resolved_type, target)) {
+                                if (rhs_val.compare(.neq, Value.negative_one, resolved_type, mod)) {
                                     return sema.addConstUndef(resolved_type);
                                 }
                             }
@@ -9972,7 +9989,7 @@ fn analyzeArithmetic(
                     if (lhs_val.isUndef()) {
                         if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) {
                             if (maybe_rhs_val) |rhs_val| {
-                                if (rhs_val.compare(.neq, Value.negative_one, resolved_type, target)) {
+                                if (rhs_val.compare(.neq, Value.negative_one, resolved_type, mod)) {
                                     return sema.addConstUndef(resolved_type);
                                 }
                             }
@@ -10062,7 +10079,7 @@ fn analyzeArithmetic(
                         if (lhs_val.compareWithZero(.eq)) {
                             return sema.addConstant(resolved_type, Value.zero);
                         }
-                        if (lhs_val.compare(.eq, Value.one, resolved_type, target)) {
+                        if (lhs_val.compare(.eq, Value.one, resolved_type, mod)) {
                             return casted_rhs;
                         }
                     }
@@ -10078,7 +10095,7 @@ fn analyzeArithmetic(
                     if (rhs_val.compareWithZero(.eq)) {
                         return sema.addConstant(resolved_type, Value.zero);
                     }
-                    if (rhs_val.compare(.eq, Value.one, resolved_type, target)) {
+                    if (rhs_val.compare(.eq, Value.one, resolved_type, mod)) {
                         return casted_lhs;
                     }
                     if (maybe_lhs_val) |lhs_val| {
@@ -10113,7 +10130,7 @@ fn analyzeArithmetic(
                         if (lhs_val.compareWithZero(.eq)) {
                             return sema.addConstant(resolved_type, Value.zero);
                         }
-                        if (lhs_val.compare(.eq, Value.one, resolved_type, target)) {
+                        if (lhs_val.compare(.eq, Value.one, resolved_type, mod)) {
                             return casted_rhs;
                         }
                     }
@@ -10125,7 +10142,7 @@ fn analyzeArithmetic(
                     if (rhs_val.compareWithZero(.eq)) {
                         return sema.addConstant(resolved_type, Value.zero);
                     }
-                    if (rhs_val.compare(.eq, Value.one, resolved_type, target)) {
+                    if (rhs_val.compare(.eq, Value.one, resolved_type, mod)) {
                         return casted_lhs;
                     }
                     if (maybe_lhs_val) |lhs_val| {
@@ -10149,7 +10166,7 @@ fn analyzeArithmetic(
                         if (lhs_val.compareWithZero(.eq)) {
                             return sema.addConstant(resolved_type, Value.zero);
                         }
-                        if (lhs_val.compare(.eq, Value.one, resolved_type, target)) {
+                        if (lhs_val.compare(.eq, Value.one, resolved_type, mod)) {
                             return casted_rhs;
                         }
                     }
@@ -10161,7 +10178,7 @@ fn analyzeArithmetic(
                     if (rhs_val.compareWithZero(.eq)) {
                         return sema.addConstant(resolved_type, Value.zero);
                     }
-                    if (rhs_val.compare(.eq, Value.one, resolved_type, target)) {
+                    if (rhs_val.compare(.eq, Value.one, resolved_type, mod)) {
                         return casted_lhs;
                     }
                     if (maybe_lhs_val) |lhs_val| {
@@ -10431,7 +10448,7 @@ fn analyzePtrArithmetic(
                 if (air_tag == .ptr_sub) {
                     return sema.fail(block, op_src, "TODO implement Sema comptime pointer subtraction", .{});
                 }
-                const new_ptr_val = try ptr_val.elemPtr(ptr_ty, sema.arena, offset_int, target);
+                const new_ptr_val = try ptr_val.elemPtr(ptr_ty, sema.arena, offset_int, sema.mod);
                 return sema.addConstant(new_ptr_ty, new_ptr_val);
             } else break :rs offset_src;
         } else break :rs ptr_src;
@@ -10605,7 +10622,6 @@ fn zirCmpEq(
     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
     const lhs = sema.resolveInst(extra.lhs);
     const rhs = sema.resolveInst(extra.rhs);
-    const target = sema.mod.getTarget();
 
     const lhs_ty = sema.typeOf(lhs);
     const rhs_ty = sema.typeOf(rhs);
@@ -10630,7 +10646,7 @@ fn zirCmpEq(
 
     if (lhs_ty_tag == .Null or rhs_ty_tag == .Null) {
         const non_null_type = if (lhs_ty_tag == .Null) rhs_ty else lhs_ty;
-        return sema.fail(block, src, "comparison of '{}' with null", .{non_null_type.fmt(target)});
+        return sema.fail(block, src, "comparison of '{}' with null", .{non_null_type.fmt(sema.mod)});
     }
 
     if (lhs_ty_tag == .Union and (rhs_ty_tag == .EnumLiteral or rhs_ty_tag == .Enum)) {
@@ -10670,7 +10686,7 @@ fn zirCmpEq(
     if (lhs_ty_tag == .Type and rhs_ty_tag == .Type) {
         const lhs_as_type = try sema.analyzeAsType(block, lhs_src, lhs);
         const rhs_as_type = try sema.analyzeAsType(block, rhs_src, rhs);
-        if (lhs_as_type.eql(rhs_as_type, target) == (op == .eq)) {
+        if (lhs_as_type.eql(rhs_as_type, sema.mod) == (op == .eq)) {
             return Air.Inst.Ref.bool_true;
         } else {
             return Air.Inst.Ref.bool_false;
@@ -10747,10 +10763,9 @@ fn analyzeCmp(
     }
     const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
     const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ .override = &[_]LazySrcLoc{ lhs_src, rhs_src } });
-    const target = sema.mod.getTarget();
     if (!resolved_type.isSelfComparable(is_equality_cmp)) {
         return sema.fail(block, src, "{s} operator not allowed for type '{}'", .{
-            @tagName(op), resolved_type.fmt(target),
+            @tagName(op), resolved_type.fmt(sema.mod),
         });
     }
     const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
@@ -10768,7 +10783,6 @@ fn cmpSelf(
     rhs_src: LazySrcLoc,
 ) CompileError!Air.Inst.Ref {
     const resolved_type = sema.typeOf(casted_lhs);
-    const target = sema.mod.getTarget();
     const runtime_src: LazySrcLoc = src: {
         if (try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs)) |lhs_val| {
             if (lhs_val.isUndef()) return sema.addConstUndef(Type.bool);
@@ -10777,11 +10791,11 @@ fn cmpSelf(
 
                 if (resolved_type.zigTypeTag() == .Vector) {
                     const result_ty = try Type.vector(sema.arena, resolved_type.vectorLen(), Type.@"bool");
-                    const cmp_val = try lhs_val.compareVector(op, rhs_val, resolved_type, sema.arena, target);
+                    const cmp_val = try lhs_val.compareVector(op, rhs_val, resolved_type, sema.arena, sema.mod);
                     return sema.addConstant(result_ty, cmp_val);
                 }
 
-                if (lhs_val.compare(op, rhs_val, resolved_type, target)) {
+                if (lhs_val.compare(op, rhs_val, resolved_type, sema.mod)) {
                     return Air.Inst.Ref.bool_true;
                 } else {
                     return Air.Inst.Ref.bool_false;
@@ -10849,7 +10863,7 @@ fn zirSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
         .Null,
         .BoundFn,
         .Opaque,
-        => return sema.fail(block, src, "no size available for type '{}'", .{operand_ty.fmt(target)}),
+        => return sema.fail(block, src, "no size available for type '{}'", .{operand_ty.fmt(sema.mod)}),
 
         .Type,
         .EnumLiteral,
@@ -10892,9 +10906,9 @@ fn zirThis(
     block: *Block,
     extended: Zir.Inst.Extended.InstData,
 ) CompileError!Air.Inst.Ref {
-    const this_decl = block.namespace.getDecl();
+    const this_decl_index = block.namespace.getDeclIndex();
     const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
-    return sema.analyzeDeclVal(block, src, this_decl);
+    return sema.analyzeDeclVal(block, src, this_decl_index);
 }
 
 fn zirClosureCapture(
@@ -10927,7 +10941,7 @@ fn zirClosureGet(
 ) CompileError!Air.Inst.Ref {
     // TODO CLOSURE: Test this with inline functions
     const inst_data = sema.code.instructions.items(.data)[inst].inst_node;
-    var scope: *CaptureScope = block.src_decl.src_scope.?;
+    var scope: *CaptureScope = sema.mod.declPtr(block.src_decl).src_scope.?;
     // Note: The target closure must be in this scope list.
     // If it's not here, the zir is invalid, or the list is broken.
     const tv = while (true) {
@@ -10973,11 +10987,12 @@ fn zirBuiltinSrc(
     const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
     const extra = sema.code.extraData(Zir.Inst.LineColumn, extended.operand).data;
     const func = sema.func orelse return sema.fail(block, src, "@src outside function", .{});
+    const fn_owner_decl = sema.mod.declPtr(func.owner_decl);
 
     const func_name_val = blk: {
         var anon_decl = try block.startAnonDecl(src);
         defer anon_decl.deinit();
-        const name = std.mem.span(func.owner_decl.name);
+        const name = std.mem.span(fn_owner_decl.name);
         const bytes = try anon_decl.arena().dupe(u8, name[0 .. name.len + 1]);
         const new_decl = try anon_decl.finish(
             try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len - 1),
@@ -10990,7 +11005,7 @@ fn zirBuiltinSrc(
     const file_name_val = blk: {
         var anon_decl = try block.startAnonDecl(src);
         defer anon_decl.deinit();
-        const name = try func.owner_decl.getFileScope().fullPathZ(anon_decl.arena());
+        const name = try fn_owner_decl.getFileScope().fullPathZ(anon_decl.arena());
         const new_decl = try anon_decl.finish(
             try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), name.len),
             try Value.Tag.bytes.create(anon_decl.arena(), name[0 .. name.len + 1]),
@@ -11118,24 +11133,26 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
             }
 
             const args_val = v: {
-                const fn_info_decl = (try sema.namespaceLookup(
+                const fn_info_decl_index = (try sema.namespaceLookup(
                     block,
                     src,
                     type_info_ty.getNamespace().?,
                     "Fn",
                 )).?;
-                try sema.mod.declareDeclDependency(sema.owner_decl, fn_info_decl);
-                try sema.ensureDeclAnalyzed(fn_info_decl);
+                try sema.mod.declareDeclDependency(sema.owner_decl_index, fn_info_decl_index);
+                try sema.ensureDeclAnalyzed(fn_info_decl_index);
+                const fn_info_decl = sema.mod.declPtr(fn_info_decl_index);
                 var fn_ty_buffer: Value.ToTypeBuffer = undefined;
                 const fn_ty = fn_info_decl.val.toType(&fn_ty_buffer);
-                const param_info_decl = (try sema.namespaceLookup(
+                const param_info_decl_index = (try sema.namespaceLookup(
                     block,
                     src,
                     fn_ty.getNamespace().?,
                     "Param",
                 )).?;
-                try sema.mod.declareDeclDependency(sema.owner_decl, param_info_decl);
-                try sema.ensureDeclAnalyzed(param_info_decl);
+                try sema.mod.declareDeclDependency(sema.owner_decl_index, param_info_decl_index);
+                try sema.ensureDeclAnalyzed(param_info_decl_index);
+                const param_info_decl = sema.mod.declPtr(param_info_decl_index);
                 var param_buffer: Value.ToTypeBuffer = undefined;
                 const param_ty = param_info_decl.val.toType(&param_buffer);
                 const new_decl = try params_anon_decl.finish(
@@ -11307,14 +11324,15 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
 
             // Get the Error type
             const error_field_ty = t: {
-                const set_field_ty_decl = (try sema.namespaceLookup(
+                const set_field_ty_decl_index = (try sema.namespaceLookup(
                     block,
                     src,
                     type_info_ty.getNamespace().?,
                     "Error",
                 )).?;
-                try sema.mod.declareDeclDependency(sema.owner_decl, set_field_ty_decl);
-                try sema.ensureDeclAnalyzed(set_field_ty_decl);
+                try sema.mod.declareDeclDependency(sema.owner_decl_index, set_field_ty_decl_index);
+                try sema.ensureDeclAnalyzed(set_field_ty_decl_index);
+                const set_field_ty_decl = sema.mod.declPtr(set_field_ty_decl_index);
                 var buffer: Value.ToTypeBuffer = undefined;
                 break :t try set_field_ty_decl.val.toType(&buffer).copy(fields_anon_decl.arena());
             };
@@ -11416,14 +11434,15 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
             defer fields_anon_decl.deinit();
 
             const enum_field_ty = t: {
-                const enum_field_ty_decl = (try sema.namespaceLookup(
+                const enum_field_ty_decl_index = (try sema.namespaceLookup(
                     block,
                     src,
                     type_info_ty.getNamespace().?,
                     "EnumField",
                 )).?;
-                try sema.mod.declareDeclDependency(sema.owner_decl, enum_field_ty_decl);
-                try sema.ensureDeclAnalyzed(enum_field_ty_decl);
+                try sema.mod.declareDeclDependency(sema.owner_decl_index, enum_field_ty_decl_index);
+                try sema.ensureDeclAnalyzed(enum_field_ty_decl_index);
+                const enum_field_ty_decl = sema.mod.declPtr(enum_field_ty_decl_index);
                 var buffer: Value.ToTypeBuffer = undefined;
                 break :t try enum_field_ty_decl.val.toType(&buffer).copy(fields_anon_decl.arena());
             };
@@ -11514,14 +11533,15 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
             defer fields_anon_decl.deinit();
 
             const union_field_ty = t: {
-                const union_field_ty_decl = (try sema.namespaceLookup(
+                const union_field_ty_decl_index = (try sema.namespaceLookup(
                     block,
                     src,
                     type_info_ty.getNamespace().?,
                     "UnionField",
                 )).?;
-                try sema.mod.declareDeclDependency(sema.owner_decl, union_field_ty_decl);
-                try sema.ensureDeclAnalyzed(union_field_ty_decl);
+                try sema.mod.declareDeclDependency(sema.owner_decl_index, union_field_ty_decl_index);
+                try sema.ensureDeclAnalyzed(union_field_ty_decl_index);
+                const union_field_ty_decl = sema.mod.declPtr(union_field_ty_decl_index);
                 var buffer: Value.ToTypeBuffer = undefined;
                 break :t try union_field_ty_decl.val.toType(&buffer).copy(fields_anon_decl.arena());
             };
@@ -11621,14 +11641,15 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
             defer fields_anon_decl.deinit();
 
             const struct_field_ty = t: {
-                const struct_field_ty_decl = (try sema.namespaceLookup(
+                const struct_field_ty_decl_index = (try sema.namespaceLookup(
                     block,
                     src,
                     type_info_ty.getNamespace().?,
                     "StructField",
                 )).?;
-                try sema.mod.declareDeclDependency(sema.owner_decl, struct_field_ty_decl);
-                try sema.ensureDeclAnalyzed(struct_field_ty_decl);
+                try sema.mod.declareDeclDependency(sema.owner_decl_index, struct_field_ty_decl_index);
+                try sema.ensureDeclAnalyzed(struct_field_ty_decl_index);
+                const struct_field_ty_decl = sema.mod.declPtr(struct_field_ty_decl_index);
                 var buffer: Value.ToTypeBuffer = undefined;
                 break :t try struct_field_ty_decl.val.toType(&buffer).copy(fields_anon_decl.arena());
             };
@@ -11811,14 +11832,15 @@ fn typeInfoDecls(
     defer decls_anon_decl.deinit();
 
     const declaration_ty = t: {
-        const declaration_ty_decl = (try sema.namespaceLookup(
+        const declaration_ty_decl_index = (try sema.namespaceLookup(
             block,
             src,
             type_info_ty.getNamespace().?,
             "Declaration",
         )).?;
-        try sema.mod.declareDeclDependency(sema.owner_decl, declaration_ty_decl);
-        try sema.ensureDeclAnalyzed(declaration_ty_decl);
+        try sema.mod.declareDeclDependency(sema.owner_decl_index, declaration_ty_decl_index);
+        try sema.ensureDeclAnalyzed(declaration_ty_decl_index);
+        const declaration_ty_decl = sema.mod.declPtr(declaration_ty_decl_index);
         var buffer: Value.ToTypeBuffer = undefined;
         break :t try declaration_ty_decl.val.toType(&buffer).copy(decls_anon_decl.arena());
     };
@@ -11827,7 +11849,8 @@ fn typeInfoDecls(
     const decls_len = if (opt_namespace) |ns| ns.decls.count() else 0;
     const decls_vals = try decls_anon_decl.arena().alloc(Value, decls_len);
     for (decls_vals) |*decls_val, i| {
-        const decl = opt_namespace.?.decls.keys()[i];
+        const decl_index = opt_namespace.?.decls.keys()[i];
+        const decl = sema.mod.declPtr(decl_index);
         const name_val = v: {
             var anon_decl = try block.startAnonDecl(src);
             defer anon_decl.deinit();
@@ -11947,12 +11970,11 @@ fn log2IntType(sema: *Sema, block: *Block, operand: Type, src: LazySrcLoc) Compi
         },
         else => {},
     }
-    const target = sema.mod.getTarget();
     return sema.fail(
         block,
         src,
         "bit shifting operation expected integer type, found '{}'",
-        .{operand.fmt(target)},
+        .{operand.fmt(sema.mod)},
     );
 }
 
@@ -12426,8 +12448,7 @@ fn zirPtrTypeSimple(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
 
     const inst_data = sema.code.instructions.items(.data)[inst].ptr_type_simple;
     const elem_type = try sema.resolveType(block, .unneeded, inst_data.elem_type);
-    const target = sema.mod.getTarget();
-    const ty = try Type.ptr(sema.arena, target, .{
+    const ty = try Type.ptr(sema.arena, sema.mod, .{
         .pointee_type = elem_type,
         .@"addrspace" = .generic,
         .mutable = inst_data.is_mutable,
@@ -12466,7 +12487,7 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
         // Check if this happens to be the lazy alignment of our element type, in
         // which case we can make this 0 without resolving it.
         if (val.castTag(.lazy_align)) |payload| {
-            if (payload.data.eql(unresolved_elem_ty, target)) {
+            if (payload.data.eql(unresolved_elem_ty, sema.mod)) {
                 break :blk 0;
             }
         }
@@ -12505,7 +12526,7 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
         try sema.resolveTypeLayout(block, elem_ty_src, elem_ty);
         break :t elem_ty;
     };
-    const ty = try Type.ptr(sema.arena, target, .{
+    const ty = try Type.ptr(sema.arena, sema.mod, .{
         .pointee_type = elem_ty,
         .sentinel = sentinel,
         .@"align" = abi_align,
@@ -12754,10 +12775,10 @@ fn finishStructInit(
     const gpa = sema.gpa;
 
     if (root_msg) |msg| {
-        const fqn = try struct_obj.getFullyQualifiedName(gpa);
+        const fqn = try struct_obj.getFullyQualifiedName(sema.mod);
         defer gpa.free(fqn);
         try sema.mod.errNoteNonLazy(
-            struct_obj.srcLoc(),
+            struct_obj.srcLoc(sema.mod),
             msg,
             "struct '{s}' declared here",
             .{fqn},
@@ -12782,7 +12803,7 @@ fn finishStructInit(
 
     if (is_ref) {
         const target = sema.mod.getTarget();
-        const alloc_ty = try Type.ptr(sema.arena, target, .{
+        const alloc_ty = try Type.ptr(sema.arena, sema.mod, .{
             .pointee_type = struct_ty,
             .@"addrspace" = target_util.defaultAddressSpace(target, .local),
         });
@@ -12851,7 +12872,7 @@ fn zirStructInitAnon(
 
     if (is_ref) {
         const target = sema.mod.getTarget();
-        const alloc_ty = try Type.ptr(sema.arena, target, .{
+        const alloc_ty = try Type.ptr(sema.arena, sema.mod, .{
             .pointee_type = tuple_ty,
             .@"addrspace" = target_util.defaultAddressSpace(target, .local),
         });
@@ -12862,7 +12883,7 @@ fn zirStructInitAnon(
             const item = sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index);
             extra_index = item.end;
 
-            const field_ptr_ty = try Type.ptr(sema.arena, target, .{
+            const field_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{
                 .mutable = true,
                 .@"addrspace" = target_util.defaultAddressSpace(target, .local),
                 .pointee_type = field_ty,
@@ -12949,13 +12970,13 @@ fn zirArrayInit(
 
     if (is_ref) {
         const target = sema.mod.getTarget();
-        const alloc_ty = try Type.ptr(sema.arena, target, .{
+        const alloc_ty = try Type.ptr(sema.arena, sema.mod, .{
             .pointee_type = array_ty,
             .@"addrspace" = target_util.defaultAddressSpace(target, .local),
         });
         const alloc = try block.addTy(.alloc, alloc_ty);
 
-        const elem_ptr_ty = try Type.ptr(sema.arena, target, .{
+        const elem_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{
             .mutable = true,
             .@"addrspace" = target_util.defaultAddressSpace(target, .local),
             .pointee_type = elem_ty,
@@ -13017,14 +13038,14 @@ fn zirArrayInitAnon(
 
     if (is_ref) {
         const target = sema.mod.getTarget();
-        const alloc_ty = try Type.ptr(sema.arena, target, .{
+        const alloc_ty = try Type.ptr(sema.arena, sema.mod, .{
             .pointee_type = tuple_ty,
             .@"addrspace" = target_util.defaultAddressSpace(target, .local),
         });
         const alloc = try block.addTy(.alloc, alloc_ty);
         for (operands) |operand, i_usize| {
             const i = @intCast(u32, i_usize);
-            const field_ptr_ty = try Type.ptr(sema.arena, target, .{
+            const field_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{
                 .mutable = true,
                 .@"addrspace" = target_util.defaultAddressSpace(target, .local),
                 .pointee_type = types[i],
@@ -13096,7 +13117,6 @@ fn fieldType(
     ty_src: LazySrcLoc,
 ) CompileError!Air.Inst.Ref {
     const resolved_ty = try sema.resolveTypeFields(block, ty_src, aggregate_ty);
-    const target = sema.mod.getTarget();
     var cur_ty = resolved_ty;
     while (true) {
         switch (cur_ty.zigTypeTag()) {
@@ -13127,7 +13147,7 @@ fn fieldType(
             else => {},
         }
         return sema.fail(block, ty_src, "expected struct or union; found '{}'", .{
-            resolved_ty.fmt(target),
+            resolved_ty.fmt(sema.mod),
         });
     }
 }
@@ -13216,10 +13236,10 @@ fn zirUnaryMath(
             const scalar_ty = operand_ty.scalarType();
             switch (scalar_ty.zigTypeTag()) {
                 .ComptimeFloat, .Float => {},
-                else => return sema.fail(block, operand_src, "expected vector of floats or float type, found '{}'", .{scalar_ty.fmt(target)}),
+                else => return sema.fail(block, operand_src, "expected vector of floats or float type, found '{}'", .{scalar_ty.fmt(sema.mod)}),
             }
         },
-        else => return sema.fail(block, operand_src, "expected vector of floats or float type, found '{}'", .{operand_ty.fmt(target)}),
+        else => return sema.fail(block, operand_src, "expected vector of floats or float type, found '{}'", .{operand_ty.fmt(sema.mod)}),
     }
 
     switch (operand_ty.zigTypeTag()) {
@@ -13234,7 +13254,7 @@ fn zirUnaryMath(
                 var elem_buf: Value.ElemValueBuffer = undefined;
                 const elems = try sema.arena.alloc(Value, vec_len);
                 for (elems) |*elem, i| {
-                    const elem_val = val.elemValueBuffer(i, &elem_buf);
+                    const elem_val = val.elemValueBuffer(sema.mod, i, &elem_buf);
                     elem.* = try eval(elem_val, scalar_ty, sema.arena, target);
                 }
                 return sema.addConstant(
@@ -13267,7 +13287,7 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
     const src = inst_data.src();
     const operand = sema.resolveInst(inst_data.operand);
     const operand_ty = sema.typeOf(operand);
-    const target = sema.mod.getTarget();
+    const mod = sema.mod;
 
     try sema.resolveTypeLayout(block, operand_src, operand_ty);
     const enum_ty = switch (operand_ty.zigTypeTag()) {
@@ -13278,31 +13298,33 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
         },
         .Enum => operand_ty,
         .Union => operand_ty.unionTagType() orelse {
-            const decl = operand_ty.getOwnerDecl();
+            const decl_index = operand_ty.getOwnerDecl();
+            const decl = mod.declPtr(decl_index);
             const msg = msg: {
                 const msg = try sema.errMsg(block, src, "union '{s}' is untagged", .{
                     decl.name,
                 });
                 errdefer msg.destroy(sema.gpa);
-                try sema.mod.errNoteNonLazy(decl.srcLoc(), msg, "declared here", .{});
+                try mod.errNoteNonLazy(decl.srcLoc(), msg, "declared here", .{});
                 break :msg msg;
             };
             return sema.failWithOwnedErrorMsg(block, msg);
         },
         else => return sema.fail(block, operand_src, "expected enum or union; found {}", .{
-            operand_ty.fmt(target),
+            operand_ty.fmt(mod),
         }),
     };
-    const enum_decl = enum_ty.getOwnerDecl();
+    const enum_decl_index = enum_ty.getOwnerDecl();
     const casted_operand = try sema.coerce(block, enum_ty, operand, operand_src);
     if (try sema.resolveDefinedValue(block, operand_src, casted_operand)) |val| {
-        const field_index = enum_ty.enumTagFieldIndex(val, target) orelse {
+        const field_index = enum_ty.enumTagFieldIndex(val, mod) orelse {
+            const enum_decl = mod.declPtr(enum_decl_index);
             const msg = msg: {
                 const msg = try sema.errMsg(block, src, "no field with value {} in enum '{s}'", .{
                     casted_operand, enum_decl.name,
                 });
                 errdefer msg.destroy(sema.gpa);
-                try sema.mod.errNoteNonLazy(enum_decl.srcLoc(), msg, "declared here", .{});
+                try mod.errNoteNonLazy(enum_decl.srcLoc(), msg, "declared here", .{});
                 break :msg msg;
             };
             return sema.failWithOwnedErrorMsg(block, msg);
@@ -13317,6 +13339,7 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
 }
 
 fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
+    const mod = sema.mod;
     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
     const src = inst_data.src();
     const type_info_ty = try sema.resolveBuiltinTypeFields(block, src, "Type");
@@ -13326,8 +13349,8 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
     const val = try sema.resolveConstValue(block, operand_src, type_info);
     const union_val = val.cast(Value.Payload.Union).?.data;
     const tag_ty = type_info_ty.unionTagType().?;
-    const target = sema.mod.getTarget();
-    const tag_index = tag_ty.enumTagFieldIndex(union_val.tag, target).?;
+    const target = mod.getTarget();
+    const tag_index = tag_ty.enumTagFieldIndex(union_val.tag, mod).?;
     switch (@intToEnum(std.builtin.TypeId, tag_index)) {
         .Type => return Air.Inst.Ref.type_type,
         .Void => return Air.Inst.Ref.void_type,
@@ -13406,14 +13429,14 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
                     return sema.fail(block, src, "sentinels are only allowed on slices and unknown-length pointers", .{});
                 }
                 const sentinel_ptr_val = sentinel_val.castTag(.opt_payload).?.data;
-                const ptr_ty = try Type.ptr(sema.arena, target, .{
+                const ptr_ty = try Type.ptr(sema.arena, mod, .{
                     .@"addrspace" = .generic,
                     .pointee_type = child_ty,
                 });
                 actual_sentinel = (try sema.pointerDeref(block, src, sentinel_ptr_val, ptr_ty)).?;
             }
 
-            const ty = try Type.ptr(sema.arena, target, .{
+            const ty = try Type.ptr(sema.arena, mod, .{
                 .size = ptr_size,
                 .mutable = !is_const_val.toBool(),
                 .@"volatile" = is_volatile_val.toBool(),
@@ -13439,14 +13462,14 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
             var buffer: Value.ToTypeBuffer = undefined;
             const child_ty = try child_val.toType(&buffer).copy(sema.arena);
             const sentinel = if (sentinel_val.castTag(.opt_payload)) |p| blk: {
-                const ptr_ty = try Type.ptr(sema.arena, target, .{
+                const ptr_ty = try Type.ptr(sema.arena, mod, .{
                     .@"addrspace" = .generic,
                     .pointee_type = child_ty,
                 });
                 break :blk (try sema.pointerDeref(block, src, p.data, ptr_ty)).?;
             } else null;
 
-            const ty = try Type.array(sema.arena, len, sentinel, child_ty, target);
+            const ty = try Type.array(sema.arena, len, sentinel, child_ty, sema.mod);
             return sema.addType(ty);
         },
         .Optional => {
@@ -13483,8 +13506,9 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
             const payload_val = union_val.val.optionalValue() orelse
                 return sema.addType(Type.initTag(.anyerror));
             const slice_val = payload_val.castTag(.slice).?.data;
-            const decl = slice_val.ptr.pointerDecl().?;
-            try sema.ensureDeclAnalyzed(decl);
+            const decl_index = slice_val.ptr.pointerDecl().?;
+            try sema.ensureDeclAnalyzed(decl_index);
+            const decl = mod.declPtr(decl_index);
             const array_val = decl.val.castTag(.aggregate).?.data;
 
             var names: Module.ErrorSet.NameMap = .{};
@@ -13494,9 +13518,9 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
                 // TODO use reflection instead of magic numbers here
                 // error_set: type,
                 const name_val = struct_val[0];
-                const name_str = try name_val.toAllocatedBytes(Type.initTag(.const_slice_u8), sema.arena, target);
+                const name_str = try name_val.toAllocatedBytes(Type.initTag(.const_slice_u8), sema.arena, sema.mod);
 
-                const kv = try sema.mod.getErrorValue(name_str);
+                const kv = try mod.getErrorValue(name_str);
                 names.putAssumeCapacityNoClobber(kv.key, {});
             }
 
@@ -13518,7 +13542,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
             const is_tuple_val = struct_val[3];
 
             // Decls
-            if (decls_val.sliceLen(target) > 0) {
+            if (decls_val.sliceLen(mod) > 0) {
                 return sema.fail(block, src, "reified structs must have no decls", .{});
             }
 
@@ -13548,11 +13572,10 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
             }
 
             // Decls
-            if (decls_val.sliceLen(target) > 0) {
+            if (decls_val.sliceLen(mod) > 0) {
                 return sema.fail(block, src, "reified enums must have no decls", .{});
             }
 
-            const mod = sema.mod;
             const gpa = sema.gpa;
             var new_decl_arena = std.heap.ArenaAllocator.init(gpa);
             errdefer new_decl_arena.deinit();
@@ -13572,20 +13595,20 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
             };
             const enum_ty = Type.initPayload(&enum_ty_payload.base);
             const enum_val = try Value.Tag.ty.create(new_decl_arena_allocator, enum_ty);
-            const type_name = try sema.createTypeName(block, .anon, "enum");
-            const new_decl = try mod.createAnonymousDeclNamed(block, .{
+            const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{
                 .ty = Type.type,
                 .val = enum_val,
-            }, type_name);
+            }, .anon, "enum");
+            const new_decl = mod.declPtr(new_decl_index);
             new_decl.owns_tv = true;
-            errdefer mod.abortAnonDecl(new_decl);
+            errdefer mod.abortAnonDecl(new_decl_index);
 
             // Enum tag type
             var buffer: Value.ToTypeBuffer = undefined;
             const int_tag_ty = try tag_type_val.toType(&buffer).copy(new_decl_arena_allocator);
 
             enum_obj.* = .{
-                .owner_decl = new_decl,
+                .owner_decl = new_decl_index,
                 .tag_ty = int_tag_ty,
                 .tag_ty_inferred = false,
                 .fields = .{},
@@ -13599,17 +13622,17 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
             };
 
             // Fields
-            const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(target));
+            const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(mod));
             if (fields_len > 0) {
                 try enum_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len);
                 try enum_obj.values.ensureTotalCapacityContext(new_decl_arena_allocator, fields_len, .{
                     .ty = enum_obj.tag_ty,
-                    .target = target,
+                    .mod = mod,
                 });
 
                 var i: usize = 0;
                 while (i < fields_len) : (i += 1) {
-                    const elem_val = try fields_val.elemValue(sema.arena, i);
+                    const elem_val = try fields_val.elemValue(sema.mod, sema.arena, i);
                     const field_struct_val = elem_val.castTag(.aggregate).?.data;
                     // TODO use reflection instead of magic numbers here
                     // name: []const u8
@@ -13620,7 +13643,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
                     const field_name = try name_val.toAllocatedBytes(
                         Type.initTag(.const_slice_u8),
                         new_decl_arena_allocator,
-                        target,
+                        sema.mod,
                     );
 
                     const gop = enum_obj.fields.getOrPutAssumeCapacity(field_name);
@@ -13632,13 +13655,13 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
                     const copied_tag_val = try value_val.copy(new_decl_arena_allocator);
                     enum_obj.values.putAssumeCapacityNoClobberContext(copied_tag_val, {}, .{
                         .ty = enum_obj.tag_ty,
-                        .target = target,
+                        .mod = mod,
                     });
                 }
             }
 
             try new_decl.finalizeNewArena(&new_decl_arena);
-            return sema.analyzeDeclVal(block, src, new_decl);
+            return sema.analyzeDeclVal(block, src, new_decl_index);
         },
         .Opaque => {
             const struct_val = union_val.val.castTag(.aggregate).?.data;
@@ -13646,11 +13669,10 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
             const decls_val = struct_val[0];
 
             // Decls
-            if (decls_val.sliceLen(target) > 0) {
+            if (decls_val.sliceLen(mod) > 0) {
                 return sema.fail(block, src, "reified opaque must have no decls", .{});
             }
 
-            const mod = sema.mod;
             var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
             errdefer new_decl_arena.deinit();
             const new_decl_arena_allocator = new_decl_arena.allocator();
@@ -13663,16 +13685,16 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
             };
             const opaque_ty = Type.initPayload(&opaque_ty_payload.base);
             const opaque_val = try Value.Tag.ty.create(new_decl_arena_allocator, opaque_ty);
-            const type_name = try sema.createTypeName(block, .anon, "opaque");
-            const new_decl = try mod.createAnonymousDeclNamed(block, .{
+            const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{
                 .ty = Type.type,
                 .val = opaque_val,
-            }, type_name);
+            }, .anon, "opaque");
+            const new_decl = mod.declPtr(new_decl_index);
             new_decl.owns_tv = true;
-            errdefer mod.abortAnonDecl(new_decl);
+            errdefer mod.abortAnonDecl(new_decl_index);
 
             opaque_obj.* = .{
-                .owner_decl = new_decl,
+                .owner_decl = new_decl_index,
                 .node_offset = src.node_offset,
                 .namespace = .{
                     .parent = block.namespace,
@@ -13682,7 +13704,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
             };
 
             try new_decl.finalizeNewArena(&new_decl_arena);
-            return sema.analyzeDeclVal(block, src, new_decl);
+            return sema.analyzeDeclVal(block, src, new_decl_index);
         },
         .Union => {
             // TODO use reflection instead of magic numbers here
@@ -13697,7 +13719,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
             const decls_val = struct_val[3];
 
             // Decls
-            if (decls_val.sliceLen(target) > 0) {
+            if (decls_val.sliceLen(mod) > 0) {
                 return sema.fail(block, src, "reified unions must have no decls", .{});
             }
 
@@ -13714,15 +13736,15 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
             };
             const union_ty = Type.initPayload(&union_payload.base);
             const new_union_val = try Value.Tag.ty.create(new_decl_arena_allocator, union_ty);
-            const type_name = try sema.createTypeName(block, .anon, "union");
-            const new_decl = try sema.mod.createAnonymousDeclNamed(block, .{
+            const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{
                 .ty = Type.type,
                 .val = new_union_val,
-            }, type_name);
+            }, .anon, "union");
+            const new_decl = mod.declPtr(new_decl_index);
             new_decl.owns_tv = true;
-            errdefer sema.mod.abortAnonDecl(new_decl);
+            errdefer mod.abortAnonDecl(new_decl_index);
             union_obj.* = .{
-                .owner_decl = new_decl,
+                .owner_decl = new_decl_index,
                 .tag_ty = Type.initTag(.@"null"),
                 .fields = .{},
                 .node_offset = src.node_offset,
@@ -13737,7 +13759,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
             };
 
             // Tag type
-            const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(target));
+            const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(mod));
             union_obj.tag_ty = if (tag_type_val.optionalValue()) |payload_val| blk: {
                 var buffer: Value.ToTypeBuffer = undefined;
                 break :blk try payload_val.toType(&buffer).copy(new_decl_arena_allocator);
@@ -13749,7 +13771,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
 
                 var i: usize = 0;
                 while (i < fields_len) : (i += 1) {
-                    const elem_val = try fields_val.elemValue(sema.arena, i);
+                    const elem_val = try fields_val.elemValue(sema.mod, sema.arena, i);
                     const field_struct_val = elem_val.castTag(.aggregate).?.data;
                     // TODO use reflection instead of magic numbers here
                     // name: []const u8
@@ -13762,7 +13784,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
                     const field_name = try name_val.toAllocatedBytes(
                         Type.initTag(.const_slice_u8),
                         new_decl_arena_allocator,
-                        target,
+                        sema.mod,
                     );
 
                     const gop = union_obj.fields.getOrPutAssumeCapacity(field_name);
@@ -13780,7 +13802,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
             }
 
             try new_decl.finalizeNewArena(&new_decl_arena);
-            return sema.analyzeDeclVal(block, src, new_decl);
+            return sema.analyzeDeclVal(block, src, new_decl_index);
         },
         .Fn => return sema.fail(block, src, "TODO: Sema.zirReify for Fn", .{}),
         .BoundFn => @panic("TODO delete BoundFn from the language"),
@@ -13794,9 +13816,7 @@ fn reifyTuple(
     src: LazySrcLoc,
     fields_val: Value,
 ) CompileError!Air.Inst.Ref {
-    const target = sema.mod.getTarget();
-
-    const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(target));
+    const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(sema.mod));
     if (fields_len == 0) return sema.addType(Type.initTag(.empty_struct_literal));
 
     const types = try sema.arena.alloc(Type, fields_len);
@@ -13808,7 +13828,7 @@ fn reifyTuple(
 
     var i: usize = 0;
     while (i < fields_len) : (i += 1) {
-        const elem_val = try fields_val.elemValue(sema.arena, i);
+        const elem_val = try fields_val.elemValue(sema.mod, sema.arena, i);
         const field_struct_val = elem_val.castTag(.aggregate).?.data;
         // TODO use reflection instead of magic numbers here
         // name: []const u8
@@ -13821,7 +13841,7 @@ fn reifyTuple(
         const field_name = try name_val.toAllocatedBytes(
             Type.initTag(.const_slice_u8),
             sema.arena,
-            target,
+            sema.mod,
         );
 
         const field_index = std.fmt.parseUnsigned(u32, field_name, 10) catch |err| {
@@ -13850,7 +13870,7 @@ fn reifyTuple(
 
         const default_val = if (default_value_val.optionalValue()) |opt_val| blk: {
             const payload_val = if (opt_val.pointerDecl()) |opt_decl|
-                opt_decl.val
+                sema.mod.declPtr(opt_decl).val
             else
                 opt_val;
             break :blk try payload_val.copy(sema.arena);
@@ -13883,15 +13903,16 @@ fn reifyStruct(
     const struct_obj = try new_decl_arena_allocator.create(Module.Struct);
     const struct_ty = try Type.Tag.@"struct".create(new_decl_arena_allocator, struct_obj);
     const new_struct_val = try Value.Tag.ty.create(new_decl_arena_allocator, struct_ty);
-    const type_name = try sema.createTypeName(block, .anon, "struct");
-    const new_decl = try sema.mod.createAnonymousDeclNamed(block, .{
+    const mod = sema.mod;
+    const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, .{
         .ty = Type.type,
         .val = new_struct_val,
-    }, type_name);
+    }, .anon, "struct");
+    const new_decl = mod.declPtr(new_decl_index);
     new_decl.owns_tv = true;
-    errdefer sema.mod.abortAnonDecl(new_decl);
+    errdefer mod.abortAnonDecl(new_decl_index);
     struct_obj.* = .{
-        .owner_decl = new_decl,
+        .owner_decl = new_decl_index,
         .fields = .{},
         .node_offset = src.node_offset,
         .zir_index = inst,
@@ -13905,14 +13926,14 @@ fn reifyStruct(
         },
     };
 
-    const target = sema.mod.getTarget();
+    const target = mod.getTarget();
 
     // Fields
-    const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(target));
+    const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(mod));
     try struct_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len);
     var i: usize = 0;
     while (i < fields_len) : (i += 1) {
-        const elem_val = try fields_val.elemValue(sema.arena, i);
+        const elem_val = try fields_val.elemValue(sema.mod, sema.arena, i);
         const field_struct_val = elem_val.castTag(.aggregate).?.data;
         // TODO use reflection instead of magic numbers here
         // name: []const u8
@@ -13929,7 +13950,7 @@ fn reifyStruct(
         const field_name = try name_val.toAllocatedBytes(
             Type.initTag(.const_slice_u8),
             new_decl_arena_allocator,
-            target,
+            mod,
         );
 
         const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name);
@@ -13940,7 +13961,7 @@ fn reifyStruct(
 
         const default_val = if (default_value_val.optionalValue()) |opt_val| blk: {
             const payload_val = if (opt_val.pointerDecl()) |opt_decl|
-                opt_decl.val
+                mod.declPtr(opt_decl).val
             else
                 opt_val;
             break :blk try payload_val.copy(new_decl_arena_allocator);
@@ -13957,7 +13978,7 @@ fn reifyStruct(
     }
 
     try new_decl.finalizeNewArena(&new_decl_arena);
-    return sema.analyzeDeclVal(block, src, new_decl);
+    return sema.analyzeDeclVal(block, src, new_decl_index);
 }
 
 fn zirTypeName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -13968,8 +13989,7 @@ fn zirTypeName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
     var anon_decl = try block.startAnonDecl(LazySrcLoc.unneeded);
     defer anon_decl.deinit();
 
-    const target = sema.mod.getTarget();
-    const bytes = try ty.nameAllocArena(anon_decl.arena(), target);
+    const bytes = try ty.nameAllocArena(anon_decl.arena(), sema.mod);
 
     const new_decl = try anon_decl.finish(
         try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len),
@@ -14010,7 +14030,7 @@ fn zirFloatToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
             error.FloatCannotFit => {
                 return sema.fail(block, operand_src, "integer value {d} cannot be stored in type '{}'", .{
                     std.math.floor(val.toFloat(f64)),
-                    dest_ty.fmt(target),
+                    dest_ty.fmt(sema.mod),
                 });
             },
             else => |e| return e,
@@ -14064,9 +14084,9 @@ fn zirIntToPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
     if (try sema.resolveDefinedValue(block, operand_src, operand_coerced)) |val| {
         const addr = val.toUnsignedInt(target);
         if (!type_res.isAllowzeroPtr() and addr == 0)
-            return sema.fail(block, operand_src, "pointer type '{}' does not allow address zero", .{type_res.fmt(target)});
+            return sema.fail(block, operand_src, "pointer type '{}' does not allow address zero", .{type_res.fmt(sema.mod)});
         if (addr != 0 and addr % ptr_align != 0)
-            return sema.fail(block, operand_src, "pointer type '{}' requires aligned address", .{type_res.fmt(target)});
+            return sema.fail(block, operand_src, "pointer type '{}' requires aligned address", .{type_res.fmt(sema.mod)});
 
         const val_payload = try sema.arena.create(Value.Payload.U64);
         val_payload.* = .{
@@ -14110,7 +14130,6 @@ fn zirErrSetCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
     const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs);
     const operand = sema.resolveInst(extra.rhs);
     const operand_ty = sema.typeOf(operand);
-    const target = sema.mod.getTarget();
     try sema.checkErrorSetType(block, dest_ty_src, dest_ty);
     try sema.checkErrorSetType(block, operand_src, operand_ty);
 
@@ -14124,7 +14143,7 @@ fn zirErrSetCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
                     block,
                     src,
                     "error.{s} not a member of error set '{}'",
-                    .{ error_name, dest_ty.fmt(target) },
+                    .{ error_name, dest_ty.fmt(sema.mod) },
                 );
             }
         }
@@ -14178,11 +14197,11 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
             var buf: Type.Payload.ElemType = undefined;
             var dest_ptr_info = dest_ty.optionalChild(&buf).ptrInfo().data;
             dest_ptr_info.@"align" = operand_align;
-            break :blk try Type.optional(sema.arena, try Type.ptr(sema.arena, target, dest_ptr_info));
+            break :blk try Type.optional(sema.arena, try Type.ptr(sema.arena, sema.mod, dest_ptr_info));
         } else {
             var dest_ptr_info = dest_ty.ptrInfo().data;
             dest_ptr_info.@"align" = operand_align;
-            break :blk try Type.ptr(sema.arena, target, dest_ptr_info);
+            break :blk try Type.ptr(sema.arena, sema.mod, dest_ptr_info);
         }
     };
 
@@ -14235,7 +14254,7 @@ fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
 
         if (operand_info.signedness != dest_info.signedness) {
             return sema.fail(block, operand_src, "expected {s} integer type, found '{}'", .{
-                @tagName(dest_info.signedness), operand_ty.fmt(target),
+                @tagName(dest_info.signedness), operand_ty.fmt(sema.mod),
             });
         }
         if (operand_info.bits < dest_info.bits) {
@@ -14244,7 +14263,7 @@ fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                     block,
                     src,
                     "destination type '{}' has more bits than source type '{}'",
-                    .{ dest_ty.fmt(target), operand_ty.fmt(target) },
+                    .{ dest_ty.fmt(sema.mod), operand_ty.fmt(sema.mod) },
                 );
                 errdefer msg.destroy(sema.gpa);
                 try sema.errNote(block, dest_ty_src, msg, "destination type has {d} bits", .{
@@ -14270,7 +14289,7 @@ fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
         var elem_buf: Value.ElemValueBuffer = undefined;
         const elems = try sema.arena.alloc(Value, operand_ty.vectorLen());
         for (elems) |*elem, i| {
-            const elem_val = val.elemValueBuffer(i, &elem_buf);
+            const elem_val = val.elemValueBuffer(sema.mod, i, &elem_buf);
             elem.* = try elem_val.intTrunc(operand_scalar_ty, sema.arena, dest_info.signedness, dest_info.bits, target);
         }
         return sema.addConstant(
@@ -14302,8 +14321,7 @@ fn zirAlignCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
     // TODO insert safety check that the alignment is correct
 
     const ptr_info = ptr_ty.ptrInfo().data;
-    const target = sema.mod.getTarget();
-    const dest_ty = try Type.ptr(sema.arena, target, .{
+    const dest_ty = try Type.ptr(sema.arena, sema.mod, .{
         .pointee_type = ptr_info.pointee_type,
         .@"align" = dest_align,
         .@"addrspace" = ptr_info.@"addrspace",
@@ -14346,7 +14364,7 @@ fn zirBitCount(
                 const elems = try sema.arena.alloc(Value, vec_len);
                 const scalar_ty = operand_ty.scalarType();
                 for (elems) |*elem, i| {
-                    const elem_val = val.elemValueBuffer(i, &elem_buf);
+                    const elem_val = val.elemValueBuffer(sema.mod, i, &elem_buf);
                     const count = comptimeOp(elem_val, scalar_ty, target);
                     elem.* = try Value.Tag.int_u64.create(sema.arena, count);
                 }
@@ -14386,7 +14404,7 @@ fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
             block,
             ty_src,
             "@byteSwap requires the number of bits to be evenly divisible by 8, but {} has {} bits",
-            .{ scalar_ty.fmt(target), bits },
+            .{ scalar_ty.fmt(sema.mod), bits },
         );
     }
 
@@ -14414,7 +14432,7 @@ fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                 var elem_buf: Value.ElemValueBuffer = undefined;
                 const elems = try sema.arena.alloc(Value, vec_len);
                 for (elems) |*elem, i| {
-                    const elem_val = val.elemValueBuffer(i, &elem_buf);
+                    const elem_val = val.elemValueBuffer(sema.mod, i, &elem_buf);
                     elem.* = try elem_val.byteSwap(operand_ty, target, sema.arena);
                 }
                 return sema.addConstant(
@@ -14462,7 +14480,7 @@ fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
                 var elem_buf: Value.ElemValueBuffer = undefined;
                 const elems = try sema.arena.alloc(Value, vec_len);
                 for (elems) |*elem, i| {
-                    const elem_val = val.elemValueBuffer(i, &elem_buf);
+                    const elem_val = val.elemValueBuffer(sema.mod, i, &elem_buf);
                     elem.* = try elem_val.bitReverse(operand_ty, target, sema.arena);
                 }
                 return sema.addConstant(
@@ -14506,7 +14524,7 @@ fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u6
             block,
             lhs_src,
             "expected struct type, found '{}'",
-            .{ty.fmt(target)},
+            .{ty.fmt(sema.mod)},
         );
     }
 
@@ -14516,7 +14534,7 @@ fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u6
             block,
             rhs_src,
             "struct '{}' has no field '{s}'",
-            .{ ty.fmt(target), field_name },
+            .{ ty.fmt(sema.mod), field_name },
         );
     };
 
@@ -14542,20 +14560,18 @@ fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u6
 }
 
 fn checkNamespaceType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void {
-    const target = sema.mod.getTarget();
     switch (ty.zigTypeTag()) {
         .Struct, .Enum, .Union, .Opaque => return,
-        else => return sema.fail(block, src, "expected struct, enum, union, or opaque; found '{}'", .{ty.fmt(target)}),
+        else => return sema.fail(block, src, "expected struct, enum, union, or opaque; found '{}'", .{ty.fmt(sema.mod)}),
     }
 }
 
 /// Returns `true` if the type was a comptime_int.
 fn checkIntType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!bool {
-    const target = sema.mod.getTarget();
     switch (try ty.zigTypeTagOrPoison()) {
         .ComptimeInt => return true,
         .Int => return false,
-        else => return sema.fail(block, src, "expected integer type, found '{}'", .{ty.fmt(target)}),
+        else => return sema.fail(block, src, "expected integer type, found '{}'", .{ty.fmt(sema.mod)}),
     }
 }
 
@@ -14565,7 +14581,6 @@ fn checkPtrOperand(
     ty_src: LazySrcLoc,
     ty: Type,
 ) CompileError!void {
-    const target = sema.mod.getTarget();
     switch (ty.zigTypeTag()) {
         .Pointer => return,
         .Fn => {
@@ -14574,7 +14589,7 @@ fn checkPtrOperand(
                     block,
                     ty_src,
                     "expected pointer, found {}",
-                    .{ty.fmt(target)},
+                    .{ty.fmt(sema.mod)},
                 );
                 errdefer msg.destroy(sema.gpa);
 
@@ -14587,7 +14602,7 @@ fn checkPtrOperand(
         .Optional => if (ty.isPtrLikeOptional()) return,
         else => {},
     }
-    return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{ty.fmt(target)});
+    return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{ty.fmt(sema.mod)});
 }
 
 fn checkPtrType(
@@ -14596,7 +14611,6 @@ fn checkPtrType(
     ty_src: LazySrcLoc,
     ty: Type,
 ) CompileError!void {
-    const target = sema.mod.getTarget();
     switch (ty.zigTypeTag()) {
         .Pointer => return,
         .Fn => {
@@ -14605,7 +14619,7 @@ fn checkPtrType(
                     block,
                     ty_src,
                     "expected pointer type, found '{}'",
-                    .{ty.fmt(target)},
+                    .{ty.fmt(sema.mod)},
                 );
                 errdefer msg.destroy(sema.gpa);
 
@@ -14618,7 +14632,7 @@ fn checkPtrType(
         .Optional => if (ty.isPtrLikeOptional()) return,
         else => {},
     }
-    return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{ty.fmt(target)});
+    return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{ty.fmt(sema.mod)});
 }
 
 fn checkVectorElemType(
@@ -14631,8 +14645,7 @@ fn checkVectorElemType(
         .Int, .Float, .Bool => return,
         else => if (ty.isPtrAtRuntime()) return,
     }
-    const target = sema.mod.getTarget();
-    return sema.fail(block, ty_src, "expected integer, float, bool, or pointer for the vector element type; found '{}'", .{ty.fmt(target)});
+    return sema.fail(block, ty_src, "expected integer, float, bool, or pointer for the vector element type; found '{}'", .{ty.fmt(sema.mod)});
 }
 
 fn checkFloatType(
@@ -14641,10 +14654,9 @@ fn checkFloatType(
     ty_src: LazySrcLoc,
     ty: Type,
 ) CompileError!void {
-    const target = sema.mod.getTarget();
     switch (ty.zigTypeTag()) {
         .ComptimeInt, .ComptimeFloat, .Float => {},
-        else => return sema.fail(block, ty_src, "expected float type, found '{}'", .{ty.fmt(target)}),
+        else => return sema.fail(block, ty_src, "expected float type, found '{}'", .{ty.fmt(sema.mod)}),
     }
 }
 
@@ -14654,14 +14666,13 @@ fn checkNumericType(
     ty_src: LazySrcLoc,
     ty: Type,
 ) CompileError!void {
-    const target = sema.mod.getTarget();
     switch (ty.zigTypeTag()) {
         .ComptimeFloat, .Float, .ComptimeInt, .Int => {},
         .Vector => switch (ty.childType().zigTypeTag()) {
             .ComptimeFloat, .Float, .ComptimeInt, .Int => {},
             else => |t| return sema.fail(block, ty_src, "expected number, found '{}'", .{t}),
         },
-        else => return sema.fail(block, ty_src, "expected number, found '{}'", .{ty.fmt(target)}),
+        else => return sema.fail(block, ty_src, "expected number, found '{}'", .{ty.fmt(sema.mod)}),
     }
 }
 
@@ -14697,7 +14708,7 @@ fn checkAtomicOperandType(
                 block,
                 ty_src,
                 "expected bool, integer, float, enum, or pointer type; found {}",
-                .{ty.fmt(target)},
+                .{ty.fmt(sema.mod)},
             );
         },
     };
@@ -14761,7 +14772,6 @@ fn checkIntOrVector(
     operand_src: LazySrcLoc,
 ) CompileError!Type {
     const operand_ty = sema.typeOf(operand);
-    const target = sema.mod.getTarget();
     switch (try operand_ty.zigTypeTagOrPoison()) {
         .Int => return operand_ty,
         .Vector => {
@@ -14769,12 +14779,12 @@ fn checkIntOrVector(
             switch (try elem_ty.zigTypeTagOrPoison()) {
                 .Int => return elem_ty,
                 else => return sema.fail(block, operand_src, "expected vector of integers; found vector of '{}'", .{
-                    elem_ty.fmt(target),
+                    elem_ty.fmt(sema.mod),
                 }),
             }
         },
         else => return sema.fail(block, operand_src, "expected integer or vector, found '{}'", .{
-            operand_ty.fmt(target),
+            operand_ty.fmt(sema.mod),
         }),
     }
 }
@@ -14786,7 +14796,6 @@ fn checkIntOrVectorAllowComptime(
     operand_src: LazySrcLoc,
 ) CompileError!Type {
     const operand_ty = sema.typeOf(operand);
-    const target = sema.mod.getTarget();
     switch (try operand_ty.zigTypeTagOrPoison()) {
         .Int, .ComptimeInt => return operand_ty,
         .Vector => {
@@ -14794,21 +14803,20 @@ fn checkIntOrVectorAllowComptime(
             switch (try elem_ty.zigTypeTagOrPoison()) {
                 .Int, .ComptimeInt => return elem_ty,
                 else => return sema.fail(block, operand_src, "expected vector of integers; found vector of '{}'", .{
-                    elem_ty.fmt(target),
+                    elem_ty.fmt(sema.mod),
                 }),
             }
         },
         else => return sema.fail(block, operand_src, "expected integer or vector, found '{}'", .{
-            operand_ty.fmt(target),
+            operand_ty.fmt(sema.mod),
         }),
     }
 }
 
 fn checkErrorSetType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void {
-    const target = sema.mod.getTarget();
     switch (ty.zigTypeTag()) {
         .ErrorSet => return,
-        else => return sema.fail(block, src, "expected error set type, found '{}'", .{ty.fmt(target)}),
+        else => return sema.fail(block, src, "expected error set type, found '{}'", .{ty.fmt(sema.mod)}),
     }
 }
 
@@ -14892,10 +14900,9 @@ fn checkVectorizableBinaryOperands(
             return sema.failWithOwnedErrorMsg(block, msg);
         }
     } else {
-        const target = sema.mod.getTarget();
         const msg = msg: {
             const msg = try sema.errMsg(block, src, "mixed scalar and vector operands: {} and {}", .{
-                lhs_ty.fmt(target), rhs_ty.fmt(target),
+                lhs_ty.fmt(sema.mod), rhs_ty.fmt(sema.mod),
             });
             errdefer msg.destroy(sema.gpa);
             if (lhs_is_vector) {
@@ -14934,9 +14941,8 @@ fn resolveExportOptions(
         return sema.fail(block, src, "TODO: implement exporting with linksection", .{});
     }
     const name_ty = Type.initTag(.const_slice_u8);
-    const target = sema.mod.getTarget();
     return std.builtin.ExportOptions{
-        .name = try name_val.toAllocatedBytes(name_ty, sema.arena, target),
+        .name = try name_val.toAllocatedBytes(name_ty, sema.arena, sema.mod),
         .linkage = linkage_val.toEnum(std.builtin.GlobalLinkage),
         .section = null, // TODO
     };
@@ -14995,13 +15001,12 @@ fn zirCmpxchg(
     const ptr_ty = sema.typeOf(ptr);
     const elem_ty = ptr_ty.elemType();
     try sema.checkAtomicOperandType(block, elem_ty_src, elem_ty);
-    const target = sema.mod.getTarget();
     if (elem_ty.zigTypeTag() == .Float) {
         return sema.fail(
             block,
             elem_ty_src,
             "expected bool, integer, enum, or pointer type; found '{}'",
-            .{elem_ty.fmt(target)},
+            .{elem_ty.fmt(sema.mod)},
         );
     }
     const expected_value = try sema.coerce(block, elem_ty, sema.resolveInst(extra.expected_value), expected_src);
@@ -15038,7 +15043,7 @@ fn zirCmpxchg(
                     return sema.addConstUndef(result_ty);
                 }
                 const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src;
-                const result_val = if (stored_val.eql(expected_val, elem_ty, target)) blk: {
+                const result_val = if (stored_val.eql(expected_val, elem_ty, sema.mod)) blk: {
                     try sema.storePtr(block, src, ptr, new_value);
                     break :blk Value.@"null";
                 } else try Value.Tag.opt_payload.create(sema.arena, stored_val);
@@ -15103,7 +15108,7 @@ fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
     const target = sema.mod.getTarget();
 
     if (operand_ty.zigTypeTag() != .Vector) {
-        return sema.fail(block, operand_src, "expected vector, found {}", .{operand_ty.fmt(target)});
+        return sema.fail(block, operand_src, "expected vector, found {}", .{operand_ty.fmt(sema.mod)});
     }
 
     const scalar_ty = operand_ty.childType();
@@ -15113,13 +15118,13 @@ fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
         .And, .Or, .Xor => switch (scalar_ty.zigTypeTag()) {
             .Int, .Bool => {},
             else => return sema.fail(block, operand_src, "@reduce operation '{s}' requires integer or boolean operand; found {}", .{
-                @tagName(operation), operand_ty.fmt(target),
+                @tagName(operation), operand_ty.fmt(sema.mod),
             }),
         },
         .Min, .Max, .Add, .Mul => switch (scalar_ty.zigTypeTag()) {
             .Int, .Float => {},
             else => return sema.fail(block, operand_src, "@reduce operation '{s}' requires integer or float operand; found {}", .{
-                @tagName(operation), operand_ty.fmt(target),
+                @tagName(operation), operand_ty.fmt(sema.mod),
             }),
         },
     }
@@ -15134,11 +15139,11 @@ fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
     if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |operand_val| {
         if (operand_val.isUndef()) return sema.addConstUndef(scalar_ty);
 
-        var accum: Value = try operand_val.elemValue(sema.arena, 0);
+        var accum: Value = try operand_val.elemValue(sema.mod, sema.arena, 0);
         var elem_buf: Value.ElemValueBuffer = undefined;
         var i: u32 = 1;
         while (i < vec_len) : (i += 1) {
-            const elem_val = operand_val.elemValueBuffer(i, &elem_buf);
+            const elem_val = operand_val.elemValueBuffer(sema.mod, i, &elem_buf);
             switch (operation) {
                 .And => accum = try accum.bitwiseAnd(elem_val, scalar_ty, sema.arena, target),
                 .Or => accum = try accum.bitwiseOr(elem_val, scalar_ty, sema.arena, target),
@@ -15174,11 +15179,10 @@ fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
     var b = sema.resolveInst(extra.b);
     var mask = sema.resolveInst(extra.mask);
     var mask_ty = sema.typeOf(mask);
-    const target = sema.mod.getTarget();
 
     const mask_len = switch (sema.typeOf(mask).zigTypeTag()) {
         .Array, .Vector => sema.typeOf(mask).arrayLen(),
-        else => return sema.fail(block, mask_src, "expected vector or array, found {}", .{sema.typeOf(mask).fmt(target)}),
+        else => return sema.fail(block, mask_src, "expected vector or array, found {}", .{sema.typeOf(mask).fmt(sema.mod)}),
     };
     mask_ty = try Type.Tag.vector.create(sema.arena, .{
         .len = mask_len,
@@ -15210,21 +15214,20 @@ fn analyzeShuffle(
         .elem_type = elem_ty,
     });
 
-    const target = sema.mod.getTarget();
     var maybe_a_len = switch (sema.typeOf(a).zigTypeTag()) {
         .Array, .Vector => sema.typeOf(a).arrayLen(),
         .Undefined => null,
         else => return sema.fail(block, a_src, "expected vector or array with element type {}, found {}", .{
-            elem_ty.fmt(target),
-            sema.typeOf(a).fmt(target),
+            elem_ty.fmt(sema.mod),
+            sema.typeOf(a).fmt(sema.mod),
         }),
     };
     var maybe_b_len = switch (sema.typeOf(b).zigTypeTag()) {
         .Array, .Vector => sema.typeOf(b).arrayLen(),
         .Undefined => null,
         else => return sema.fail(block, b_src, "expected vector or array with element type {}, found {}", .{
-            elem_ty.fmt(target),
-            sema.typeOf(b).fmt(target),
+            elem_ty.fmt(sema.mod),
+            sema.typeOf(b).fmt(sema.mod),
         }),
     };
     if (maybe_a_len == null and maybe_b_len == null) {
@@ -15253,7 +15256,7 @@ fn analyzeShuffle(
     var i: usize = 0;
     while (i < mask_len) : (i += 1) {
         var buf: Value.ElemValueBuffer = undefined;
-        const elem = mask.elemValueBuffer(i, &buf);
+        const elem = mask.elemValueBuffer(sema.mod, i, &buf);
         if (elem.isUndef()) continue;
         const int = elem.toSignedInt();
         var unsigned: u32 = undefined;
@@ -15272,7 +15275,7 @@ fn analyzeShuffle(
 
                 try sema.errNote(block, operand_info[chosen][1], msg, "selected index {d} out of bounds of {}", .{
                     unsigned,
-                    operand_info[chosen][2].fmt(target),
+                    operand_info[chosen][2].fmt(sema.mod),
                 });
 
                 if (chosen == 1) {
@@ -15292,7 +15295,7 @@ fn analyzeShuffle(
             i = 0;
             while (i < mask_len) : (i += 1) {
                 var buf: Value.ElemValueBuffer = undefined;
-                const mask_elem_val = mask.elemValueBuffer(i, &buf);
+                const mask_elem_val = mask.elemValueBuffer(sema.mod, i, &buf);
                 if (mask_elem_val.isUndef()) {
                     values[i] = Value.undef;
                     continue;
@@ -15300,9 +15303,9 @@ fn analyzeShuffle(
                 const int = mask_elem_val.toSignedInt();
                 const unsigned = if (int >= 0) @intCast(u32, int) else @intCast(u32, ~int);
                 if (int >= 0) {
-                    values[i] = try a_val.elemValue(sema.arena, unsigned);
+                    values[i] = try a_val.elemValue(sema.mod, sema.arena, unsigned);
                 } else {
-                    values[i] = try b_val.elemValue(sema.arena, unsigned);
+                    values[i] = try b_val.elemValue(sema.mod, sema.arena, unsigned);
                 }
             }
             const res_val = try Value.Tag.aggregate.create(sema.arena, values);
@@ -15358,7 +15361,6 @@ fn analyzeShuffle(
 fn zirSelect(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
     const extra = sema.code.extraData(Zir.Inst.Select, inst_data.payload_index).data;
-    const target = sema.mod.getTarget();
 
     const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
     const pred_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
@@ -15372,7 +15374,7 @@ fn zirSelect(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
 
     const vec_len_u64 = switch (try pred_ty.zigTypeTagOrPoison()) {
         .Vector, .Array => pred_ty.arrayLen(),
-        else => return sema.fail(block, pred_src, "expected vector or array, found '{}'", .{pred_ty.fmt(target)}),
+        else => return sema.fail(block, pred_src, "expected vector or array, found '{}'", .{pred_ty.fmt(sema.mod)}),
     };
     const vec_len = try sema.usizeCast(block, pred_src, vec_len_u64);
 
@@ -15399,12 +15401,12 @@ fn zirSelect(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
                 var buf: Value.ElemValueBuffer = undefined;
                 const elems = try sema.gpa.alloc(Value, vec_len);
                 for (elems) |*elem, i| {
-                    const pred_elem_val = pred_val.elemValueBuffer(i, &buf);
+                    const pred_elem_val = pred_val.elemValueBuffer(sema.mod, i, &buf);
                     const should_choose_a = pred_elem_val.toBool();
                     if (should_choose_a) {
-                        elem.* = a_val.elemValueBuffer(i, &buf);
+                        elem.* = a_val.elemValueBuffer(sema.mod, i, &buf);
                     } else {
-                        elem.* = b_val.elemValueBuffer(i, &buf);
+                        elem.* = b_val.elemValueBuffer(sema.mod, i, &buf);
                     }
                 }
 
@@ -15630,7 +15632,7 @@ fn zirMulAdd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
 
     switch (ty.zigTypeTag()) {
         .ComptimeFloat, .Float, .Vector => {},
-        else => return sema.fail(block, src, "expected vector of floats or float type, found '{}'", .{ty.fmt(target)}),
+        else => return sema.fail(block, src, "expected vector of floats or float type, found '{}'", .{ty.fmt(sema.mod)}),
     }
 
     const runtime_src = if (maybe_mulend1) |mulend1_val| rs: {
@@ -15704,10 +15706,9 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
         break :modifier modifier_val.toEnum(std.builtin.CallOptions.Modifier);
     };
 
-    const target = sema.mod.getTarget();
     const args_ty = sema.typeOf(args);
     if (!args_ty.isTuple() and args_ty.tag() != .empty_struct_literal) {
-        return sema.fail(block, args_src, "expected a tuple, found {}", .{args_ty.fmt(target)});
+        return sema.fail(block, args_src, "expected a tuple, found {}", .{args_ty.fmt(sema.mod)});
     }
 
     var resolved_args: []Air.Inst.Ref = undefined;
@@ -15744,10 +15745,9 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
     const field_name = try sema.resolveConstString(block, name_src, extra.field_name);
     const field_ptr = sema.resolveInst(extra.field_ptr);
     const field_ptr_ty = sema.typeOf(field_ptr);
-    const target = sema.mod.getTarget();
 
     if (struct_ty.zigTypeTag() != .Struct) {
-        return sema.fail(block, ty_src, "expected struct type, found '{}'", .{struct_ty.fmt(target)});
+        return sema.fail(block, ty_src, "expected struct type, found '{}'", .{struct_ty.fmt(sema.mod)});
     }
     try sema.resolveTypeLayout(block, ty_src, struct_ty);
 
@@ -15756,7 +15756,7 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
         return sema.failWithBadStructFieldAccess(block, struct_obj, name_src, field_name);
 
     if (field_ptr_ty.zigTypeTag() != .Pointer) {
-        return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{field_ptr_ty.fmt(target)});
+        return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{field_ptr_ty.fmt(sema.mod)});
     }
     const field = struct_obj.fields.values()[field_index];
     const field_ptr_ty_info = field_ptr_ty.ptrInfo().data;
@@ -15773,11 +15773,11 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
         ptr_ty_data.@"align" = field.abi_align;
     }
 
-    const actual_field_ptr_ty = try Type.ptr(sema.arena, target, ptr_ty_data);
+    const actual_field_ptr_ty = try Type.ptr(sema.arena, sema.mod, ptr_ty_data);
     const casted_field_ptr = try sema.coerce(block, actual_field_ptr_ty, field_ptr, ptr_src);
 
     ptr_ty_data.pointee_type = struct_ty;
-    const result_ptr = try Type.ptr(sema.arena, target, ptr_ty_data);
+    const result_ptr = try Type.ptr(sema.arena, sema.mod, ptr_ty_data);
 
     if (try sema.resolveDefinedValue(block, src, casted_field_ptr)) |field_ptr_val| {
         const payload = field_ptr_val.castTag(.field_ptr).?.data;
@@ -15850,8 +15850,8 @@ fn analyzeMinMax(
         var rhs_buf: Value.ElemValueBuffer = undefined;
         const elems = try sema.arena.alloc(Value, vec_len);
         for (elems) |*elem, i| {
-            const lhs_elem_val = lhs_val.elemValueBuffer(i, &lhs_buf);
-            const rhs_elem_val = rhs_val.elemValueBuffer(i, &rhs_buf);
+            const lhs_elem_val = lhs_val.elemValueBuffer(sema.mod, i, &lhs_buf);
+            const rhs_elem_val = rhs_val.elemValueBuffer(sema.mod, i, &rhs_buf);
             elem.* = opFunc(lhs_elem_val, rhs_elem_val, target);
         }
         return sema.addConstant(
@@ -15878,18 +15878,17 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
     const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
     const dest_ptr = sema.resolveInst(extra.dest);
     const dest_ptr_ty = sema.typeOf(dest_ptr);
-    const target = sema.mod.getTarget();
 
     try sema.checkPtrOperand(block, dest_src, dest_ptr_ty);
     if (dest_ptr_ty.isConstPtr()) {
-        return sema.fail(block, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty.fmt(target)});
+        return sema.fail(block, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty.fmt(sema.mod)});
     }
 
     const uncasted_src_ptr = sema.resolveInst(extra.source);
     const uncasted_src_ptr_ty = sema.typeOf(uncasted_src_ptr);
     try sema.checkPtrOperand(block, src_src, uncasted_src_ptr_ty);
     const src_ptr_info = uncasted_src_ptr_ty.ptrInfo().data;
-    const wanted_src_ptr_ty = try Type.ptr(sema.arena, target, .{
+    const wanted_src_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{
         .pointee_type = dest_ptr_ty.elemType2(),
         .@"align" = src_ptr_info.@"align",
         .@"addrspace" = src_ptr_info.@"addrspace",
@@ -15936,10 +15935,9 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
     const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
     const dest_ptr = sema.resolveInst(extra.dest);
     const dest_ptr_ty = sema.typeOf(dest_ptr);
-    const target = sema.mod.getTarget();
     try sema.checkPtrOperand(block, dest_src, dest_ptr_ty);
     if (dest_ptr_ty.isConstPtr()) {
-        return sema.fail(block, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty.fmt(target)});
+        return sema.fail(block, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty.fmt(sema.mod)});
     }
     const elem_ty = dest_ptr_ty.elemType2();
     const value = try sema.coerce(block, elem_ty, sema.resolveInst(extra.byte), value_src);
@@ -16057,7 +16055,7 @@ fn zirVarExtended(
     });
 
     new_var.* = .{
-        .owner_decl = sema.owner_decl,
+        .owner_decl = sema.owner_decl_index,
         .init = init_val,
         .is_extern = small.is_extern,
         .is_mutable = true, // TODO get rid of this unused field
@@ -16294,7 +16292,7 @@ fn zirBuiltinExtern(
 
     var ty = try sema.resolveType(block, ty_src, extra.lhs);
     const options_inst = sema.resolveInst(extra.rhs);
-    const target = sema.mod.getTarget();
+    const mod = sema.mod;
 
     const options = options: {
         const extern_options_ty = try sema.getBuiltinType(block, options_src, "ExternOptions");
@@ -16315,11 +16313,11 @@ fn zirBuiltinExtern(
         var library_name: ?[]const u8 = null;
         if (!library_name_val.isNull()) {
             const payload = library_name_val.castTag(.opt_payload).?.data;
-            library_name = try payload.toAllocatedBytes(Type.initTag(.const_slice_u8), sema.arena, target);
+            library_name = try payload.toAllocatedBytes(Type.initTag(.const_slice_u8), sema.arena, mod);
         }
 
         break :options std.builtin.ExternOptions{
-            .name = try name_val.toAllocatedBytes(Type.initTag(.const_slice_u8), sema.arena, target),
+            .name = try name_val.toAllocatedBytes(Type.initTag(.const_slice_u8), sema.arena, mod),
             .library_name = library_name,
             .linkage = linkage_val.toEnum(std.builtin.GlobalLinkage),
             .is_thread_local = is_thread_local_val.toBool(),
@@ -16344,8 +16342,10 @@ fn zirBuiltinExtern(
 
     // TODO check duplicate extern
 
-    const new_decl = try sema.mod.allocateNewDecl(try sema.gpa.dupeZ(u8, options.name), sema.owner_decl.src_namespace, sema.owner_decl.src_node, null);
-    errdefer new_decl.destroy(sema.mod);
+    const new_decl_index = try mod.allocateNewDecl(sema.owner_decl.src_namespace, sema.owner_decl.src_node, null);
+    errdefer mod.destroyDecl(new_decl_index);
+    const new_decl = mod.declPtr(new_decl_index);
+    new_decl.name = try sema.gpa.dupeZ(u8, options.name);
 
     var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
     errdefer new_decl_arena.deinit();
@@ -16355,7 +16355,7 @@ fn zirBuiltinExtern(
     errdefer new_decl_arena_allocator.destroy(new_var);
 
     new_var.* = .{
-        .owner_decl = sema.owner_decl,
+        .owner_decl = sema.owner_decl_index,
         .init = Value.initTag(.unreachable_value),
         .is_extern = true,
         .is_mutable = false, // TODO get rid of this unused field
@@ -16378,13 +16378,13 @@ fn zirBuiltinExtern(
     new_decl.@"linksection" = null;
     new_decl.has_tv = true;
     new_decl.analysis = .complete;
-    new_decl.generation = sema.mod.generation;
+    new_decl.generation = mod.generation;
 
     const arena_state = try new_decl_arena_allocator.create(std.heap.ArenaAllocator.State);
     arena_state.* = new_decl_arena.state;
     new_decl.value_arena = arena_state;
 
-    const ref = try sema.analyzeDeclRef(new_decl);
+    const ref = try sema.analyzeDeclRef(new_decl_index);
     try sema.requireRuntimeBlock(block, src);
     return block.addBitCast(ty, ref);
 }
@@ -16412,12 +16412,14 @@ fn validateVarType(
 ) CompileError!void {
     if (try sema.validateRunTimeType(block, src, var_ty, is_extern)) return;
 
-    const target = sema.mod.getTarget();
+    const mod = sema.mod;
+
     const msg = msg: {
-        const msg = try sema.errMsg(block, src, "variable of type '{}' must be const or comptime", .{var_ty.fmt(target)});
+        const msg = try sema.errMsg(block, src, "variable of type '{}' must be const or comptime", .{var_ty.fmt(mod)});
         errdefer msg.destroy(sema.gpa);
 
-        try sema.explainWhyTypeIsComptime(block, src, msg, src.toSrcLoc(block.src_decl), var_ty);
+        const src_decl = mod.declPtr(block.src_decl);
+        try sema.explainWhyTypeIsComptime(block, src, msg, src.toSrcLoc(src_decl), var_ty);
 
         break :msg msg;
     };
@@ -16489,7 +16491,6 @@ fn explainWhyTypeIsComptime(
     ty: Type,
 ) CompileError!void {
     const mod = sema.mod;
-    const target = mod.getTarget();
     switch (ty.zigTypeTag()) {
         .Bool,
         .Int,
@@ -16503,7 +16504,7 @@ fn explainWhyTypeIsComptime(
 
         .Fn => {
             try mod.errNoteNonLazy(src_loc, msg, "use '*const {}' for a function pointer type", .{
-                ty.fmt(target),
+                ty.fmt(sema.mod),
             });
         },
 
@@ -16534,7 +16535,7 @@ fn explainWhyTypeIsComptime(
             if (ty.castTag(.@"struct")) |payload| {
                 const struct_obj = payload.data;
                 for (struct_obj.fields.values()) |field, i| {
-                    const field_src_loc = struct_obj.fieldSrcLoc(sema.gpa, .{
+                    const field_src_loc = struct_obj.fieldSrcLoc(sema.mod, .{
                         .index = i,
                         .range = .type,
                     });
@@ -16551,7 +16552,7 @@ fn explainWhyTypeIsComptime(
             if (ty.cast(Type.Payload.Union)) |payload| {
                 const union_obj = payload.data;
                 for (union_obj.fields.values()) |field, i| {
-                    const field_src_loc = union_obj.fieldSrcLoc(sema.gpa, .{
+                    const field_src_loc = union_obj.fieldSrcLoc(sema.mod, .{
                         .index = i,
                         .range = .type,
                     });
@@ -16668,7 +16669,7 @@ fn panicWithMsg(
     const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace");
     const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty);
     const target = mod.getTarget();
-    const ptr_stack_trace_ty = try Type.ptr(arena, target, .{
+    const ptr_stack_trace_ty = try Type.ptr(arena, mod, .{
         .pointee_type = stack_trace_ty,
         .@"addrspace" = target_util.defaultAddressSpace(target, .global_constant), // TODO might need a place that is more dynamic
     });
@@ -16748,8 +16749,6 @@ fn fieldVal(
     else
         object_ty;
 
-    const target = sema.mod.getTarget();
-
     switch (inner_ty.zigTypeTag()) {
         .Array => {
             if (mem.eql(u8, field_name, "len")) {
@@ -16762,7 +16761,7 @@ fn fieldVal(
                     block,
                     field_name_src,
                     "no member named '{s}' in '{}'",
-                    .{ field_name, object_ty.fmt(target) },
+                    .{ field_name, object_ty.fmt(sema.mod) },
                 );
             }
         },
@@ -16786,7 +16785,7 @@ fn fieldVal(
                         block,
                         field_name_src,
                         "no member named '{s}' in '{}'",
-                        .{ field_name, object_ty.fmt(target) },
+                        .{ field_name, object_ty.fmt(sema.mod) },
                     );
                 }
             } else if (ptr_info.pointee_type.zigTypeTag() == .Array) {
@@ -16800,7 +16799,7 @@ fn fieldVal(
                         block,
                         field_name_src,
                         "no member named '{s}' in '{}'",
-                        .{ field_name, ptr_info.pointee_type.fmt(target) },
+                        .{ field_name, ptr_info.pointee_type.fmt(sema.mod) },
                     );
                 }
             }
@@ -16822,7 +16821,7 @@ fn fieldVal(
                             break :blk entry.key_ptr.*;
                         }
                         return sema.fail(block, src, "no error named '{s}' in '{}'", .{
-                            field_name, child_type.fmt(target),
+                            field_name, child_type.fmt(sema.mod),
                         });
                     } else (try sema.mod.getErrorValue(field_name)).key;
 
@@ -16876,10 +16875,10 @@ fn fieldVal(
                         else => unreachable,
                     };
                     return sema.fail(block, src, "{s} '{}' has no member named '{s}'", .{
-                        kw_name, child_type.fmt(target), field_name,
+                        kw_name, child_type.fmt(sema.mod), field_name,
                     });
                 },
-                else => return sema.fail(block, src, "type '{}' has no members", .{child_type.fmt(target)}),
+                else => return sema.fail(block, src, "type '{}' has no members", .{child_type.fmt(sema.mod)}),
             }
         },
         .Struct => if (is_pointer_to) {
@@ -16898,7 +16897,7 @@ fn fieldVal(
         },
         else => {},
     }
-    return sema.fail(block, src, "type '{}' does not support field access", .{object_ty.fmt(target)});
+    return sema.fail(block, src, "type '{}' does not support field access", .{object_ty.fmt(sema.mod)});
 }
 
 fn fieldPtr(
@@ -16912,12 +16911,11 @@ fn fieldPtr(
     // When editing this function, note that there is corresponding logic to be edited
     // in `fieldVal`. This function takes a pointer and returns a pointer.
 
-    const target = sema.mod.getTarget();
     const object_ptr_src = src; // TODO better source location
     const object_ptr_ty = sema.typeOf(object_ptr);
     const object_ty = switch (object_ptr_ty.zigTypeTag()) {
         .Pointer => object_ptr_ty.elemType(),
-        else => return sema.fail(block, object_ptr_src, "expected pointer, found '{}'", .{object_ptr_ty.fmt(target)}),
+        else => return sema.fail(block, object_ptr_src, "expected pointer, found '{}'", .{object_ptr_ty.fmt(sema.mod)}),
     };
 
     // Zig allows dereferencing a single pointer during field lookup. Note that
@@ -16945,7 +16943,7 @@ fn fieldPtr(
                     block,
                     field_name_src,
                     "no member named '{s}' in '{}'",
-                    .{ field_name, object_ty.fmt(target) },
+                    .{ field_name, object_ty.fmt(sema.mod) },
                 );
             }
         },
@@ -16971,7 +16969,7 @@ fn fieldPtr(
                 }
                 try sema.requireRuntimeBlock(block, src);
 
-                const result_ty = try Type.ptr(sema.arena, target, .{
+                const result_ty = try Type.ptr(sema.arena, sema.mod, .{
                     .pointee_type = slice_ptr_ty,
                     .mutable = object_ptr_ty.ptrIsMutable(),
                     .@"addrspace" = object_ptr_ty.ptrAddressSpace(),
@@ -16985,13 +16983,13 @@ fn fieldPtr(
 
                     return sema.analyzeDeclRef(try anon_decl.finish(
                         Type.usize,
-                        try Value.Tag.int_u64.create(anon_decl.arena(), val.sliceLen(target)),
+                        try Value.Tag.int_u64.create(anon_decl.arena(), val.sliceLen(sema.mod)),
                         0, // default alignment
                     ));
                 }
                 try sema.requireRuntimeBlock(block, src);
 
-                const result_ty = try Type.ptr(sema.arena, target, .{
+                const result_ty = try Type.ptr(sema.arena, sema.mod, .{
                     .pointee_type = Type.usize,
                     .mutable = object_ptr_ty.ptrIsMutable(),
                     .@"addrspace" = object_ptr_ty.ptrAddressSpace(),
@@ -17003,7 +17001,7 @@ fn fieldPtr(
                     block,
                     field_name_src,
                     "no member named '{s}' in '{}'",
-                    .{ field_name, object_ty.fmt(target) },
+                    .{ field_name, object_ty.fmt(sema.mod) },
                 );
             }
         },
@@ -17027,7 +17025,7 @@ fn fieldPtr(
                             break :blk entry.key_ptr.*;
                         }
                         return sema.fail(block, src, "no error named '{s}' in '{}'", .{
-                            field_name, child_type.fmt(target),
+                            field_name, child_type.fmt(sema.mod),
                         });
                     } else (try sema.mod.getErrorValue(field_name)).key;
 
@@ -17085,7 +17083,7 @@ fn fieldPtr(
                     }
                     return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
                 },
-                else => return sema.fail(block, src, "type '{}' has no members", .{child_type.fmt(target)}),
+                else => return sema.fail(block, src, "type '{}' has no members", .{child_type.fmt(sema.mod)}),
             }
         },
         .Struct => {
@@ -17104,7 +17102,7 @@ fn fieldPtr(
         },
         else => {},
     }
-    return sema.fail(block, src, "type '{}' does not support field access (fieldPtr, {}.{s})", .{ object_ty.fmt(target), object_ptr_ty.fmt(target), field_name });
+    return sema.fail(block, src, "type '{}' does not support field access (fieldPtr, {}.{s})", .{ object_ty.fmt(sema.mod), object_ptr_ty.fmt(sema.mod), field_name });
 }
 
 fn fieldCallBind(
@@ -17118,13 +17116,12 @@ fn fieldCallBind(
     // When editing this function, note that there is corresponding logic to be edited
     // in `fieldVal`. This function takes a pointer and returns a pointer.
 
-    const target = sema.mod.getTarget();
     const raw_ptr_src = src; // TODO better source location
     const raw_ptr_ty = sema.typeOf(raw_ptr);
     const inner_ty = if (raw_ptr_ty.zigTypeTag() == .Pointer and raw_ptr_ty.ptrSize() == .One)
         raw_ptr_ty.childType()
     else
-        return sema.fail(block, raw_ptr_src, "expected single pointer, found '{}'", .{raw_ptr_ty.fmt(target)});
+        return sema.fail(block, raw_ptr_src, "expected single pointer, found '{}'", .{raw_ptr_ty.fmt(sema.mod)});
 
     // Optionally dereference a second pointer to get the concrete type.
     const is_double_ptr = inner_ty.zigTypeTag() == .Pointer and inner_ty.ptrSize() == .One;
@@ -17184,7 +17181,7 @@ fn fieldCallBind(
                                 first_param_type.zigTypeTag() == .Pointer and
                                 (first_param_type.ptrSize() == .One or
                                 first_param_type.ptrSize() == .C) and
-                                first_param_type.childType().eql(concrete_ty, target)))
+                                first_param_type.childType().eql(concrete_ty, sema.mod)))
                         {
                             // zig fmt: on
                             // TODO: bound fn calls on rvalues should probably
@@ -17195,7 +17192,7 @@ fn fieldCallBind(
                                 .arg0_inst = object_ptr,
                             });
                             return sema.addConstant(ty, value);
-                        } else if (first_param_type.eql(concrete_ty, target)) {
+                        } else if (first_param_type.eql(concrete_ty, sema.mod)) {
                             var deref = try sema.analyzeLoad(block, src, object_ptr, src);
                             const ty = Type.Tag.bound_fn.init();
                             const value = try Value.Tag.bound_fn.create(arena, .{
@@ -17211,7 +17208,7 @@ fn fieldCallBind(
         else => {},
     }
 
-    return sema.fail(block, src, "type '{}' has no field or member function named '{s}'", .{ concrete_ty.fmt(target), field_name });
+    return sema.fail(block, src, "type '{}' has no field or member function named '{s}'", .{ concrete_ty.fmt(sema.mod), field_name });
 }
 
 fn finishFieldCallBind(
@@ -17224,8 +17221,7 @@ fn finishFieldCallBind(
     object_ptr: Air.Inst.Ref,
 ) CompileError!Air.Inst.Ref {
     const arena = sema.arena;
-    const target = sema.mod.getTarget();
-    const ptr_field_ty = try Type.ptr(arena, target, .{
+    const ptr_field_ty = try Type.ptr(arena, sema.mod, .{
         .pointee_type = field_ty,
         .mutable = ptr_ty.ptrIsMutable(),
         .@"addrspace" = ptr_ty.ptrAddressSpace(),
@@ -17254,9 +17250,10 @@ fn namespaceLookup(
     src: LazySrcLoc,
     namespace: *Namespace,
     decl_name: []const u8,
-) CompileError!?*Decl {
+) CompileError!?Decl.Index {
     const gpa = sema.gpa;
-    if (try sema.lookupInNamespace(block, src, namespace, decl_name, true)) |decl| {
+    if (try sema.lookupInNamespace(block, src, namespace, decl_name, true)) |decl_index| {
+        const decl = sema.mod.declPtr(decl_index);
         if (!decl.is_pub and decl.getFileScope() != block.getFileScope()) {
             const msg = msg: {
                 const msg = try sema.errMsg(block, src, "'{s}' is not marked 'pub'", .{
@@ -17268,7 +17265,7 @@ fn namespaceLookup(
             };
             return sema.failWithOwnedErrorMsg(block, msg);
         }
-        return decl;
+        return decl_index;
     }
     return null;
 }
@@ -17377,7 +17374,7 @@ fn structFieldPtrByIndex(
         ptr_ty_data.@"align" = field.abi_align;
     }
 
-    const ptr_field_ty = try Type.ptr(sema.arena, target, ptr_ty_data);
+    const ptr_field_ty = try Type.ptr(sema.arena, sema.mod, ptr_ty_data);
 
     if (field.is_comptime) {
         var anon_decl = try block.startAnonDecl(field_src);
@@ -17476,15 +17473,14 @@ fn tupleFieldIndex(
     field_name: []const u8,
     field_name_src: LazySrcLoc,
 ) CompileError!u32 {
-    const target = sema.mod.getTarget();
     const field_index = std.fmt.parseUnsigned(u32, field_name, 10) catch |err| {
         return sema.fail(block, field_name_src, "tuple {} has no such field '{s}': {s}", .{
-            tuple_ty.fmt(target), field_name, @errorName(err),
+            tuple_ty.fmt(sema.mod), field_name, @errorName(err),
         });
     };
     if (field_index >= tuple_ty.structFieldCount()) {
         return sema.fail(block, field_name_src, "tuple {} has no such field '{s}'", .{
-            tuple_ty.fmt(target), field_name,
+            tuple_ty.fmt(sema.mod), field_name,
         });
     }
     return field_index;
@@ -17535,8 +17531,7 @@ fn unionFieldPtr(
     const union_obj = union_ty.cast(Type.Payload.Union).?.data;
     const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src);
     const field = union_obj.fields.values()[field_index];
-    const target = sema.mod.getTarget();
-    const ptr_field_ty = try Type.ptr(arena, target, .{
+    const ptr_field_ty = try Type.ptr(arena, sema.mod, .{
         .pointee_type = field.ty,
         .mutable = union_ptr_ty.ptrIsMutable(),
         .@"addrspace" = union_ptr_ty.ptrAddressSpace(),
@@ -17559,7 +17554,7 @@ fn unionFieldPtr(
                 //    .data = field_index,
                 //};
                 //const field_tag = Value.initPayload(&field_tag_buf.base);
-                //const tag_matches = tag_and_val.tag.eql(field_tag, union_obj.tag_ty, target);
+                //const tag_matches = tag_and_val.tag.eql(field_tag, union_obj.tag_ty, mod);
                 //if (!tag_matches) {
                 //    // TODO enhance this saying which one was active
                 //    // and which one was accessed, and showing where the union was declared.
@@ -17608,8 +17603,7 @@ fn unionFieldVal(
             .data = field_index,
         };
         const field_tag = Value.initPayload(&field_tag_buf.base);
-        const target = sema.mod.getTarget();
-        const tag_matches = tag_and_val.tag.eql(field_tag, union_obj.tag_ty, target);
+        const tag_matches = tag_and_val.tag.eql(field_tag, union_obj.tag_ty, sema.mod);
         switch (union_obj.layout) {
             .Auto => {
                 if (tag_matches) {
@@ -17630,7 +17624,7 @@ fn unionFieldVal(
                 if (tag_matches) {
                     return sema.addConstant(field.ty, tag_and_val.val);
                 } else {
-                    const old_ty = union_ty.unionFieldType(tag_and_val.tag, target);
+                    const old_ty = union_ty.unionFieldType(tag_and_val.tag, sema.mod);
                     const new_val = try sema.bitCastVal(block, src, tag_and_val.val, old_ty, field.ty, 0);
                     return sema.addConstant(field.ty, new_val);
                 }
@@ -17655,17 +17649,17 @@ fn elemPtr(
     const target = sema.mod.getTarget();
     const indexable_ty = switch (indexable_ptr_ty.zigTypeTag()) {
         .Pointer => indexable_ptr_ty.elemType(),
-        else => return sema.fail(block, indexable_ptr_src, "expected pointer, found '{}'", .{indexable_ptr_ty.fmt(target)}),
+        else => return sema.fail(block, indexable_ptr_src, "expected pointer, found '{}'", .{indexable_ptr_ty.fmt(sema.mod)}),
     };
     if (!indexable_ty.isIndexable()) {
-        return sema.fail(block, src, "element access of non-indexable type '{}'", .{indexable_ty.fmt(target)});
+        return sema.fail(block, src, "element access of non-indexable type '{}'", .{indexable_ty.fmt(sema.mod)});
     }
 
     switch (indexable_ty.zigTypeTag()) {
         .Pointer => {
             // In all below cases, we have to deref the ptr operand to get the actual indexable pointer.
             const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src);
-            const result_ty = try indexable_ty.elemPtrType(sema.arena, target);
+            const result_ty = try indexable_ty.elemPtrType(sema.arena, sema.mod);
             switch (indexable_ty.ptrSize()) {
                 .Slice => return sema.elemPtrSlice(block, indexable_ptr_src, indexable, elem_index_src, elem_index),
                 .Many, .C => {
@@ -17676,7 +17670,7 @@ fn elemPtr(
                         const ptr_val = maybe_ptr_val orelse break :rs indexable_ptr_src;
                         const index_val = maybe_index_val orelse break :rs elem_index_src;
                         const index = @intCast(usize, index_val.toUnsignedInt(target));
-                        const elem_ptr = try ptr_val.elemPtr(indexable_ty, sema.arena, index, target);
+                        const elem_ptr = try ptr_val.elemPtr(indexable_ty, sema.arena, index, sema.mod);
                         return sema.addConstant(result_ty, elem_ptr);
                     };
 
@@ -17713,7 +17707,7 @@ fn elemVal(
     const target = sema.mod.getTarget();
 
     if (!indexable_ty.isIndexable()) {
-        return sema.fail(block, src, "element access of non-indexable type '{}'", .{indexable_ty.fmt(target)});
+        return sema.fail(block, src, "element access of non-indexable type '{}'", .{indexable_ty.fmt(sema.mod)});
     }
 
     // TODO in case of a vector of pointers, we need to detect whether the element
@@ -17731,7 +17725,7 @@ fn elemVal(
                     const indexable_val = maybe_indexable_val orelse break :rs indexable_src;
                     const index_val = maybe_index_val orelse break :rs elem_index_src;
                     const index = @intCast(usize, index_val.toUnsignedInt(target));
-                    const elem_ptr_val = try indexable_val.elemPtr(indexable_ty, sema.arena, index, target);
+                    const elem_ptr_val = try indexable_val.elemPtr(indexable_ty, sema.arena, index, sema.mod);
                     if (try sema.pointerDeref(block, indexable_src, elem_ptr_val, indexable_ty)) |elem_val| {
                         return sema.addConstant(indexable_ty.elemType2(), elem_val);
                     }
@@ -17785,8 +17779,7 @@ fn tupleFieldPtr(
     }
 
     const field_ty = tuple_fields.types[field_index];
-    const target = sema.mod.getTarget();
-    const ptr_field_ty = try Type.ptr(sema.arena, target, .{
+    const ptr_field_ty = try Type.ptr(sema.arena, sema.mod, .{
         .pointee_type = field_ty,
         .mutable = tuple_ptr_ty.ptrIsMutable(),
         .@"addrspace" = tuple_ptr_ty.ptrAddressSpace(),
@@ -17881,7 +17874,7 @@ fn elemValArray(
         }
         if (maybe_index_val) |index_val| {
             const index = @intCast(usize, index_val.toUnsignedInt(target));
-            const elem_val = try array_val.elemValue(sema.arena, index);
+            const elem_val = try array_val.elemValue(sema.mod, sema.arena, index);
             return sema.addConstant(elem_ty, elem_val);
         }
     }
@@ -17914,7 +17907,7 @@ fn elemPtrArray(
     const array_sent = array_ty.sentinel() != null;
     const array_len = array_ty.arrayLen();
     const array_len_s = array_len + @boolToInt(array_sent);
-    const elem_ptr_ty = try array_ptr_ty.elemPtrType(sema.arena, target);
+    const elem_ptr_ty = try array_ptr_ty.elemPtrType(sema.arena, sema.mod);
 
     if (array_len_s == 0) {
         return sema.fail(block, elem_index_src, "indexing into empty array", .{});
@@ -17937,7 +17930,7 @@ fn elemPtrArray(
         }
         if (maybe_index_val) |index_val| {
             const index = @intCast(usize, index_val.toUnsignedInt(target));
-            const elem_ptr = try array_ptr_val.elemPtr(array_ptr_ty, sema.arena, index, target);
+            const elem_ptr = try array_ptr_val.elemPtr(array_ptr_ty, sema.arena, index, sema.mod);
             return sema.addConstant(elem_ptr_ty, elem_ptr);
         }
     }
@@ -17977,7 +17970,7 @@ fn elemValSlice(
 
     if (maybe_slice_val) |slice_val| {
         runtime_src = elem_index_src;
-        const slice_len = slice_val.sliceLen(target);
+        const slice_len = slice_val.sliceLen(sema.mod);
         const slice_len_s = slice_len + @boolToInt(slice_sent);
         if (slice_len_s == 0) {
             return sema.fail(block, elem_index_src, "indexing into empty slice", .{});
@@ -17988,7 +17981,7 @@ fn elemValSlice(
                 const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else "";
                 return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label });
             }
-            const elem_ptr_val = try slice_val.elemPtr(slice_ty, sema.arena, index, target);
+            const elem_ptr_val = try slice_val.elemPtr(slice_ty, sema.arena, index, sema.mod);
             if (try sema.pointerDeref(block, slice_src, elem_ptr_val, slice_ty)) |elem_val| {
                 return sema.addConstant(elem_ty, elem_val);
             }
@@ -17999,7 +17992,7 @@ fn elemValSlice(
     try sema.requireRuntimeBlock(block, runtime_src);
     if (block.wantSafety()) {
         const len_inst = if (maybe_slice_val) |slice_val|
-            try sema.addIntUnsigned(Type.usize, slice_val.sliceLen(target))
+            try sema.addIntUnsigned(Type.usize, slice_val.sliceLen(sema.mod))
         else
             try block.addTyOp(.slice_len, Type.usize, slice);
         const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt;
@@ -18020,7 +18013,7 @@ fn elemPtrSlice(
     const target = sema.mod.getTarget();
     const slice_ty = sema.typeOf(slice);
     const slice_sent = slice_ty.sentinel() != null;
-    const elem_ptr_ty = try slice_ty.elemPtrType(sema.arena, target);
+    const elem_ptr_ty = try slice_ty.elemPtrType(sema.arena, sema.mod);
 
     const maybe_undef_slice_val = try sema.resolveMaybeUndefVal(block, slice_src, slice);
     // index must be defined since it can index out of bounds
@@ -18030,7 +18023,7 @@ fn elemPtrSlice(
         if (slice_val.isUndef()) {
             return sema.addConstUndef(elem_ptr_ty);
         }
-        const slice_len = slice_val.sliceLen(target);
+        const slice_len = slice_val.sliceLen(sema.mod);
         const slice_len_s = slice_len + @boolToInt(slice_sent);
         if (slice_len_s == 0) {
             return sema.fail(block, elem_index_src, "indexing into empty slice", .{});
@@ -18041,7 +18034,7 @@ fn elemPtrSlice(
                 const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else "";
                 return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label });
             }
-            const elem_ptr_val = try slice_val.elemPtr(slice_ty, sema.arena, index, target);
+            const elem_ptr_val = try slice_val.elemPtr(slice_ty, sema.arena, index, sema.mod);
             return sema.addConstant(elem_ptr_ty, elem_ptr_val);
         }
     }
@@ -18052,7 +18045,7 @@ fn elemPtrSlice(
         const len_inst = len: {
             if (maybe_undef_slice_val) |slice_val|
                 if (!slice_val.isUndef())
-                    break :len try sema.addIntUnsigned(Type.usize, slice_val.sliceLen(target));
+                    break :len try sema.addIntUnsigned(Type.usize, slice_val.sliceLen(sema.mod));
             break :len try block.addTyOp(.slice_len, Type.usize, slice);
         };
         const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt;
@@ -18079,7 +18072,7 @@ fn coerce(
     const inst_ty = try sema.resolveTypeFields(block, inst_src, sema.typeOf(inst));
     const target = sema.mod.getTarget();
     // If the types are the same, we can return the operand.
-    if (dest_ty.eql(inst_ty, target))
+    if (dest_ty.eql(inst_ty, sema.mod))
         return inst;
 
     const arena = sema.arena;
@@ -18185,7 +18178,7 @@ fn coerce(
                         // *[N:s]T to [*]T
                         if (dest_info.sentinel) |dst_sentinel| {
                             if (array_ty.sentinel()) |src_sentinel| {
-                                if (src_sentinel.eql(dst_sentinel, dst_elem_type, target)) {
+                                if (src_sentinel.eql(dst_sentinel, dst_elem_type, sema.mod)) {
                                     return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
                                 }
                             }
@@ -18254,7 +18247,7 @@ fn coerce(
                         }
                         if (inst_info.size == .Slice) {
                             if (dest_info.sentinel == null or inst_info.sentinel == null or
-                                !dest_info.sentinel.?.eql(inst_info.sentinel.?, dest_info.pointee_type, target))
+                                !dest_info.sentinel.?.eql(inst_info.sentinel.?, dest_info.pointee_type, sema.mod))
                                 break :p;
 
                             const slice_ptr = try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty);
@@ -18334,7 +18327,7 @@ fn coerce(
                     }
 
                     if (dest_info.sentinel == null or inst_info.sentinel == null or
-                        !dest_info.sentinel.?.eql(inst_info.sentinel.?, dest_info.pointee_type, target))
+                        !dest_info.sentinel.?.eql(inst_info.sentinel.?, dest_info.pointee_type, sema.mod))
                         break :p;
 
                     const slice_ptr = try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty);
@@ -18347,11 +18340,16 @@ fn coerce(
                 const val = (try sema.resolveDefinedValue(block, inst_src, inst)) orelse break :float;
 
                 if (val.floatHasFraction()) {
-                    return sema.fail(block, inst_src, "fractional component prevents float value {} from coercion to type '{}'", .{ val.fmtValue(inst_ty, target), dest_ty.fmt(target) });
+                    return sema.fail(
+                        block,
+                        inst_src,
+                        "fractional component prevents float value {} from coercion to type '{}'",
+                        .{ val.fmtValue(inst_ty, sema.mod), dest_ty.fmt(sema.mod) },
+                    );
                 }
                 const result_val = val.floatToInt(sema.arena, inst_ty, dest_ty, target) catch |err| switch (err) {
                     error.FloatCannotFit => {
-                        return sema.fail(block, inst_src, "integer value {d} cannot be stored in type '{}'", .{ std.math.floor(val.toFloat(f64)), dest_ty.fmt(target) });
+                        return sema.fail(block, inst_src, "integer value {d} cannot be stored in type '{}'", .{ std.math.floor(val.toFloat(f64)), dest_ty.fmt(sema.mod) });
                     },
                     else => |e| return e,
                 };
@@ -18361,7 +18359,7 @@ fn coerce(
                 if (try sema.resolveDefinedValue(block, inst_src, inst)) |val| {
                     // comptime known integer to other number
                     if (!val.intFitsInType(dest_ty, target)) {
-                        return sema.fail(block, inst_src, "type {} cannot represent integer value {}", .{ dest_ty.fmt(target), val.fmtValue(inst_ty, target) });
+                        return sema.fail(block, inst_src, "type {} cannot represent integer value {}", .{ dest_ty.fmt(sema.mod), val.fmtValue(inst_ty, sema.mod) });
                     }
                     return try sema.addConstant(dest_ty, val);
                 }
@@ -18391,12 +18389,12 @@ fn coerce(
             .Float => {
                 if (try sema.resolveDefinedValue(block, inst_src, inst)) |val| {
                     const result_val = try val.floatCast(sema.arena, dest_ty, target);
-                    if (!val.eql(result_val, dest_ty, target)) {
+                    if (!val.eql(result_val, dest_ty, sema.mod)) {
                         return sema.fail(
                             block,
                             inst_src,
                             "type {} cannot represent float value {}",
-                            .{ dest_ty.fmt(target), val.fmtValue(inst_ty, target) },
+                            .{ dest_ty.fmt(sema.mod), val.fmtValue(inst_ty, sema.mod) },
                         );
                     }
                     return try sema.addConstant(dest_ty, result_val);
@@ -18415,12 +18413,12 @@ fn coerce(
                 const result_val = try val.intToFloat(sema.arena, inst_ty, dest_ty, target);
                 // TODO implement this compile error
                 //const int_again_val = try result_val.floatToInt(sema.arena, inst_ty);
-                //if (!int_again_val.eql(val, inst_ty, target)) {
+                //if (!int_again_val.eql(val, inst_ty, mod)) {
                 //    return sema.fail(
                 //        block,
                 //        inst_src,
                 //        "type {} cannot represent integer value {}",
-                //        .{ dest_ty.fmt(target), val },
+                //        .{ dest_ty.fmt(sema.mod), val },
                 //    );
                 //}
                 return try sema.addConstant(dest_ty, result_val);
@@ -18441,11 +18439,11 @@ fn coerce(
                             block,
                             inst_src,
                             "enum '{}' has no field named '{s}'",
-                            .{ dest_ty.fmt(target), bytes },
+                            .{ dest_ty.fmt(sema.mod), bytes },
                         );
                         errdefer msg.destroy(sema.gpa);
                         try sema.mod.errNoteNonLazy(
-                            dest_ty.declSrcLoc(),
+                            dest_ty.declSrcLoc(sema.mod),
                             msg,
                             "enum declared here",
                             .{},
@@ -18462,7 +18460,7 @@ fn coerce(
             .Union => blk: {
                 // union to its own tag type
                 const union_tag_ty = inst_ty.unionTagType() orelse break :blk;
-                if (union_tag_ty.eql(dest_ty, target)) {
+                if (union_tag_ty.eql(dest_ty, sema.mod)) {
                     return sema.unionToTag(block, dest_ty, inst, inst_src);
                 }
             },
@@ -18557,7 +18555,7 @@ fn coerce(
         return sema.addConstUndef(dest_ty);
     }
 
-    return sema.fail(block, inst_src, "expected {}, found {}", .{ dest_ty.fmt(target), inst_ty.fmt(target) });
+    return sema.fail(block, inst_src, "expected {}, found {}", .{ dest_ty.fmt(sema.mod), inst_ty.fmt(sema.mod) });
 }
 
 const InMemoryCoercionResult = enum {
@@ -18586,7 +18584,7 @@ fn coerceInMemoryAllowed(
     dest_src: LazySrcLoc,
     src_src: LazySrcLoc,
 ) CompileError!InMemoryCoercionResult {
-    if (dest_ty.eql(src_ty, target))
+    if (dest_ty.eql(src_ty, sema.mod))
         return .ok;
 
     // Differently-named integers with the same number of bits.
@@ -18650,7 +18648,7 @@ fn coerceInMemoryAllowed(
         }
         const ok_sent = dest_info.sentinel == null or
             (src_info.sentinel != null and
-            dest_info.sentinel.?.eql(src_info.sentinel.?, dest_info.elem_type, target));
+            dest_info.sentinel.?.eql(src_info.sentinel.?, dest_info.elem_type, sema.mod));
         if (!ok_sent) {
             return .no_match;
         }
@@ -18893,7 +18891,7 @@ fn coerceInMemoryAllowedPtrs(
 
     const ok_sent = dest_info.sentinel == null or src_info.size == .C or
         (src_info.sentinel != null and
-        dest_info.sentinel.?.eql(src_info.sentinel.?, dest_info.pointee_type, target));
+        dest_info.sentinel.?.eql(src_info.sentinel.?, dest_info.pointee_type, sema.mod));
     if (!ok_sent) {
         return .no_match;
     }
@@ -18934,7 +18932,7 @@ fn coerceInMemoryAllowedPtrs(
     // resolved and we compare the alignment numerically.
     alignment: {
         if (src_info.@"align" == 0 and dest_info.@"align" == 0 and
-            dest_info.pointee_type.eql(src_info.pointee_type, target))
+            dest_info.pointee_type.eql(src_info.pointee_type, sema.mod))
         {
             break :alignment;
         }
@@ -19089,8 +19087,7 @@ fn obtainBitCastedVectorPtr(sema: *Sema, ptr: Air.Inst.Ref) ?Air.Inst.Ref {
     // We have a pointer-to-array and a pointer-to-vector. If the elements and
     // lengths match, return the result.
     const vector_ty = sema.typeOf(prev_ptr).childType();
-    const target = sema.mod.getTarget();
-    if (array_ty.childType().eql(vector_ty.childType(), target) and
+    if (array_ty.childType().eql(vector_ty.childType(), sema.mod) and
         array_ty.arrayLen() == vector_ty.vectorLen())
     {
         return prev_ptr;
@@ -19114,8 +19111,8 @@ fn storePtrVal(
 
     const bitcasted_val = try sema.bitCastVal(block, src, operand_val, operand_ty, mut_kit.ty, 0);
 
-    const arena = mut_kit.beginArena(sema.gpa);
-    defer mut_kit.finishArena();
+    const arena = mut_kit.beginArena(sema.mod);
+    defer mut_kit.finishArena(sema.mod);
 
     mut_kit.val.* = try bitcasted_val.copy(arena);
 }
@@ -19126,13 +19123,15 @@ const ComptimePtrMutationKit = struct {
     ty: Type,
     decl_arena: std.heap.ArenaAllocator = undefined,
 
-    fn beginArena(self: *ComptimePtrMutationKit, gpa: Allocator) Allocator {
-        self.decl_arena = self.decl_ref_mut.decl.value_arena.?.promote(gpa);
+    fn beginArena(self: *ComptimePtrMutationKit, mod: *Module) Allocator {
+        const decl = mod.declPtr(self.decl_ref_mut.decl_index);
+        self.decl_arena = decl.value_arena.?.promote(mod.gpa);
         return self.decl_arena.allocator();
     }
 
-    fn finishArena(self: *ComptimePtrMutationKit) void {
-        self.decl_ref_mut.decl.value_arena.?.* = self.decl_arena.state;
+    fn finishArena(self: *ComptimePtrMutationKit, mod: *Module) void {
+        const decl = mod.declPtr(self.decl_ref_mut.decl_index);
+        decl.value_arena.?.* = self.decl_arena.state;
         self.decl_arena = undefined;
     }
 };
@@ -19154,10 +19153,11 @@ fn beginComptimePtrMutation(
     switch (ptr_val.tag()) {
         .decl_ref_mut => {
             const decl_ref_mut = ptr_val.castTag(.decl_ref_mut).?.data;
+            const decl = sema.mod.declPtr(decl_ref_mut.decl_index);
             return ComptimePtrMutationKit{
                 .decl_ref_mut = decl_ref_mut,
-                .val = &decl_ref_mut.decl.val,
-                .ty = decl_ref_mut.decl.ty,
+                .val = &decl.val,
+                .ty = decl.ty,
             };
         },
         .elem_ptr => {
@@ -19178,8 +19178,8 @@ fn beginComptimePtrMutation(
                             // An array has been initialized to undefined at comptime and now we
                             // are for the first time setting an element. We must change the representation
                             // of the array from `undef` to `array`.
-                            const arena = parent.beginArena(sema.gpa);
-                            defer parent.finishArena();
+                            const arena = parent.beginArena(sema.mod);
+                            defer parent.finishArena(sema.mod);
 
                             const array_len_including_sentinel =
                                 try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel());
@@ -19200,8 +19200,8 @@ fn beginComptimePtrMutation(
                             // If we wanted to avoid this, there would need to be special detection
                             // elsewhere to identify when writing a value to an array element that is stored
                             // using the `bytes` tag, and handle it without making a call to this function.
-                            const arena = parent.beginArena(sema.gpa);
-                            defer parent.finishArena();
+                            const arena = parent.beginArena(sema.mod);
+                            defer parent.finishArena(sema.mod);
 
                             const bytes = parent.val.castTag(.bytes).?.data;
                             const dest_len = parent.ty.arrayLenIncludingSentinel();
@@ -19229,8 +19229,8 @@ fn beginComptimePtrMutation(
                             // need to be special detection elsewhere to identify when writing a value to an
                             // array element that is stored using the `repeated` tag, and handle it
                             // without making a call to this function.
-                            const arena = parent.beginArena(sema.gpa);
-                            defer parent.finishArena();
+                            const arena = parent.beginArena(sema.mod);
+                            defer parent.finishArena(sema.mod);
 
                             const repeated_val = try parent.val.castTag(.repeated).?.data.copy(arena);
                             const array_len_including_sentinel =
@@ -19281,8 +19281,8 @@ fn beginComptimePtrMutation(
                     // A struct or union has been initialized to undefined at comptime and now we
                     // are for the first time setting a field. We must change the representation
                     // of the struct/union from `undef` to `struct`/`union`.
-                    const arena = parent.beginArena(sema.gpa);
-                    defer parent.finishArena();
+                    const arena = parent.beginArena(sema.mod);
+                    defer parent.finishArena(sema.mod);
 
                     switch (parent.ty.zigTypeTag()) {
                         .Struct => {
@@ -19322,8 +19322,8 @@ fn beginComptimePtrMutation(
                 },
                 .@"union" => {
                     // We need to set the active field of the union.
-                    const arena = parent.beginArena(sema.gpa);
-                    defer parent.finishArena();
+                    const arena = parent.beginArena(sema.mod);
+                    defer parent.finishArena(sema.mod);
 
                     const payload = &parent.val.castTag(.@"union").?.data;
                     payload.tag = try Value.Tag.enum_field_index.create(arena, field_index);
@@ -19347,8 +19347,8 @@ fn beginComptimePtrMutation(
                     // An error union has been initialized to undefined at comptime and now we
                     // are for the first time setting the payload. We must change the
                     // representation of the error union from `undef` to `opt_payload`.
-                    const arena = parent.beginArena(sema.gpa);
-                    defer parent.finishArena();
+                    const arena = parent.beginArena(sema.mod);
+                    defer parent.finishArena(sema.mod);
 
                     const payload = try arena.create(Value.Payload.SubValue);
                     payload.* = .{
@@ -19380,8 +19380,8 @@ fn beginComptimePtrMutation(
                     // An optional has been initialized to undefined at comptime and now we
                     // are for the first time setting the payload. We must change the
                     // representation of the optional from `undef` to `opt_payload`.
-                    const arena = parent.beginArena(sema.gpa);
-                    defer parent.finishArena();
+                    const arena = parent.beginArena(sema.mod);
+                    defer parent.finishArena(sema.mod);
 
                     const payload = try arena.create(Value.Payload.SubValue);
                     payload.* = .{
@@ -19451,12 +19451,13 @@ fn beginComptimePtrLoad(
         .decl_ref,
         .decl_ref_mut,
         => blk: {
-            const decl = switch (ptr_val.tag()) {
+            const decl_index = switch (ptr_val.tag()) {
                 .decl_ref => ptr_val.castTag(.decl_ref).?.data,
-                .decl_ref_mut => ptr_val.castTag(.decl_ref_mut).?.data.decl,
+                .decl_ref_mut => ptr_val.castTag(.decl_ref_mut).?.data.decl_index,
                 else => unreachable,
             };
             const is_mutable = ptr_val.tag() == .decl_ref_mut;
+            const decl = sema.mod.declPtr(decl_index);
             const decl_tv = try decl.typedValue();
             if (decl_tv.val.tag() == .variable) return error.RuntimeLoad;
 
@@ -19477,7 +19478,9 @@ fn beginComptimePtrLoad(
             // This code assumes that elem_ptrs have been "flattened" in order for direct dereference
             // to succeed, meaning that elem ptrs of the same elem_ty are coalesced. Here we check that
             // our parent is not an elem_ptr with the same elem_ty, since that would be "unflattened"
-            if (elem_ptr.array_ptr.castTag(.elem_ptr)) |parent_elem_ptr| assert(!(parent_elem_ptr.data.elem_ty.eql(elem_ty, target)));
+            if (elem_ptr.array_ptr.castTag(.elem_ptr)) |parent_elem_ptr| {
+                assert(!(parent_elem_ptr.data.elem_ty.eql(elem_ty, sema.mod)));
+            }
 
             if (elem_ptr.index != 0) {
                 if (elem_ty.hasWellDefinedLayout()) {
@@ -19510,11 +19513,11 @@ fn beginComptimePtrLoad(
             if (maybe_array_ty) |load_ty| {
                 // It's possible that we're loading a [N]T, in which case we'd like to slice
                 // the pointee array directly from our parent array.
-                if (load_ty.isArrayOrVector() and load_ty.childType().eql(elem_ty, target)) {
+                if (load_ty.isArrayOrVector() and load_ty.childType().eql(elem_ty, sema.mod)) {
                     const N = try sema.usizeCast(block, src, load_ty.arrayLenIncludingSentinel());
                     deref.pointee = if (elem_ptr.index + N <= check_len) TypedValue{
-                        .ty = try Type.array(sema.arena, N, null, elem_ty, target),
-                        .val = try array_tv.val.sliceArray(sema.arena, elem_ptr.index, elem_ptr.index + N),
+                        .ty = try Type.array(sema.arena, N, null, elem_ty, sema.mod),
+                        .val = try array_tv.val.sliceArray(sema.mod, sema.arena, elem_ptr.index, elem_ptr.index + N),
                     } else null;
                     break :blk deref;
                 }
@@ -19522,7 +19525,7 @@ fn beginComptimePtrLoad(
 
             deref.pointee = if (elem_ptr.index < check_len) TypedValue{
                 .ty = elem_ty,
-                .val = try array_tv.val.elemValue(sema.arena, elem_ptr.index),
+                .val = try array_tv.val.elemValue(sema.mod, sema.arena, elem_ptr.index),
             } else null;
             break :blk deref;
         },
@@ -19637,9 +19640,9 @@ fn bitCast(
 
     if (old_bits != dest_bits) {
         return sema.fail(block, inst_src, "@bitCast size mismatch: destination type '{}' has {d} bits but source type '{}' has {d} bits", .{
-            dest_ty.fmt(target),
+            dest_ty.fmt(sema.mod),
             dest_bits,
-            old_ty.fmt(target),
+            old_ty.fmt(sema.mod),
             old_bits,
         });
     }
@@ -19662,7 +19665,7 @@ pub fn bitCastVal(
     buffer_offset: usize,
 ) !Value {
     const target = sema.mod.getTarget();
-    if (old_ty.eql(new_ty, target)) return val;
+    if (old_ty.eql(new_ty, sema.mod)) return val;
 
     // For types with well-defined memory layouts, we serialize them a byte buffer,
     // then deserialize to the new type.
@@ -19718,12 +19721,11 @@ fn coerceEnumToUnion(
     inst_src: LazySrcLoc,
 ) !Air.Inst.Ref {
     const inst_ty = sema.typeOf(inst);
-    const target = sema.mod.getTarget();
 
     const tag_ty = union_ty.unionTagType() orelse {
         const msg = msg: {
             const msg = try sema.errMsg(block, inst_src, "expected {}, found {}", .{
-                union_ty.fmt(target), inst_ty.fmt(target),
+                union_ty.fmt(sema.mod), inst_ty.fmt(sema.mod),
             });
             errdefer msg.destroy(sema.gpa);
             try sema.errNote(block, union_ty_src, msg, "cannot coerce enum to untagged union", .{});
@@ -19736,10 +19738,10 @@ fn coerceEnumToUnion(
     const enum_tag = try sema.coerce(block, tag_ty, inst, inst_src);
     if (try sema.resolveDefinedValue(block, inst_src, enum_tag)) |val| {
         const union_obj = union_ty.cast(Type.Payload.Union).?.data;
-        const field_index = union_obj.tag_ty.enumTagFieldIndex(val, target) orelse {
+        const field_index = union_obj.tag_ty.enumTagFieldIndex(val, sema.mod) orelse {
             const msg = msg: {
                 const msg = try sema.errMsg(block, inst_src, "union {} has no tag with value {}", .{
-                    union_ty.fmt(target), val.fmtValue(tag_ty, target),
+                    union_ty.fmt(sema.mod), val.fmtValue(tag_ty, sema.mod),
                 });
                 errdefer msg.destroy(sema.gpa);
                 try sema.addDeclaredHereNote(msg, union_ty);
@@ -19753,7 +19755,7 @@ fn coerceEnumToUnion(
             const msg = msg: {
                 const field_name = union_obj.fields.keys()[field_index];
                 const msg = try sema.errMsg(block, inst_src, "coercion from enum '{}' to union '{}' must initialize '{}' field '{s}'", .{
-                    inst_ty.fmt(target), union_ty.fmt(target), field_ty.fmt(target), field_name,
+                    inst_ty.fmt(sema.mod), union_ty.fmt(sema.mod), field_ty.fmt(sema.mod), field_name,
                 });
                 errdefer msg.destroy(sema.gpa);
 
@@ -19775,7 +19777,7 @@ fn coerceEnumToUnion(
     if (tag_ty.isNonexhaustiveEnum()) {
         const msg = msg: {
             const msg = try sema.errMsg(block, inst_src, "runtime coercion to union {} from non-exhaustive enum", .{
-                union_ty.fmt(target),
+                union_ty.fmt(sema.mod),
             });
             errdefer msg.destroy(sema.gpa);
             try sema.addDeclaredHereNote(msg, tag_ty);
@@ -19795,7 +19797,7 @@ fn coerceEnumToUnion(
             block,
             inst_src,
             "runtime coercion from enum '{}' to union '{}' which has non-void fields",
-            .{ tag_ty.fmt(target), union_ty.fmt(target) },
+            .{ tag_ty.fmt(sema.mod), union_ty.fmt(sema.mod) },
         );
         errdefer msg.destroy(sema.gpa);
 
@@ -19804,7 +19806,7 @@ fn coerceEnumToUnion(
         while (it.next()) |field| {
             const field_name = field.key_ptr.*;
             const field_ty = field.value_ptr.ty;
-            try sema.addFieldErrNote(block, union_ty, field_index, msg, "field '{s}' has type '{}'", .{ field_name, field_ty.fmt(target) });
+            try sema.addFieldErrNote(block, union_ty, field_index, msg, "field '{s}' has type '{}'", .{ field_name, field_ty.fmt(sema.mod) });
             field_index += 1;
         }
         try sema.addDeclaredHereNote(msg, union_ty);
@@ -19892,7 +19894,7 @@ fn coerceArrayLike(
     if (dest_len != inst_len) {
         const msg = msg: {
             const msg = try sema.errMsg(block, inst_src, "expected {}, found {}", .{
-                dest_ty.fmt(target), inst_ty.fmt(target),
+                dest_ty.fmt(sema.mod), inst_ty.fmt(sema.mod),
             });
             errdefer msg.destroy(sema.gpa);
             try sema.errNote(block, dest_ty_src, msg, "destination has length {d}", .{dest_len});
@@ -19959,12 +19961,11 @@ fn coerceTupleToArray(
     const inst_ty = sema.typeOf(inst);
     const inst_len = inst_ty.arrayLen();
     const dest_len = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLen());
-    const target = sema.mod.getTarget();
 
     if (dest_len != inst_len) {
         const msg = msg: {
             const msg = try sema.errMsg(block, inst_src, "expected {}, found {}", .{
-                dest_ty.fmt(target), inst_ty.fmt(target),
+                dest_ty.fmt(sema.mod), inst_ty.fmt(sema.mod),
             });
             errdefer msg.destroy(sema.gpa);
             try sema.errNote(block, dest_ty_src, msg, "destination has length {d}", .{dest_len});
@@ -20017,8 +20018,7 @@ fn coerceTupleToSlicePtrs(
     const tuple_ty = sema.typeOf(ptr_tuple).childType();
     const tuple = try sema.analyzeLoad(block, tuple_src, ptr_tuple, tuple_src);
     const slice_info = slice_ty.ptrInfo().data;
-    const target = sema.mod.getTarget();
-    const array_ty = try Type.array(sema.arena, tuple_ty.structFieldCount(), slice_info.sentinel, slice_info.pointee_type, target);
+    const array_ty = try Type.array(sema.arena, tuple_ty.structFieldCount(), slice_info.sentinel, slice_info.pointee_type, sema.mod);
     const array_inst = try sema.coerceTupleToArray(block, array_ty, slice_ty_src, tuple, tuple_src);
     if (slice_info.@"align" != 0) {
         return sema.fail(block, slice_ty_src, "TODO: override the alignment of the array decl we create here", .{});
@@ -20141,23 +20141,23 @@ fn analyzeDeclVal(
     sema: *Sema,
     block: *Block,
     src: LazySrcLoc,
-    decl: *Decl,
+    decl_index: Decl.Index,
 ) CompileError!Air.Inst.Ref {
-    if (sema.decl_val_table.get(decl)) |result| {
+    if (sema.decl_val_table.get(decl_index)) |result| {
         return result;
     }
-    const decl_ref = try sema.analyzeDeclRef(decl);
+    const decl_ref = try sema.analyzeDeclRef(decl_index);
     const result = try sema.analyzeLoad(block, src, decl_ref, src);
     if (Air.refToIndex(result)) |index| {
         if (sema.air_instructions.items(.tag)[index] == .constant) {
-            try sema.decl_val_table.put(sema.gpa, decl, result);
+            try sema.decl_val_table.put(sema.gpa, decl_index, result);
         }
     }
     return result;
 }
 
-fn ensureDeclAnalyzed(sema: *Sema, decl: *Decl) CompileError!void {
-    sema.mod.ensureDeclAnalyzed(decl) catch |err| {
+fn ensureDeclAnalyzed(sema: *Sema, decl_index: Decl.Index) CompileError!void {
+    sema.mod.ensureDeclAnalyzed(decl_index) catch |err| {
         if (sema.owner_func) |owner_func| {
             owner_func.state = .dependency_failure;
         } else {
@@ -20186,7 +20186,7 @@ fn refValue(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type, val: Value) !
         try val.copy(anon_decl.arena()),
         0, // default alignment
     );
-    try sema.mod.declareDeclDependency(sema.owner_decl, decl);
+    try sema.mod.declareDeclDependency(sema.owner_decl_index, decl);
     return try Value.Tag.decl_ref.create(sema.arena, decl);
 }
 
@@ -20197,29 +20197,29 @@ fn optRefValue(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type, opt_val: ?
     return result;
 }
 
-fn analyzeDeclRef(sema: *Sema, decl: *Decl) CompileError!Air.Inst.Ref {
-    try sema.mod.declareDeclDependency(sema.owner_decl, decl);
-    try sema.ensureDeclAnalyzed(decl);
+fn analyzeDeclRef(sema: *Sema, decl_index: Decl.Index) CompileError!Air.Inst.Ref {
+    try sema.mod.declareDeclDependency(sema.owner_decl_index, decl_index);
+    try sema.ensureDeclAnalyzed(decl_index);
 
-    const target = sema.mod.getTarget();
+    const decl = sema.mod.declPtr(decl_index);
     const decl_tv = try decl.typedValue();
     if (decl_tv.val.castTag(.variable)) |payload| {
         const variable = payload.data;
-        const ty = try Type.ptr(sema.arena, target, .{
+        const ty = try Type.ptr(sema.arena, sema.mod, .{
             .pointee_type = decl_tv.ty,
             .mutable = variable.is_mutable,
             .@"addrspace" = decl.@"addrspace",
             .@"align" = decl.@"align",
         });
-        return sema.addConstant(ty, try Value.Tag.decl_ref.create(sema.arena, decl));
+        return sema.addConstant(ty, try Value.Tag.decl_ref.create(sema.arena, decl_index));
     }
     return sema.addConstant(
-        try Type.ptr(sema.arena, target, .{
+        try Type.ptr(sema.arena, sema.mod, .{
             .pointee_type = decl_tv.ty,
             .mutable = false,
             .@"addrspace" = decl.@"addrspace",
         }),
-        try Value.Tag.decl_ref.create(sema.arena, decl),
+        try Value.Tag.decl_ref.create(sema.arena, decl_index),
     );
 }
 
@@ -20243,13 +20243,12 @@ fn analyzeRef(
 
     try sema.requireRuntimeBlock(block, src);
     const address_space = target_util.defaultAddressSpace(sema.mod.getTarget(), .local);
-    const target = sema.mod.getTarget();
-    const ptr_type = try Type.ptr(sema.arena, target, .{
+    const ptr_type = try Type.ptr(sema.arena, sema.mod, .{
         .pointee_type = operand_ty,
         .mutable = false,
         .@"addrspace" = address_space,
     });
-    const mut_ptr_type = try Type.ptr(sema.arena, target, .{
+    const mut_ptr_type = try Type.ptr(sema.arena, sema.mod, .{
         .pointee_type = operand_ty,
         .@"addrspace" = address_space,
     });
@@ -20267,11 +20266,10 @@ fn analyzeLoad(
     ptr: Air.Inst.Ref,
     ptr_src: LazySrcLoc,
 ) CompileError!Air.Inst.Ref {
-    const target = sema.mod.getTarget();
     const ptr_ty = sema.typeOf(ptr);
     const elem_ty = switch (ptr_ty.zigTypeTag()) {
         .Pointer => ptr_ty.childType(),
-        else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty.fmt(target)}),
+        else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ty.fmt(sema.mod)}),
     };
     if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| {
         if (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) |elem_val| {
@@ -20310,8 +20308,7 @@ fn analyzeSliceLen(
         if (slice_val.isUndef()) {
             return sema.addConstUndef(Type.usize);
         }
-        const target = sema.mod.getTarget();
-        return sema.addIntUnsigned(Type.usize, slice_val.sliceLen(target));
+        return sema.addIntUnsigned(Type.usize, slice_val.sliceLen(sema.mod));
     }
     try sema.requireRuntimeBlock(block, src);
     return block.addTyOp(.slice_len, Type.usize, slice_inst);
@@ -20417,8 +20414,9 @@ fn analyzeSlice(
     const target = sema.mod.getTarget();
     const ptr_ptr_child_ty = switch (ptr_ptr_ty.zigTypeTag()) {
         .Pointer => ptr_ptr_ty.elemType(),
-        else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ptr_ty.fmt(target)}),
+        else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{ptr_ptr_ty.fmt(sema.mod)}),
     };
+    const mod = sema.mod;
 
     var array_ty = ptr_ptr_child_ty;
     var slice_ty = ptr_ptr_ty;
@@ -20465,7 +20463,7 @@ fn analyzeSlice(
                 elem_ty = ptr_ptr_child_ty.childType();
             },
         },
-        else => return sema.fail(block, ptr_src, "slice of non-array type '{}'", .{ptr_ptr_child_ty.fmt(target)}),
+        else => return sema.fail(block, ptr_src, "slice of non-array type '{}'", .{ptr_ptr_child_ty.fmt(mod)}),
     }
 
     const ptr = if (slice_ty.isSlice())
@@ -20492,7 +20490,7 @@ fn analyzeSlice(
                         sema.arena,
                         array_ty.arrayLenIncludingSentinel(),
                     );
-                    if (end_val.compare(.gt, len_s_val, Type.usize, target)) {
+                    if (end_val.compare(.gt, len_s_val, Type.usize, mod)) {
                         const sentinel_label: []const u8 = if (array_ty.sentinel() != null)
                             " +1 (sentinel)"
                         else
@@ -20503,8 +20501,8 @@ fn analyzeSlice(
                             end_src,
                             "end index {} out of bounds for array of length {}{s}",
                             .{
-                                end_val.fmtValue(Type.usize, target),
-                                len_val.fmtValue(Type.usize, target),
+                                end_val.fmtValue(Type.usize, mod),
+                                len_val.fmtValue(Type.usize, mod),
                                 sentinel_label,
                             },
                         );
@@ -20513,7 +20511,7 @@ fn analyzeSlice(
                     // end_is_len is only true if we are NOT using the sentinel
                     // length. For sentinel-length, we don't want the type to
                     // contain the sentinel.
-                    if (end_val.eql(len_val, Type.usize, target)) {
+                    if (end_val.eql(len_val, Type.usize, mod)) {
                         end_is_len = true;
                     }
                 }
@@ -20529,10 +20527,10 @@ fn analyzeSlice(
                         const has_sentinel = slice_ty.sentinel() != null;
                         var int_payload: Value.Payload.U64 = .{
                             .base = .{ .tag = .int_u64 },
-                            .data = slice_val.sliceLen(target) + @boolToInt(has_sentinel),
+                            .data = slice_val.sliceLen(mod) + @boolToInt(has_sentinel),
                         };
                         const slice_len_val = Value.initPayload(&int_payload.base);
-                        if (end_val.compare(.gt, slice_len_val, Type.usize, target)) {
+                        if (end_val.compare(.gt, slice_len_val, Type.usize, mod)) {
                             const sentinel_label: []const u8 = if (has_sentinel)
                                 " +1 (sentinel)"
                             else
@@ -20543,8 +20541,8 @@ fn analyzeSlice(
                                 end_src,
                                 "end index {} out of bounds for slice of length {d}{s}",
                                 .{
-                                    end_val.fmtValue(Type.usize, target),
-                                    slice_val.sliceLen(target),
+                                    end_val.fmtValue(Type.usize, mod),
+                                    slice_val.sliceLen(mod),
                                     sentinel_label,
                                 },
                             );
@@ -20557,7 +20555,7 @@ fn analyzeSlice(
                             int_payload.data -= 1;
                         }
 
-                        if (end_val.eql(slice_len_val, Type.usize, target)) {
+                        if (end_val.eql(slice_len_val, Type.usize, mod)) {
                             end_is_len = true;
                         }
                     }
@@ -20590,14 +20588,14 @@ fn analyzeSlice(
     // requirement: start <= end
     if (try sema.resolveDefinedValue(block, src, end)) |end_val| {
         if (try sema.resolveDefinedValue(block, src, start)) |start_val| {
-            if (start_val.compare(.gt, end_val, Type.usize, target)) {
+            if (start_val.compare(.gt, end_val, Type.usize, mod)) {
                 return sema.fail(
                     block,
                     start_src,
                     "start index {} is larger than end index {}",
                     .{
-                        start_val.fmtValue(Type.usize, target),
-                        end_val.fmtValue(Type.usize, target),
+                        start_val.fmtValue(Type.usize, mod),
+                        end_val.fmtValue(Type.usize, mod),
                     },
                 );
             }
@@ -20613,8 +20611,8 @@ fn analyzeSlice(
     if (opt_new_len_val) |new_len_val| {
         const new_len_int = new_len_val.toUnsignedInt(target);
 
-        const return_ty = try Type.ptr(sema.arena, target, .{
-            .pointee_type = try Type.array(sema.arena, new_len_int, sentinel, elem_ty, target),
+        const return_ty = try Type.ptr(sema.arena, mod, .{
+            .pointee_type = try Type.array(sema.arena, new_len_int, sentinel, elem_ty, mod),
             .sentinel = null,
             .@"align" = new_ptr_ty_info.@"align",
             .@"addrspace" = new_ptr_ty_info.@"addrspace",
@@ -20641,7 +20639,7 @@ fn analyzeSlice(
         return sema.fail(block, ptr_src, "non-zero length slice of undefined pointer", .{});
     }
 
-    const return_ty = try Type.ptr(sema.arena, target, .{
+    const return_ty = try Type.ptr(sema.arena, mod, .{
         .pointee_type = elem_ty,
         .sentinel = sentinel,
         .@"align" = new_ptr_ty_info.@"align",
@@ -20667,7 +20665,7 @@ fn analyzeSlice(
             if (try sema.resolveDefinedValue(block, src, ptr_or_slice)) |slice_val| {
                 // we don't need to add one for sentinels because the
                 // underlying value data includes the sentinel
-                break :blk try sema.addIntUnsigned(Type.usize, slice_val.sliceLen(target));
+                break :blk try sema.addIntUnsigned(Type.usize, slice_val.sliceLen(mod));
             }
 
             const slice_len_inst = try block.addTyOp(.slice_len, Type.usize, ptr_or_slice);
@@ -20920,7 +20918,6 @@ fn cmpVector(
     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
 
     const result_ty = try Type.vector(sema.arena, lhs_ty.vectorLen(), Type.@"bool");
-    const target = sema.mod.getTarget();
 
     const runtime_src: LazySrcLoc = src: {
         if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| {
@@ -20928,7 +20925,7 @@ fn cmpVector(
                 if (lhs_val.isUndef() or rhs_val.isUndef()) {
                     return sema.addConstUndef(result_ty);
                 }
-                const cmp_val = try lhs_val.compareVector(op, rhs_val, lhs_ty, sema.arena, target);
+                const cmp_val = try lhs_val.compareVector(op, rhs_val, lhs_ty, sema.arena, sema.mod);
                 return sema.addConstant(result_ty, cmp_val);
             } else {
                 break :src rhs_src;
@@ -21080,7 +21077,7 @@ fn resolvePeerTypes(
         const candidate_ty_tag = try candidate_ty.zigTypeTagOrPoison();
         const chosen_ty_tag = try chosen_ty.zigTypeTagOrPoison();
 
-        if (candidate_ty.eql(chosen_ty, target))
+        if (candidate_ty.eql(chosen_ty, sema.mod))
             continue;
 
         switch (candidate_ty_tag) {
@@ -21496,27 +21493,27 @@ fn resolvePeerTypes(
         // the source locations.
         const chosen_src = candidate_srcs.resolve(
             sema.gpa,
-            block.src_decl,
+            sema.mod.declPtr(block.src_decl),
             chosen_i,
         );
         const candidate_src = candidate_srcs.resolve(
             sema.gpa,
-            block.src_decl,
+            sema.mod.declPtr(block.src_decl),
             candidate_i + 1,
         );
 
         const msg = msg: {
             const msg = try sema.errMsg(block, src, "incompatible types: '{}' and '{}'", .{
-                chosen_ty.fmt(target),
-                candidate_ty.fmt(target),
+                chosen_ty.fmt(sema.mod),
+                candidate_ty.fmt(sema.mod),
             });
             errdefer msg.destroy(sema.gpa);
 
             if (chosen_src) |src_loc|
-                try sema.errNote(block, src_loc, msg, "type '{}' here", .{chosen_ty.fmt(target)});
+                try sema.errNote(block, src_loc, msg, "type '{}' here", .{chosen_ty.fmt(sema.mod)});
 
             if (candidate_src) |src_loc|
-                try sema.errNote(block, src_loc, msg, "type '{}' here", .{candidate_ty.fmt(target)});
+                try sema.errNote(block, src_loc, msg, "type '{}' here", .{candidate_ty.fmt(sema.mod)});
 
             break :msg msg;
         };
@@ -21538,13 +21535,13 @@ fn resolvePeerTypes(
             else => unreachable,
         };
 
-        const new_ptr_ty = try Type.ptr(sema.arena, target, info.data);
+        const new_ptr_ty = try Type.ptr(sema.arena, sema.mod, info.data);
         const opt_ptr_ty = if (any_are_null)
             try Type.optional(sema.arena, new_ptr_ty)
         else
             new_ptr_ty;
         const set_ty = err_set_ty orelse return opt_ptr_ty;
-        return try Type.errorUnion(sema.arena, set_ty, opt_ptr_ty, target);
+        return try Type.errorUnion(sema.arena, set_ty, opt_ptr_ty, sema.mod);
     }
 
     if (seen_const) {
@@ -21554,24 +21551,24 @@ fn resolvePeerTypes(
                 const ptr_ty = chosen_ty.errorUnionPayload();
                 var info = ptr_ty.ptrInfo();
                 info.data.mutable = false;
-                const new_ptr_ty = try Type.ptr(sema.arena, target, info.data);
+                const new_ptr_ty = try Type.ptr(sema.arena, sema.mod, info.data);
                 const opt_ptr_ty = if (any_are_null)
                     try Type.optional(sema.arena, new_ptr_ty)
                 else
                     new_ptr_ty;
                 const set_ty = err_set_ty orelse chosen_ty.errorUnionSet();
-                return try Type.errorUnion(sema.arena, set_ty, opt_ptr_ty, target);
+                return try Type.errorUnion(sema.arena, set_ty, opt_ptr_ty, sema.mod);
             },
             .Pointer => {
                 var info = chosen_ty.ptrInfo();
                 info.data.mutable = false;
-                const new_ptr_ty = try Type.ptr(sema.arena, target, info.data);
+                const new_ptr_ty = try Type.ptr(sema.arena, sema.mod, info.data);
                 const opt_ptr_ty = if (any_are_null)
                     try Type.optional(sema.arena, new_ptr_ty)
                 else
                     new_ptr_ty;
                 const set_ty = err_set_ty orelse return opt_ptr_ty;
-                return try Type.errorUnion(sema.arena, set_ty, opt_ptr_ty, target);
+                return try Type.errorUnion(sema.arena, set_ty, opt_ptr_ty, sema.mod);
             },
             else => return chosen_ty,
         }
@@ -21583,16 +21580,16 @@ fn resolvePeerTypes(
             else => try Type.optional(sema.arena, chosen_ty),
         };
         const set_ty = err_set_ty orelse return opt_ty;
-        return try Type.errorUnion(sema.arena, set_ty, opt_ty, target);
+        return try Type.errorUnion(sema.arena, set_ty, opt_ty, sema.mod);
     }
 
     if (err_set_ty) |ty| switch (chosen_ty.zigTypeTag()) {
         .ErrorSet => return ty,
         .ErrorUnion => {
             const payload_ty = chosen_ty.errorUnionPayload();
-            return try Type.errorUnion(sema.arena, ty, payload_ty, target);
+            return try Type.errorUnion(sema.arena, ty, payload_ty, sema.mod);
         },
-        else => return try Type.errorUnion(sema.arena, ty, chosen_ty, target),
+        else => return try Type.errorUnion(sema.arena, ty, chosen_ty, sema.mod),
     };
 
     return chosen_ty;
@@ -21670,12 +21667,11 @@ fn resolveStructLayout(
 ) CompileError!void {
     const resolved_ty = try sema.resolveTypeFields(block, src, ty);
     if (resolved_ty.castTag(.@"struct")) |payload| {
-        const target = sema.mod.getTarget();
         const struct_obj = payload.data;
         switch (struct_obj.status) {
             .none, .have_field_types => {},
             .field_types_wip, .layout_wip => {
-                return sema.fail(block, src, "struct {} depends on itself", .{ty.fmt(target)});
+                return sema.fail(block, src, "struct {} depends on itself", .{ty.fmt(sema.mod)});
             },
             .have_layout, .fully_resolved_wip, .fully_resolved => return,
         }
@@ -21703,11 +21699,10 @@ fn resolveUnionLayout(
 ) CompileError!void {
     const resolved_ty = try sema.resolveTypeFields(block, src, ty);
     const union_obj = resolved_ty.cast(Type.Payload.Union).?.data;
-    const target = sema.mod.getTarget();
     switch (union_obj.status) {
         .none, .have_field_types => {},
         .field_types_wip, .layout_wip => {
-            return sema.fail(block, src, "union {} depends on itself", .{ty.fmt(target)});
+            return sema.fail(block, src, "union {} depends on itself", .{ty.fmt(sema.mod)});
         },
         .have_layout, .fully_resolved_wip, .fully_resolved => return,
     }
@@ -21774,10 +21769,6 @@ fn resolveStructFully(
         .fully_resolved_wip, .fully_resolved => return,
     }
 
-    log.debug("resolveStructFully {*} ('{s}')", .{
-        struct_obj.owner_decl, struct_obj.owner_decl.name,
-    });
-
     {
         // After we have resolve struct layout we have to go over the fields again to
         // make sure pointer fields get their child types resolved as well.
@@ -21866,11 +21857,10 @@ fn resolveTypeFieldsStruct(
     ty: Type,
     struct_obj: *Module.Struct,
 ) CompileError!void {
-    const target = sema.mod.getTarget();
     switch (struct_obj.status) {
         .none => {},
         .field_types_wip => {
-            return sema.fail(block, src, "struct {} depends on itself", .{ty.fmt(target)});
+            return sema.fail(block, src, "struct {} depends on itself", .{ty.fmt(sema.mod)});
         },
         .have_field_types,
         .have_layout,
@@ -21897,11 +21887,10 @@ fn resolveTypeFieldsUnion(
     ty: Type,
     union_obj: *Module.Union,
 ) CompileError!void {
-    const target = sema.mod.getTarget();
     switch (union_obj.status) {
         .none => {},
         .field_types_wip => {
-            return sema.fail(block, src, "union {} depends on itself", .{ty.fmt(target)});
+            return sema.fail(block, src, "union {} depends on itself", .{ty.fmt(sema.mod)});
         },
         .have_field_types,
         .have_layout,
@@ -21945,7 +21934,8 @@ fn resolveInferredErrorSet(
     // `*Module.Fn`. Not only is the function not relevant to the inferred error set
     // in this case, it may be a generic function which would cause an assertion failure
     // if we called `ensureFuncBodyAnalyzed` on it here.
-    if (ies.func.owner_decl.ty.fnInfo().return_type.errorUnionSet().castTag(.error_set_inferred).?.data == ies) {
+    const ies_func_owner_decl = sema.mod.declPtr(ies.func.owner_decl);
+    if (ies_func_owner_decl.ty.fnInfo().return_type.errorUnionSet().castTag(.error_set_inferred).?.data == ies) {
         // In this case we are dealing with the actual InferredErrorSet object that
         // corresponds to the function, not one created to track an inline/comptime call.
         try sema.ensureFuncBodyAnalyzed(ies.func);
@@ -21986,7 +21976,7 @@ fn semaStructFields(
     defer tracy.end();
 
     const gpa = mod.gpa;
-    const decl = struct_obj.owner_decl;
+    const decl_index = struct_obj.owner_decl;
     const zir = struct_obj.namespace.file_scope.zir;
     const extended = zir.instructions.items(.data)[struct_obj.zir_index].extended;
     assert(extended.opcode == .struct_decl);
@@ -22026,6 +22016,7 @@ fn semaStructFields(
     }
     extra_index += body.len;
 
+    const decl = mod.declPtr(decl_index);
     var decl_arena = decl.value_arena.?.promote(gpa);
     defer decl.value_arena.?.* = decl_arena.state;
     const decl_arena_allocator = decl_arena.allocator();
@@ -22040,6 +22031,7 @@ fn semaStructFields(
         .perm_arena = decl_arena_allocator,
         .code = zir,
         .owner_decl = decl,
+        .owner_decl_index = decl_index,
         .func = null,
         .fn_ret_ty = Type.void,
         .owner_func = null,
@@ -22052,7 +22044,7 @@ fn semaStructFields(
     var block_scope: Block = .{
         .parent = null,
         .sema = &sema,
-        .src_decl = decl,
+        .src_decl = decl_index,
         .namespace = &struct_obj.namespace,
         .wip_capture_scope = wip_captures.scope,
         .instructions = .{},
@@ -22171,7 +22163,7 @@ fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) Compil
     defer tracy.end();
 
     const gpa = mod.gpa;
-    const decl = union_obj.owner_decl;
+    const decl_index = union_obj.owner_decl;
     const zir = union_obj.namespace.file_scope.zir;
     const extended = zir.instructions.items(.data)[union_obj.zir_index].extended;
     assert(extended.opcode == .union_decl);
@@ -22217,8 +22209,10 @@ fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) Compil
     }
     extra_index += body.len;
 
-    var decl_arena = union_obj.owner_decl.value_arena.?.promote(gpa);
-    defer union_obj.owner_decl.value_arena.?.* = decl_arena.state;
+    const decl = mod.declPtr(decl_index);
+
+    var decl_arena = decl.value_arena.?.promote(gpa);
+    defer decl.value_arena.?.* = decl_arena.state;
     const decl_arena_allocator = decl_arena.allocator();
 
     var analysis_arena = std.heap.ArenaAllocator.init(gpa);
@@ -22231,6 +22225,7 @@ fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) Compil
         .perm_arena = decl_arena_allocator,
         .code = zir,
         .owner_decl = decl,
+        .owner_decl_index = decl_index,
         .func = null,
         .fn_ret_ty = Type.void,
         .owner_func = null,
@@ -22243,7 +22238,7 @@ fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) Compil
     var block_scope: Block = .{
         .parent = null,
         .sema = &sema,
-        .src_decl = decl,
+        .src_decl = decl_index,
         .namespace = &union_obj.namespace,
         .wip_capture_scope = wip_captures.scope,
         .instructions = .{},
@@ -22353,7 +22348,7 @@ fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) Compil
                 const copied_val = try val.copy(decl_arena_allocator);
                 map.putAssumeCapacityContext(copied_val, {}, .{
                     .ty = int_tag_ty,
-                    .target = target,
+                    .mod = mod,
                 });
             } else {
                 const val = if (last_tag_val) |val|
@@ -22365,7 +22360,7 @@ fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) Compil
                 const copied_val = try val.copy(decl_arena_allocator);
                 map.putAssumeCapacityContext(copied_val, {}, .{
                     .ty = int_tag_ty,
-                    .target = target,
+                    .mod = mod,
                 });
             }
         }
@@ -22411,7 +22406,7 @@ fn semaUnionFields(block: *Block, mod: *Module, union_obj: *Module.Union) Compil
             const enum_has_field = names.orderedRemove(field_name);
             if (!enum_has_field) {
                 const msg = msg: {
-                    const msg = try sema.errMsg(block, src, "enum '{}' has no field named '{s}'", .{ union_obj.tag_ty.fmt(target), field_name });
+                    const msg = try sema.errMsg(block, src, "enum '{}' has no field named '{s}'", .{ union_obj.tag_ty.fmt(sema.mod), field_name });
                     errdefer msg.destroy(sema.gpa);
                     try sema.addDeclaredHereNote(msg, union_obj.tag_ty);
                     break :msg msg;
@@ -22475,15 +22470,16 @@ fn generateUnionTagTypeNumbered(
     const enum_ty = Type.initPayload(&enum_ty_payload.base);
     const enum_val = try Value.Tag.ty.create(new_decl_arena_allocator, enum_ty);
     // TODO better type name
-    const new_decl = try mod.createAnonymousDecl(block, .{
+    const new_decl_index = try mod.createAnonymousDecl(block, .{
         .ty = Type.type,
         .val = enum_val,
     });
+    const new_decl = mod.declPtr(new_decl_index);
     new_decl.owns_tv = true;
-    errdefer mod.abortAnonDecl(new_decl);
+    errdefer mod.abortAnonDecl(new_decl_index);
 
     enum_obj.* = .{
-        .owner_decl = new_decl,
+        .owner_decl = new_decl_index,
         .tag_ty = int_ty,
         .fields = .{},
         .values = .{},
@@ -22493,7 +22489,7 @@ fn generateUnionTagTypeNumbered(
     try enum_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len);
     try enum_obj.values.ensureTotalCapacityContext(new_decl_arena_allocator, fields_len, .{
         .ty = int_ty,
-        .target = sema.mod.getTarget(),
+        .mod = mod,
     });
     try new_decl.finalizeNewArena(&new_decl_arena);
     return enum_ty;
@@ -22515,15 +22511,16 @@ fn generateUnionTagTypeSimple(sema: *Sema, block: *Block, fields_len: usize) !Ty
     const enum_ty = Type.initPayload(&enum_ty_payload.base);
     const enum_val = try Value.Tag.ty.create(new_decl_arena_allocator, enum_ty);
     // TODO better type name
-    const new_decl = try mod.createAnonymousDecl(block, .{
+    const new_decl_index = try mod.createAnonymousDecl(block, .{
         .ty = Type.type,
         .val = enum_val,
     });
+    const new_decl = mod.declPtr(new_decl_index);
     new_decl.owns_tv = true;
-    errdefer mod.abortAnonDecl(new_decl);
+    errdefer mod.abortAnonDecl(new_decl_index);
 
     enum_obj.* = .{
-        .owner_decl = new_decl,
+        .owner_decl = new_decl_index,
         .fields = .{},
         .node_offset = 0,
     };
@@ -22545,7 +22542,7 @@ fn getBuiltin(
     const opt_builtin_inst = try sema.namespaceLookupRef(
         block,
         src,
-        std_file.root_decl.?.src_namespace,
+        mod.declPtr(std_file.root_decl.unwrap().?).src_namespace,
         "builtin",
     );
     const builtin_inst = try sema.analyzeLoad(block, src, opt_builtin_inst.?, src);
@@ -22984,8 +22981,7 @@ fn analyzeComptimeAlloc(
     // Needed to make an anon decl with type `var_type` (the `finish()` call below).
     _ = try sema.typeHasOnePossibleValue(block, src, var_type);
 
-    const target = sema.mod.getTarget();
-    const ptr_type = try Type.ptr(sema.arena, target, .{
+    const ptr_type = try Type.ptr(sema.arena, sema.mod, .{
         .pointee_type = var_type,
         .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .global_constant),
         .@"align" = alignment,
@@ -22994,7 +22990,7 @@ fn analyzeComptimeAlloc(
     var anon_decl = try block.startAnonDecl(src);
     defer anon_decl.deinit();
 
-    const decl = try anon_decl.finish(
+    const decl_index = try anon_decl.finish(
         try var_type.copy(anon_decl.arena()),
         // There will be stores before the first load, but they may be to sub-elements or
         // sub-fields. So we need to initialize with undef to allow the mechanism to expand
@@ -23002,12 +22998,13 @@ fn analyzeComptimeAlloc(
         Value.undef,
         alignment,
     );
+    const decl = sema.mod.declPtr(decl_index);
     decl.@"align" = alignment;
 
-    try sema.mod.declareDeclDependency(sema.owner_decl, decl);
+    try sema.mod.declareDeclDependency(sema.owner_decl_index, decl_index);
     return sema.addConstant(ptr_type, try Value.Tag.decl_ref_mut.create(sema.arena, .{
         .runtime_index = block.runtime_index,
-        .decl = decl,
+        .decl_index = decl_index,
     }));
 }
 
@@ -23099,7 +23096,7 @@ fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr
     // The type is not in-memory coercible or the direct dereference failed, so it must
     // be bitcast according to the pointer type we are performing the load through.
     if (!load_ty.hasWellDefinedLayout())
-        return sema.fail(block, src, "comptime dereference requires {} to have a well-defined layout, but it does not.", .{load_ty.fmt(target)});
+        return sema.fail(block, src, "comptime dereference requires {} to have a well-defined layout, but it does not.", .{load_ty.fmt(sema.mod)});
 
     const load_sz = try sema.typeAbiSize(block, src, load_ty);
 
@@ -23114,11 +23111,11 @@ fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr
     if (deref.ty_without_well_defined_layout) |bad_ty| {
         // We got no parent for bit-casting, or the parent we got was too small. Either way, the problem
         // is that some type we encountered when de-referencing does not have a well-defined layout.
-        return sema.fail(block, src, "comptime dereference requires {} to have a well-defined layout, but it does not.", .{bad_ty.fmt(target)});
+        return sema.fail(block, src, "comptime dereference requires {} to have a well-defined layout, but it does not.", .{bad_ty.fmt(sema.mod)});
     } else {
         // If all encountered types had well-defined layouts, the parent is the root decl and it just
         // wasn't big enough for the load.
-        return sema.fail(block, src, "dereference of {} exceeds bounds of containing decl of type {}", .{ ptr_ty.fmt(target), deref.parent.?.tv.ty.fmt(target) });
+        return sema.fail(block, src, "dereference of {} exceeds bounds of containing decl of type {}", .{ ptr_ty.fmt(sema.mod), deref.parent.?.tv.ty.fmt(sema.mod) });
     }
 }
 
@@ -23484,9 +23481,8 @@ fn anonStructFieldIndex(
             return @intCast(u32, i);
         }
     }
-    const target = sema.mod.getTarget();
     return sema.fail(block, field_src, "anonymous struct {} has no such field '{s}'", .{
-        struct_ty.fmt(target), field_name,
+        struct_ty.fmt(sema.mod), field_name,
     });
 }
 
src/type.zig
@@ -521,7 +521,7 @@ pub const Type = extern union {
         }
     }
 
-    pub fn eql(a: Type, b: Type, target: Target) bool {
+    pub fn eql(a: Type, b: Type, mod: *Module) bool {
         // As a shortcut, if the small tags / addresses match, we're done.
         if (a.tag_if_small_enough == b.tag_if_small_enough) return true;
 
@@ -637,7 +637,7 @@ pub const Type = extern union {
                 const a_info = a.fnInfo();
                 const b_info = b.fnInfo();
 
-                if (!eql(a_info.return_type, b_info.return_type, target))
+                if (!eql(a_info.return_type, b_info.return_type, mod))
                     return false;
 
                 if (a_info.cc != b_info.cc)
@@ -663,7 +663,7 @@ pub const Type = extern union {
                     if (a_param_ty.tag() == .generic_poison) continue;
                     if (b_param_ty.tag() == .generic_poison) continue;
 
-                    if (!eql(a_param_ty, b_param_ty, target))
+                    if (!eql(a_param_ty, b_param_ty, mod))
                         return false;
                 }
 
@@ -681,13 +681,13 @@ pub const Type = extern union {
                 if (a.arrayLen() != b.arrayLen())
                     return false;
                 const elem_ty = a.elemType();
-                if (!elem_ty.eql(b.elemType(), target))
+                if (!elem_ty.eql(b.elemType(), mod))
                     return false;
                 const sentinel_a = a.sentinel();
                 const sentinel_b = b.sentinel();
                 if (sentinel_a) |sa| {
                     if (sentinel_b) |sb| {
-                        return sa.eql(sb, elem_ty, target);
+                        return sa.eql(sb, elem_ty, mod);
                     } else {
                         return false;
                     }
@@ -718,7 +718,7 @@ pub const Type = extern union {
 
                 const info_a = a.ptrInfo().data;
                 const info_b = b.ptrInfo().data;
-                if (!info_a.pointee_type.eql(info_b.pointee_type, target))
+                if (!info_a.pointee_type.eql(info_b.pointee_type, mod))
                     return false;
                 if (info_a.@"align" != info_b.@"align")
                     return false;
@@ -741,7 +741,7 @@ pub const Type = extern union {
                 const sentinel_b = info_b.sentinel;
                 if (sentinel_a) |sa| {
                     if (sentinel_b) |sb| {
-                        if (!sa.eql(sb, info_a.pointee_type, target))
+                        if (!sa.eql(sb, info_a.pointee_type, mod))
                             return false;
                     } else {
                         return false;
@@ -762,7 +762,7 @@ pub const Type = extern union {
 
                 var buf_a: Payload.ElemType = undefined;
                 var buf_b: Payload.ElemType = undefined;
-                return a.optionalChild(&buf_a).eql(b.optionalChild(&buf_b), target);
+                return a.optionalChild(&buf_a).eql(b.optionalChild(&buf_b), mod);
             },
 
             .anyerror_void_error_union, .error_union => {
@@ -770,18 +770,18 @@ pub const Type = extern union {
 
                 const a_set = a.errorUnionSet();
                 const b_set = b.errorUnionSet();
-                if (!a_set.eql(b_set, target)) return false;
+                if (!a_set.eql(b_set, mod)) return false;
 
                 const a_payload = a.errorUnionPayload();
                 const b_payload = b.errorUnionPayload();
-                if (!a_payload.eql(b_payload, target)) return false;
+                if (!a_payload.eql(b_payload, mod)) return false;
 
                 return true;
             },
 
             .anyframe_T => {
                 if (b.zigTypeTag() != .AnyFrame) return false;
-                return a.childType().eql(b.childType(), target);
+                return a.childType().eql(b.childType(), mod);
             },
 
             .empty_struct => {
@@ -804,7 +804,7 @@ pub const Type = extern union {
 
                 for (a_tuple.types) |a_ty, i| {
                     const b_ty = b_tuple.types[i];
-                    if (!eql(a_ty, b_ty, target)) return false;
+                    if (!eql(a_ty, b_ty, mod)) return false;
                 }
 
                 for (a_tuple.values) |a_val, i| {
@@ -820,7 +820,7 @@ pub const Type = extern union {
                         if (b_val.tag() == .unreachable_value) {
                             return false;
                         } else {
-                            if (!Value.eql(a_val, b_val, ty, target)) return false;
+                            if (!Value.eql(a_val, b_val, ty, mod)) return false;
                         }
                     }
                 }
@@ -840,7 +840,7 @@ pub const Type = extern union {
 
                 for (a_struct_obj.types) |a_ty, i| {
                     const b_ty = b_struct_obj.types[i];
-                    if (!eql(a_ty, b_ty, target)) return false;
+                    if (!eql(a_ty, b_ty, mod)) return false;
                 }
 
                 for (a_struct_obj.values) |a_val, i| {
@@ -856,7 +856,7 @@ pub const Type = extern union {
                         if (b_val.tag() == .unreachable_value) {
                             return false;
                         } else {
-                            if (!Value.eql(a_val, b_val, ty, target)) return false;
+                            if (!Value.eql(a_val, b_val, ty, mod)) return false;
                         }
                     }
                 }
@@ -911,13 +911,13 @@ pub const Type = extern union {
         }
     }
 
-    pub fn hash(self: Type, target: Target) u64 {
+    pub fn hash(self: Type, mod: *Module) u64 {
         var hasher = std.hash.Wyhash.init(0);
-        self.hashWithHasher(&hasher, target);
+        self.hashWithHasher(&hasher, mod);
         return hasher.final();
     }
 
-    pub fn hashWithHasher(ty: Type, hasher: *std.hash.Wyhash, target: Target) void {
+    pub fn hashWithHasher(ty: Type, hasher: *std.hash.Wyhash, mod: *Module) void {
         switch (ty.tag()) {
             .generic_poison => unreachable,
 
@@ -1036,7 +1036,7 @@ pub const Type = extern union {
                 std.hash.autoHash(hasher, std.builtin.TypeId.Fn);
 
                 const fn_info = ty.fnInfo();
-                hashWithHasher(fn_info.return_type, hasher, target);
+                hashWithHasher(fn_info.return_type, hasher, mod);
                 std.hash.autoHash(hasher, fn_info.alignment);
                 std.hash.autoHash(hasher, fn_info.cc);
                 std.hash.autoHash(hasher, fn_info.is_var_args);
@@ -1046,7 +1046,7 @@ pub const Type = extern union {
                 for (fn_info.param_types) |param_ty, i| {
                     std.hash.autoHash(hasher, fn_info.paramIsComptime(i));
                     if (param_ty.tag() == .generic_poison) continue;
-                    hashWithHasher(param_ty, hasher, target);
+                    hashWithHasher(param_ty, hasher, mod);
                 }
             },
 
@@ -1059,8 +1059,8 @@ pub const Type = extern union {
 
                 const elem_ty = ty.elemType();
                 std.hash.autoHash(hasher, ty.arrayLen());
-                hashWithHasher(elem_ty, hasher, target);
-                hashSentinel(ty.sentinel(), elem_ty, hasher, target);
+                hashWithHasher(elem_ty, hasher, mod);
+                hashSentinel(ty.sentinel(), elem_ty, hasher, mod);
             },
 
             .vector => {
@@ -1068,7 +1068,7 @@ pub const Type = extern union {
 
                 const elem_ty = ty.elemType();
                 std.hash.autoHash(hasher, ty.vectorLen());
-                hashWithHasher(elem_ty, hasher, target);
+                hashWithHasher(elem_ty, hasher, mod);
             },
 
             .single_const_pointer_to_comptime_int,
@@ -1092,8 +1092,8 @@ pub const Type = extern union {
                 std.hash.autoHash(hasher, std.builtin.TypeId.Pointer);
 
                 const info = ty.ptrInfo().data;
-                hashWithHasher(info.pointee_type, hasher, target);
-                hashSentinel(info.sentinel, info.pointee_type, hasher, target);
+                hashWithHasher(info.pointee_type, hasher, mod);
+                hashSentinel(info.sentinel, info.pointee_type, hasher, mod);
                 std.hash.autoHash(hasher, info.@"align");
                 std.hash.autoHash(hasher, info.@"addrspace");
                 std.hash.autoHash(hasher, info.bit_offset);
@@ -1111,22 +1111,22 @@ pub const Type = extern union {
                 std.hash.autoHash(hasher, std.builtin.TypeId.Optional);
 
                 var buf: Payload.ElemType = undefined;
-                hashWithHasher(ty.optionalChild(&buf), hasher, target);
+                hashWithHasher(ty.optionalChild(&buf), hasher, mod);
             },
 
             .anyerror_void_error_union, .error_union => {
                 std.hash.autoHash(hasher, std.builtin.TypeId.ErrorUnion);
 
                 const set_ty = ty.errorUnionSet();
-                hashWithHasher(set_ty, hasher, target);
+                hashWithHasher(set_ty, hasher, mod);
 
                 const payload_ty = ty.errorUnionPayload();
-                hashWithHasher(payload_ty, hasher, target);
+                hashWithHasher(payload_ty, hasher, mod);
             },
 
             .anyframe_T => {
                 std.hash.autoHash(hasher, std.builtin.TypeId.AnyFrame);
-                hashWithHasher(ty.childType(), hasher, target);
+                hashWithHasher(ty.childType(), hasher, mod);
             },
 
             .empty_struct => {
@@ -1145,10 +1145,10 @@ pub const Type = extern union {
                 std.hash.autoHash(hasher, tuple.types.len);
 
                 for (tuple.types) |field_ty, i| {
-                    hashWithHasher(field_ty, hasher, target);
+                    hashWithHasher(field_ty, hasher, mod);
                     const field_val = tuple.values[i];
                     if (field_val.tag() == .unreachable_value) continue;
-                    field_val.hash(field_ty, hasher, target);
+                    field_val.hash(field_ty, hasher, mod);
                 }
             },
             .anon_struct => {
@@ -1160,9 +1160,9 @@ pub const Type = extern union {
                     const field_name = struct_obj.names[i];
                     const field_val = struct_obj.values[i];
                     hasher.update(field_name);
-                    hashWithHasher(field_ty, hasher, target);
+                    hashWithHasher(field_ty, hasher, mod);
                     if (field_val.tag() == .unreachable_value) continue;
-                    field_val.hash(field_ty, hasher, target);
+                    field_val.hash(field_ty, hasher, mod);
                 }
             },
 
@@ -1210,35 +1210,35 @@ pub const Type = extern union {
         }
     }
 
-    fn hashSentinel(opt_val: ?Value, ty: Type, hasher: *std.hash.Wyhash, target: Target) void {
+    fn hashSentinel(opt_val: ?Value, ty: Type, hasher: *std.hash.Wyhash, mod: *Module) void {
         if (opt_val) |s| {
             std.hash.autoHash(hasher, true);
-            s.hash(ty, hasher, target);
+            s.hash(ty, hasher, mod);
         } else {
             std.hash.autoHash(hasher, false);
         }
     }
 
     pub const HashContext64 = struct {
-        target: Target,
+        mod: *Module,
 
         pub fn hash(self: @This(), t: Type) u64 {
-            return t.hash(self.target);
+            return t.hash(self.mod);
         }
         pub fn eql(self: @This(), a: Type, b: Type) bool {
-            return a.eql(b, self.target);
+            return a.eql(b, self.mod);
         }
     };
 
     pub const HashContext32 = struct {
-        target: Target,
+        mod: *Module,
 
         pub fn hash(self: @This(), t: Type) u32 {
-            return @truncate(u32, t.hash(self.target));
+            return @truncate(u32, t.hash(self.mod));
         }
         pub fn eql(self: @This(), a: Type, b: Type, b_index: usize) bool {
             _ = b_index;
-            return a.eql(b, self.target);
+            return a.eql(b, self.mod);
         }
     };
 
@@ -1483,16 +1483,16 @@ pub const Type = extern union {
         @compileError("do not format types directly; use either ty.fmtDebug() or ty.fmt()");
     }
 
-    pub fn fmt(ty: Type, target: Target) std.fmt.Formatter(format2) {
+    pub fn fmt(ty: Type, module: *Module) std.fmt.Formatter(format2) {
         return .{ .data = .{
             .ty = ty,
-            .target = target,
+            .module = module,
         } };
     }
 
     const FormatContext = struct {
         ty: Type,
-        target: Target,
+        module: *Module,
     };
 
     fn format2(
@@ -1503,7 +1503,7 @@ pub const Type = extern union {
     ) !void {
         comptime assert(unused_format_string.len == 0);
         _ = options;
-        return print(ctx.ty, writer, ctx.target);
+        return print(ctx.ty, writer, ctx.module);
     }
 
     pub fn fmtDebug(ty: Type) std.fmt.Formatter(dump) {
@@ -1579,27 +1579,39 @@ pub const Type = extern union {
 
                 .@"struct" => {
                     const struct_obj = ty.castTag(.@"struct").?.data;
-                    return struct_obj.owner_decl.renderFullyQualifiedName(writer);
+                    return writer.print("({s} decl={d})", .{
+                        @tagName(t), struct_obj.owner_decl,
+                    });
                 },
                 .@"union", .union_tagged => {
                     const union_obj = ty.cast(Payload.Union).?.data;
-                    return union_obj.owner_decl.renderFullyQualifiedName(writer);
+                    return writer.print("({s} decl={d})", .{
+                        @tagName(t), union_obj.owner_decl,
+                    });
                 },
                 .enum_full, .enum_nonexhaustive => {
                     const enum_full = ty.cast(Payload.EnumFull).?.data;
-                    return enum_full.owner_decl.renderFullyQualifiedName(writer);
+                    return writer.print("({s} decl={d})", .{
+                        @tagName(t), enum_full.owner_decl,
+                    });
                 },
                 .enum_simple => {
                     const enum_simple = ty.castTag(.enum_simple).?.data;
-                    return enum_simple.owner_decl.renderFullyQualifiedName(writer);
+                    return writer.print("({s} decl={d})", .{
+                        @tagName(t), enum_simple.owner_decl,
+                    });
                 },
                 .enum_numbered => {
                     const enum_numbered = ty.castTag(.enum_numbered).?.data;
-                    return enum_numbered.owner_decl.renderFullyQualifiedName(writer);
+                    return writer.print("({s} decl={d})", .{
+                        @tagName(t), enum_numbered.owner_decl,
+                    });
                 },
                 .@"opaque" => {
-                    // TODO use declaration name
-                    return writer.writeAll("opaque {}");
+                    const opaque_obj = ty.castTag(.@"opaque").?.data;
+                    return writer.print("({s} decl={d})", .{
+                        @tagName(t), opaque_obj.owner_decl,
+                    });
                 },
 
                 .anyerror_void_error_union => return writer.writeAll("anyerror!void"),
@@ -1845,7 +1857,9 @@ pub const Type = extern union {
                 },
                 .error_set_inferred => {
                     const func = ty.castTag(.error_set_inferred).?.data.func;
-                    return writer.print("@typeInfo(@typeInfo(@TypeOf({s})).Fn.return_type.?).ErrorUnion.error_set", .{func.owner_decl.name});
+                    return writer.print("({s} func={d})", .{
+                        @tagName(t), func.owner_decl,
+                    });
                 },
                 .error_set_merged => {
                     const names = ty.castTag(.error_set_merged).?.data.keys();
@@ -1871,15 +1885,15 @@ pub const Type = extern union {
 
     pub const nameAllocArena = nameAlloc;
 
-    pub fn nameAlloc(ty: Type, ally: Allocator, target: Target) Allocator.Error![:0]const u8 {
+    pub fn nameAlloc(ty: Type, ally: Allocator, module: *Module) Allocator.Error![:0]const u8 {
         var buffer = std.ArrayList(u8).init(ally);
         defer buffer.deinit();
-        try ty.print(buffer.writer(), target);
+        try ty.print(buffer.writer(), module);
         return buffer.toOwnedSliceSentinel(0);
     }
 
     /// Prints a name suitable for `@typeName`.
-    pub fn print(ty: Type, writer: anytype, target: Target) @TypeOf(writer).Error!void {
+    pub fn print(ty: Type, writer: anytype, mod: *Module) @TypeOf(writer).Error!void {
         const t = ty.tag();
         switch (t) {
             .inferred_alloc_const => unreachable,
@@ -1946,32 +1960,38 @@ pub const Type = extern union {
 
             .empty_struct => {
                 const namespace = ty.castTag(.empty_struct).?.data;
-                try namespace.renderFullyQualifiedName("", writer);
+                try namespace.renderFullyQualifiedName(mod, "", writer);
             },
 
             .@"struct" => {
                 const struct_obj = ty.castTag(.@"struct").?.data;
-                try struct_obj.owner_decl.renderFullyQualifiedName(writer);
+                const decl = mod.declPtr(struct_obj.owner_decl);
+                try decl.renderFullyQualifiedName(mod, writer);
             },
             .@"union", .union_tagged => {
                 const union_obj = ty.cast(Payload.Union).?.data;
-                try union_obj.owner_decl.renderFullyQualifiedName(writer);
+                const decl = mod.declPtr(union_obj.owner_decl);
+                try decl.renderFullyQualifiedName(mod, writer);
             },
             .enum_full, .enum_nonexhaustive => {
                 const enum_full = ty.cast(Payload.EnumFull).?.data;
-                try enum_full.owner_decl.renderFullyQualifiedName(writer);
+                const decl = mod.declPtr(enum_full.owner_decl);
+                try decl.renderFullyQualifiedName(mod, writer);
             },
             .enum_simple => {
                 const enum_simple = ty.castTag(.enum_simple).?.data;
-                try enum_simple.owner_decl.renderFullyQualifiedName(writer);
+                const decl = mod.declPtr(enum_simple.owner_decl);
+                try decl.renderFullyQualifiedName(mod, writer);
             },
             .enum_numbered => {
                 const enum_numbered = ty.castTag(.enum_numbered).?.data;
-                try enum_numbered.owner_decl.renderFullyQualifiedName(writer);
+                const decl = mod.declPtr(enum_numbered.owner_decl);
+                try decl.renderFullyQualifiedName(mod, writer);
             },
             .@"opaque" => {
                 const opaque_obj = ty.cast(Payload.Opaque).?.data;
-                try opaque_obj.owner_decl.renderFullyQualifiedName(writer);
+                const decl = mod.declPtr(opaque_obj.owner_decl);
+                try decl.renderFullyQualifiedName(mod, writer);
             },
 
             .anyerror_void_error_union => try writer.writeAll("anyerror!void"),
@@ -1990,7 +2010,8 @@ pub const Type = extern union {
                 const func = ty.castTag(.error_set_inferred).?.data.func;
 
                 try writer.writeAll("@typeInfo(@typeInfo(@TypeOf(");
-                try func.owner_decl.renderFullyQualifiedName(writer);
+                const owner_decl = mod.declPtr(func.owner_decl);
+                try owner_decl.renderFullyQualifiedName(mod, writer);
                 try writer.writeAll(")).Fn.return_type.?).ErrorUnion.error_set");
             },
 
@@ -1999,7 +2020,7 @@ pub const Type = extern union {
                 try writer.writeAll("fn(");
                 for (fn_info.param_types) |param_ty, i| {
                     if (i != 0) try writer.writeAll(", ");
-                    try print(param_ty, writer, target);
+                    try print(param_ty, writer, mod);
                 }
                 if (fn_info.is_var_args) {
                     if (fn_info.param_types.len != 0) {
@@ -2016,14 +2037,14 @@ pub const Type = extern union {
                 if (fn_info.alignment != 0) {
                     try writer.print("align({d}) ", .{fn_info.alignment});
                 }
-                try print(fn_info.return_type, writer, target);
+                try print(fn_info.return_type, writer, mod);
             },
 
             .error_union => {
                 const error_union = ty.castTag(.error_union).?.data;
-                try print(error_union.error_set, writer, target);
+                try print(error_union.error_set, writer, mod);
                 try writer.writeAll("!");
-                try print(error_union.payload, writer, target);
+                try print(error_union.payload, writer, mod);
             },
 
             .array_u8 => {
@@ -2037,21 +2058,21 @@ pub const Type = extern union {
             .vector => {
                 const payload = ty.castTag(.vector).?.data;
                 try writer.print("@Vector({d}, ", .{payload.len});
-                try print(payload.elem_type, writer, target);
+                try print(payload.elem_type, writer, mod);
                 try writer.writeAll(")");
             },
             .array => {
                 const payload = ty.castTag(.array).?.data;
                 try writer.print("[{d}]", .{payload.len});
-                try print(payload.elem_type, writer, target);
+                try print(payload.elem_type, writer, mod);
             },
             .array_sentinel => {
                 const payload = ty.castTag(.array_sentinel).?.data;
                 try writer.print("[{d}:{}]", .{
                     payload.len,
-                    payload.sentinel.fmtValue(payload.elem_type, target),
+                    payload.sentinel.fmtValue(payload.elem_type, mod),
                 });
-                try print(payload.elem_type, writer, target);
+                try print(payload.elem_type, writer, mod);
             },
             .tuple => {
                 const tuple = ty.castTag(.tuple).?.data;
@@ -2063,9 +2084,9 @@ pub const Type = extern union {
                     if (val.tag() != .unreachable_value) {
                         try writer.writeAll("comptime ");
                     }
-                    try print(field_ty, writer, target);
+                    try print(field_ty, writer, mod);
                     if (val.tag() != .unreachable_value) {
-                        try writer.print(" = {}", .{val.fmtValue(field_ty, target)});
+                        try writer.print(" = {}", .{val.fmtValue(field_ty, mod)});
                     }
                 }
                 try writer.writeAll("}");
@@ -2083,10 +2104,10 @@ pub const Type = extern union {
                     try writer.writeAll(anon_struct.names[i]);
                     try writer.writeAll(": ");
 
-                    try print(field_ty, writer, target);
+                    try print(field_ty, writer, mod);
 
                     if (val.tag() != .unreachable_value) {
-                        try writer.print(" = {}", .{val.fmtValue(field_ty, target)});
+                        try writer.print(" = {}", .{val.fmtValue(field_ty, mod)});
                     }
                 }
                 try writer.writeAll("}");
@@ -2106,8 +2127,8 @@ pub const Type = extern union {
 
                 if (info.sentinel) |s| switch (info.size) {
                     .One, .C => unreachable,
-                    .Many => try writer.print("[*:{}]", .{s.fmtValue(info.pointee_type, target)}),
-                    .Slice => try writer.print("[:{}]", .{s.fmtValue(info.pointee_type, target)}),
+                    .Many => try writer.print("[*:{}]", .{s.fmtValue(info.pointee_type, mod)}),
+                    .Slice => try writer.print("[:{}]", .{s.fmtValue(info.pointee_type, mod)}),
                 } else switch (info.size) {
                     .One => try writer.writeAll("*"),
                     .Many => try writer.writeAll("[*]"),
@@ -2129,7 +2150,7 @@ pub const Type = extern union {
                 if (info.@"volatile") try writer.writeAll("volatile ");
                 if (info.@"allowzero" and info.size != .C) try writer.writeAll("allowzero ");
 
-                try print(info.pointee_type, writer, target);
+                try print(info.pointee_type, writer, mod);
             },
 
             .int_signed => {
@@ -2143,22 +2164,22 @@ pub const Type = extern union {
             .optional => {
                 const child_type = ty.castTag(.optional).?.data;
                 try writer.writeByte('?');
-                try print(child_type, writer, target);
+                try print(child_type, writer, mod);
             },
             .optional_single_mut_pointer => {
                 const pointee_type = ty.castTag(.optional_single_mut_pointer).?.data;
                 try writer.writeAll("?*");
-                try print(pointee_type, writer, target);
+                try print(pointee_type, writer, mod);
             },
             .optional_single_const_pointer => {
                 const pointee_type = ty.castTag(.optional_single_const_pointer).?.data;
                 try writer.writeAll("?*const ");
-                try print(pointee_type, writer, target);
+                try print(pointee_type, writer, mod);
             },
             .anyframe_T => {
                 const return_type = ty.castTag(.anyframe_T).?.data;
                 try writer.print("anyframe->", .{});
-                try print(return_type, writer, target);
+                try print(return_type, writer, mod);
             },
             .error_set => {
                 const names = ty.castTag(.error_set).?.data.names.keys();
@@ -3834,8 +3855,8 @@ pub const Type = extern union {
     /// For [*]T, returns *T
     /// For []T, returns *T
     /// Handles const-ness and address spaces in particular.
-    pub fn elemPtrType(ptr_ty: Type, arena: Allocator, target: Target) !Type {
-        return try Type.ptr(arena, target, .{
+    pub fn elemPtrType(ptr_ty: Type, arena: Allocator, mod: *Module) !Type {
+        return try Type.ptr(arena, mod, .{
             .pointee_type = ptr_ty.elemType2(),
             .mutable = ptr_ty.ptrIsMutable(),
             .@"addrspace" = ptr_ty.ptrAddressSpace(),
@@ -3948,9 +3969,9 @@ pub const Type = extern union {
         return union_obj.fields;
     }
 
-    pub fn unionFieldType(ty: Type, enum_tag: Value, target: Target) Type {
+    pub fn unionFieldType(ty: Type, enum_tag: Value, mod: *Module) Type {
         const union_obj = ty.cast(Payload.Union).?.data;
-        const index = union_obj.tag_ty.enumTagFieldIndex(enum_tag, target).?;
+        const index = union_obj.tag_ty.enumTagFieldIndex(enum_tag, mod).?;
         assert(union_obj.haveFieldTypes());
         return union_obj.fields.values()[index].ty;
     }
@@ -4970,20 +4991,20 @@ pub const Type = extern union {
     /// Asserts `ty` is an enum. `enum_tag` can either be `enum_field_index` or
     /// an integer which represents the enum value. Returns the field index in
     /// declaration order, or `null` if `enum_tag` does not match any field.
-    pub fn enumTagFieldIndex(ty: Type, enum_tag: Value, target: Target) ?usize {
+    pub fn enumTagFieldIndex(ty: Type, enum_tag: Value, mod: *Module) ?usize {
         if (enum_tag.castTag(.enum_field_index)) |payload| {
             return @as(usize, payload.data);
         }
         const S = struct {
-            fn fieldWithRange(int_ty: Type, int_val: Value, end: usize, tg: Target) ?usize {
+            fn fieldWithRange(int_ty: Type, int_val: Value, end: usize, m: *Module) ?usize {
                 if (int_val.compareWithZero(.lt)) return null;
                 var end_payload: Value.Payload.U64 = .{
                     .base = .{ .tag = .int_u64 },
                     .data = end,
                 };
                 const end_val = Value.initPayload(&end_payload.base);
-                if (int_val.compare(.gte, end_val, int_ty, tg)) return null;
-                return @intCast(usize, int_val.toUnsignedInt(tg));
+                if (int_val.compare(.gte, end_val, int_ty, m)) return null;
+                return @intCast(usize, int_val.toUnsignedInt(m.getTarget()));
             }
         };
         switch (ty.tag()) {
@@ -4991,11 +5012,11 @@ pub const Type = extern union {
                 const enum_full = ty.cast(Payload.EnumFull).?.data;
                 const tag_ty = enum_full.tag_ty;
                 if (enum_full.values.count() == 0) {
-                    return S.fieldWithRange(tag_ty, enum_tag, enum_full.fields.count(), target);
+                    return S.fieldWithRange(tag_ty, enum_tag, enum_full.fields.count(), mod);
                 } else {
                     return enum_full.values.getIndexContext(enum_tag, .{
                         .ty = tag_ty,
-                        .target = target,
+                        .mod = mod,
                     });
                 }
             },
@@ -5003,11 +5024,11 @@ pub const Type = extern union {
                 const enum_obj = ty.castTag(.enum_numbered).?.data;
                 const tag_ty = enum_obj.tag_ty;
                 if (enum_obj.values.count() == 0) {
-                    return S.fieldWithRange(tag_ty, enum_tag, enum_obj.fields.count(), target);
+                    return S.fieldWithRange(tag_ty, enum_tag, enum_obj.fields.count(), mod);
                 } else {
                     return enum_obj.values.getIndexContext(enum_tag, .{
                         .ty = tag_ty,
-                        .target = target,
+                        .mod = mod,
                     });
                 }
             },
@@ -5020,7 +5041,7 @@ pub const Type = extern union {
                     .data = bits,
                 };
                 const tag_ty = Type.initPayload(&buffer.base);
-                return S.fieldWithRange(tag_ty, enum_tag, fields_len, target);
+                return S.fieldWithRange(tag_ty, enum_tag, fields_len, mod);
             },
             .atomic_order,
             .atomic_rmw_op,
@@ -5224,32 +5245,35 @@ pub const Type = extern union {
         }
     }
 
-    pub fn declSrcLoc(ty: Type) Module.SrcLoc {
-        return declSrcLocOrNull(ty).?;
+    pub fn declSrcLoc(ty: Type, mod: *Module) Module.SrcLoc {
+        return declSrcLocOrNull(ty, mod).?;
     }
 
-    pub fn declSrcLocOrNull(ty: Type) ?Module.SrcLoc {
+    pub fn declSrcLocOrNull(ty: Type, mod: *Module) ?Module.SrcLoc {
         switch (ty.tag()) {
             .enum_full, .enum_nonexhaustive => {
                 const enum_full = ty.cast(Payload.EnumFull).?.data;
-                return enum_full.srcLoc();
+                return enum_full.srcLoc(mod);
+            },
+            .enum_numbered => {
+                const enum_numbered = ty.castTag(.enum_numbered).?.data;
+                return enum_numbered.srcLoc(mod);
             },
-            .enum_numbered => return ty.castTag(.enum_numbered).?.data.srcLoc(),
             .enum_simple => {
                 const enum_simple = ty.castTag(.enum_simple).?.data;
-                return enum_simple.srcLoc();
+                return enum_simple.srcLoc(mod);
             },
             .@"struct" => {
                 const struct_obj = ty.castTag(.@"struct").?.data;
-                return struct_obj.srcLoc();
+                return struct_obj.srcLoc(mod);
             },
             .error_set => {
                 const error_set = ty.castTag(.error_set).?.data;
-                return error_set.srcLoc();
+                return error_set.srcLoc(mod);
             },
             .@"union", .union_tagged => {
                 const union_obj = ty.cast(Payload.Union).?.data;
-                return union_obj.srcLoc();
+                return union_obj.srcLoc(mod);
             },
             .atomic_order,
             .atomic_rmw_op,
@@ -5268,7 +5292,7 @@ pub const Type = extern union {
         }
     }
 
-    pub fn getOwnerDecl(ty: Type) *Module.Decl {
+    pub fn getOwnerDecl(ty: Type) Module.Decl.Index {
         switch (ty.tag()) {
             .enum_full, .enum_nonexhaustive => {
                 const enum_full = ty.cast(Payload.EnumFull).?.data;
@@ -5357,30 +5381,30 @@ pub const Type = extern union {
     }
 
     /// Asserts the type is an enum.
-    pub fn enumHasInt(ty: Type, int: Value, target: Target) bool {
+    pub fn enumHasInt(ty: Type, int: Value, mod: *Module) bool {
         const S = struct {
-            fn intInRange(tag_ty: Type, int_val: Value, end: usize, tg: Target) bool {
+            fn intInRange(tag_ty: Type, int_val: Value, end: usize, m: *Module) bool {
                 if (int_val.compareWithZero(.lt)) return false;
                 var end_payload: Value.Payload.U64 = .{
                     .base = .{ .tag = .int_u64 },
                     .data = end,
                 };
                 const end_val = Value.initPayload(&end_payload.base);
-                if (int_val.compare(.gte, end_val, tag_ty, tg)) return false;
+                if (int_val.compare(.gte, end_val, tag_ty, m)) return false;
                 return true;
             }
         };
         switch (ty.tag()) {
-            .enum_nonexhaustive => return int.intFitsInType(ty, target),
+            .enum_nonexhaustive => return int.intFitsInType(ty, mod.getTarget()),
             .enum_full => {
                 const enum_full = ty.castTag(.enum_full).?.data;
                 const tag_ty = enum_full.tag_ty;
                 if (enum_full.values.count() == 0) {
-                    return S.intInRange(tag_ty, int, enum_full.fields.count(), target);
+                    return S.intInRange(tag_ty, int, enum_full.fields.count(), mod);
                 } else {
                     return enum_full.values.containsContext(int, .{
                         .ty = tag_ty,
-                        .target = target,
+                        .mod = mod,
                     });
                 }
             },
@@ -5388,11 +5412,11 @@ pub const Type = extern union {
                 const enum_obj = ty.castTag(.enum_numbered).?.data;
                 const tag_ty = enum_obj.tag_ty;
                 if (enum_obj.values.count() == 0) {
-                    return S.intInRange(tag_ty, int, enum_obj.fields.count(), target);
+                    return S.intInRange(tag_ty, int, enum_obj.fields.count(), mod);
                 } else {
                     return enum_obj.values.containsContext(int, .{
                         .ty = tag_ty,
-                        .target = target,
+                        .mod = mod,
                     });
                 }
             },
@@ -5405,7 +5429,7 @@ pub const Type = extern union {
                     .data = bits,
                 };
                 const tag_ty = Type.initPayload(&buffer.base);
-                return S.intInRange(tag_ty, int, fields_len, target);
+                return S.intInRange(tag_ty, int, fields_len, mod);
             },
             .atomic_order,
             .atomic_rmw_op,
@@ -5937,7 +5961,9 @@ pub const Type = extern union {
     pub const @"anyopaque" = initTag(.anyopaque);
     pub const @"null" = initTag(.@"null");
 
-    pub fn ptr(arena: Allocator, target: Target, data: Payload.Pointer.Data) !Type {
+    pub fn ptr(arena: Allocator, mod: *Module, data: Payload.Pointer.Data) !Type {
+        const target = mod.getTarget();
+
         var d = data;
 
         if (d.size == .C) {
@@ -5967,7 +5993,7 @@ pub const Type = extern union {
             d.bit_offset == 0 and d.host_size == 0 and !d.@"allowzero" and !d.@"volatile")
         {
             if (d.sentinel) |sent| {
-                if (!d.mutable and d.pointee_type.eql(Type.u8, target)) {
+                if (!d.mutable and d.pointee_type.eql(Type.u8, mod)) {
                     switch (d.size) {
                         .Slice => {
                             if (sent.compareWithZero(.eq)) {
@@ -5982,7 +6008,7 @@ pub const Type = extern union {
                         else => {},
                     }
                 }
-            } else if (!d.mutable and d.pointee_type.eql(Type.u8, target)) {
+            } else if (!d.mutable and d.pointee_type.eql(Type.u8, mod)) {
                 switch (d.size) {
                     .Slice => return Type.initTag(.const_slice_u8),
                     .Many => return Type.initTag(.manyptr_const_u8),
@@ -6016,11 +6042,11 @@ pub const Type = extern union {
         len: u64,
         sent: ?Value,
         elem_type: Type,
-        target: Target,
+        mod: *Module,
     ) Allocator.Error!Type {
-        if (elem_type.eql(Type.u8, target)) {
+        if (elem_type.eql(Type.u8, mod)) {
             if (sent) |some| {
-                if (some.eql(Value.zero, elem_type, target)) {
+                if (some.eql(Value.zero, elem_type, mod)) {
                     return Tag.array_u8_sentinel_0.create(arena, len);
                 }
             } else {
@@ -6067,11 +6093,11 @@ pub const Type = extern union {
         arena: Allocator,
         error_set: Type,
         payload: Type,
-        target: Target,
+        mod: *Module,
     ) Allocator.Error!Type {
         assert(error_set.zigTypeTag() == .ErrorSet);
-        if (error_set.eql(Type.@"anyerror", target) and
-            payload.eql(Type.void, target))
+        if (error_set.eql(Type.@"anyerror", mod) and
+            payload.eql(Type.void, mod))
         {
             return Type.initTag(.anyerror_void_error_union);
         }
src/TypedValue.zig
@@ -1,6 +1,7 @@
 const std = @import("std");
 const Type = @import("type.zig").Type;
 const Value = @import("value.zig").Value;
+const Module = @import("Module.zig");
 const Allocator = std.mem.Allocator;
 const TypedValue = @This();
 const Target = std.Target;
@@ -31,13 +32,13 @@ pub fn copy(self: TypedValue, arena: Allocator) error{OutOfMemory}!TypedValue {
     };
 }
 
-pub fn eql(a: TypedValue, b: TypedValue, target: std.Target) bool {
-    if (!a.ty.eql(b.ty, target)) return false;
-    return a.val.eql(b.val, a.ty, target);
+pub fn eql(a: TypedValue, b: TypedValue, mod: *Module) bool {
+    if (!a.ty.eql(b.ty, mod)) return false;
+    return a.val.eql(b.val, a.ty, mod);
 }
 
-pub fn hash(tv: TypedValue, hasher: *std.hash.Wyhash, target: std.Target) void {
-    return tv.val.hash(tv.ty, hasher, target);
+pub fn hash(tv: TypedValue, hasher: *std.hash.Wyhash, mod: *Module) void {
+    return tv.val.hash(tv.ty, hasher, mod);
 }
 
 pub fn enumToInt(tv: TypedValue, buffer: *Value.Payload.U64) Value {
@@ -48,7 +49,7 @@ const max_aggregate_items = 100;
 
 const FormatContext = struct {
     tv: TypedValue,
-    target: Target,
+    mod: *Module,
 };
 
 pub fn format(
@@ -59,7 +60,7 @@ pub fn format(
 ) !void {
     _ = options;
     comptime std.debug.assert(fmt.len == 0);
-    return ctx.tv.print(writer, 3, ctx.target);
+    return ctx.tv.print(writer, 3, ctx.mod);
 }
 
 /// Prints the Value according to the Type, not according to the Value Tag.
@@ -67,8 +68,9 @@ pub fn print(
     tv: TypedValue,
     writer: anytype,
     level: u8,
-    target: std.Target,
+    mod: *Module,
 ) @TypeOf(writer).Error!void {
+    const target = mod.getTarget();
     var val = tv.val;
     var ty = tv.ty;
     while (true) switch (val.tag()) {
@@ -156,7 +158,7 @@ pub fn print(
                     try print(.{
                         .ty = fields[i].ty,
                         .val = vals[i],
-                    }, writer, level - 1, target);
+                    }, writer, level - 1, mod);
                 }
                 return writer.writeAll(" }");
             } else {
@@ -170,7 +172,7 @@ pub fn print(
                     try print(.{
                         .ty = elem_ty,
                         .val = vals[i],
-                    }, writer, level - 1, target);
+                    }, writer, level - 1, mod);
                 }
                 return writer.writeAll(" }");
             }
@@ -185,12 +187,12 @@ pub fn print(
             try print(.{
                 .ty = ty.unionTagType().?,
                 .val = union_val.tag,
-            }, writer, level - 1, target);
+            }, writer, level - 1, mod);
             try writer.writeAll(" = ");
             try print(.{
-                .ty = ty.unionFieldType(union_val.tag, target),
+                .ty = ty.unionFieldType(union_val.tag, mod),
                 .val = union_val.val,
-            }, writer, level - 1, target);
+            }, writer, level - 1, mod);
 
             return writer.writeAll(" }");
         },
@@ -205,7 +207,7 @@ pub fn print(
         },
         .bool_true => return writer.writeAll("true"),
         .bool_false => return writer.writeAll("false"),
-        .ty => return val.castTag(.ty).?.data.print(writer, target),
+        .ty => return val.castTag(.ty).?.data.print(writer, mod),
         .int_type => {
             const int_type = val.castTag(.int_type).?.data;
             return writer.print("{s}{d}", .{
@@ -222,28 +224,32 @@ pub fn print(
             const x = sub_ty.abiAlignment(target);
             return writer.print("{d}", .{x});
         },
-        .function => return writer.print("(function '{s}')", .{val.castTag(.function).?.data.owner_decl.name}),
+        .function => return writer.print("(function '{s}')", .{
+            mod.declPtr(val.castTag(.function).?.data.owner_decl).name,
+        }),
         .extern_fn => return writer.writeAll("(extern function)"),
         .variable => return writer.writeAll("(variable)"),
         .decl_ref_mut => {
-            const decl = val.castTag(.decl_ref_mut).?.data.decl;
+            const decl_index = val.castTag(.decl_ref_mut).?.data.decl_index;
+            const decl = mod.declPtr(decl_index);
             if (level == 0) {
                 return writer.print("(decl ref mut '{s}')", .{decl.name});
             }
             return print(.{
                 .ty = decl.ty,
                 .val = decl.val,
-            }, writer, level - 1, target);
+            }, writer, level - 1, mod);
         },
         .decl_ref => {
-            const decl = val.castTag(.decl_ref).?.data;
+            const decl_index = val.castTag(.decl_ref).?.data;
+            const decl = mod.declPtr(decl_index);
             if (level == 0) {
                 return writer.print("(decl ref '{s}')", .{decl.name});
             }
             return print(.{
                 .ty = decl.ty,
                 .val = decl.val,
-            }, writer, level - 1, target);
+            }, writer, level - 1, mod);
         },
         .elem_ptr => {
             const elem_ptr = val.castTag(.elem_ptr).?.data;
@@ -251,7 +257,7 @@ pub fn print(
             try print(.{
                 .ty = elem_ptr.elem_ty,
                 .val = elem_ptr.array_ptr,
-            }, writer, level - 1, target);
+            }, writer, level - 1, mod);
             return writer.print("[{}]", .{elem_ptr.index});
         },
         .field_ptr => {
@@ -260,7 +266,7 @@ pub fn print(
             try print(.{
                 .ty = field_ptr.container_ty,
                 .val = field_ptr.container_ptr,
-            }, writer, level - 1, target);
+            }, writer, level - 1, mod);
 
             if (field_ptr.container_ty.zigTypeTag() == .Struct) {
                 const field_name = field_ptr.container_ty.structFields().keys()[field_ptr.field_index];
@@ -288,7 +294,7 @@ pub fn print(
             };
             while (i < max_aggregate_items) : (i += 1) {
                 if (i != 0) try writer.writeAll(", ");
-                try print(elem_tv, writer, level - 1, target);
+                try print(elem_tv, writer, level - 1, mod);
             }
             return writer.writeAll(" }");
         },
@@ -300,7 +306,7 @@ pub fn print(
             try print(.{
                 .ty = ty.elemType2(),
                 .val = ty.sentinel().?,
-            }, writer, level - 1, target);
+            }, writer, level - 1, mod);
             return writer.writeAll(" }");
         },
         .slice => return writer.writeAll("(slice)"),
src/value.zig
@@ -731,16 +731,16 @@ pub const Value = extern union {
             .int_i64 => return std.fmt.formatIntValue(val.castTag(.int_i64).?.data, "", options, out_stream),
             .int_big_positive => return out_stream.print("{}", .{val.castTag(.int_big_positive).?.asBigInt()}),
             .int_big_negative => return out_stream.print("{}", .{val.castTag(.int_big_negative).?.asBigInt()}),
-            .function => return out_stream.print("(function '{s}')", .{val.castTag(.function).?.data.owner_decl.name}),
+            .function => return out_stream.print("(function decl={d})", .{val.castTag(.function).?.data.owner_decl}),
             .extern_fn => return out_stream.writeAll("(extern function)"),
             .variable => return out_stream.writeAll("(variable)"),
             .decl_ref_mut => {
-                const decl = val.castTag(.decl_ref_mut).?.data.decl;
-                return out_stream.print("(decl_ref_mut '{s}')", .{decl.name});
+                const decl_index = val.castTag(.decl_ref_mut).?.data.decl_index;
+                return out_stream.print("(decl_ref_mut {d})", .{decl_index});
             },
             .decl_ref => {
-                const decl = val.castTag(.decl_ref).?.data;
-                return out_stream.print("(decl ref '{s}')", .{decl.name});
+                const decl_index = val.castTag(.decl_ref).?.data;
+                return out_stream.print("(decl_ref {d})", .{decl_index});
             },
             .elem_ptr => {
                 const elem_ptr = val.castTag(.elem_ptr).?.data;
@@ -798,16 +798,17 @@ pub const Value = extern union {
         return .{ .data = val };
     }
 
-    pub fn fmtValue(val: Value, ty: Type, target: Target) std.fmt.Formatter(TypedValue.format) {
+    pub fn fmtValue(val: Value, ty: Type, mod: *Module) std.fmt.Formatter(TypedValue.format) {
         return .{ .data = .{
             .tv = .{ .ty = ty, .val = val },
-            .target = target,
+            .mod = mod,
         } };
     }
 
     /// Asserts that the value is representable as an array of bytes.
     /// Copies the value into a freshly allocated slice of memory, which is owned by the caller.
-    pub fn toAllocatedBytes(val: Value, ty: Type, allocator: Allocator, target: Target) ![]u8 {
+    pub fn toAllocatedBytes(val: Value, ty: Type, allocator: Allocator, mod: *Module) ![]u8 {
+        const target = mod.getTarget();
         switch (val.tag()) {
             .bytes => {
                 const bytes = val.castTag(.bytes).?.data;
@@ -823,25 +824,26 @@ pub const Value = extern union {
                 return result;
             },
             .decl_ref => {
-                const decl = val.castTag(.decl_ref).?.data;
+                const decl_index = val.castTag(.decl_ref).?.data;
+                const decl = mod.declPtr(decl_index);
                 const decl_val = try decl.value();
-                return decl_val.toAllocatedBytes(decl.ty, allocator, target);
+                return decl_val.toAllocatedBytes(decl.ty, allocator, mod);
             },
             .the_only_possible_value => return &[_]u8{},
             .slice => {
                 const slice = val.castTag(.slice).?.data;
-                return arrayToAllocatedBytes(slice.ptr, slice.len.toUnsignedInt(target), allocator, target);
+                return arrayToAllocatedBytes(slice.ptr, slice.len.toUnsignedInt(target), allocator, mod);
             },
-            else => return arrayToAllocatedBytes(val, ty.arrayLen(), allocator, target),
+            else => return arrayToAllocatedBytes(val, ty.arrayLen(), allocator, mod),
         }
     }
 
-    fn arrayToAllocatedBytes(val: Value, len: u64, allocator: Allocator, target: Target) ![]u8 {
+    fn arrayToAllocatedBytes(val: Value, len: u64, allocator: Allocator, mod: *Module) ![]u8 {
         const result = try allocator.alloc(u8, @intCast(usize, len));
         var elem_value_buf: ElemValueBuffer = undefined;
         for (result) |*elem, i| {
-            const elem_val = val.elemValueBuffer(i, &elem_value_buf);
-            elem.* = @intCast(u8, elem_val.toUnsignedInt(target));
+            const elem_val = val.elemValueBuffer(mod, i, &elem_value_buf);
+            elem.* = @intCast(u8, elem_val.toUnsignedInt(mod.getTarget()));
         }
         return result;
     }
@@ -1164,7 +1166,7 @@ pub const Value = extern union {
                 var elem_value_buf: ElemValueBuffer = undefined;
                 var buf_off: usize = 0;
                 while (elem_i < len) : (elem_i += 1) {
-                    const elem_val = val.elemValueBuffer(elem_i, &elem_value_buf);
+                    const elem_val = val.elemValueBuffer(mod, elem_i, &elem_value_buf);
                     writeToMemory(elem_val, elem_ty, mod, buffer[buf_off..]);
                     buf_off += elem_size;
                 }
@@ -1975,34 +1977,47 @@ pub const Value = extern union {
 
     /// Asserts the values are comparable. Both operands have type `ty`.
     /// Vector results will be reduced with AND.
-    pub fn compare(lhs: Value, op: std.math.CompareOperator, rhs: Value, ty: Type, target: Target) bool {
+    pub fn compare(lhs: Value, op: std.math.CompareOperator, rhs: Value, ty: Type, mod: *Module) bool {
         if (ty.zigTypeTag() == .Vector) {
             var i: usize = 0;
             while (i < ty.vectorLen()) : (i += 1) {
-                if (!compareScalar(lhs.indexVectorlike(i), op, rhs.indexVectorlike(i), ty.scalarType(), target)) {
+                if (!compareScalar(lhs.indexVectorlike(i), op, rhs.indexVectorlike(i), ty.scalarType(), mod)) {
                     return false;
                 }
             }
             return true;
         }
-        return compareScalar(lhs, op, rhs, ty, target);
+        return compareScalar(lhs, op, rhs, ty, mod);
     }
 
     /// Asserts the values are comparable. Both operands have type `ty`.
-    pub fn compareScalar(lhs: Value, op: std.math.CompareOperator, rhs: Value, ty: Type, target: Target) bool {
+    pub fn compareScalar(
+        lhs: Value,
+        op: std.math.CompareOperator,
+        rhs: Value,
+        ty: Type,
+        mod: *Module,
+    ) bool {
         return switch (op) {
-            .eq => lhs.eql(rhs, ty, target),
-            .neq => !lhs.eql(rhs, ty, target),
-            else => compareHetero(lhs, op, rhs, target),
+            .eq => lhs.eql(rhs, ty, mod),
+            .neq => !lhs.eql(rhs, ty, mod),
+            else => compareHetero(lhs, op, rhs, mod.getTarget()),
         };
     }
 
     /// Asserts the values are comparable vectors of type `ty`.
-    pub fn compareVector(lhs: Value, op: std.math.CompareOperator, rhs: Value, ty: Type, allocator: Allocator, target: Target) !Value {
+    pub fn compareVector(
+        lhs: Value,
+        op: std.math.CompareOperator,
+        rhs: Value,
+        ty: Type,
+        allocator: Allocator,
+        mod: *Module,
+    ) !Value {
         assert(ty.zigTypeTag() == .Vector);
         const result_data = try allocator.alloc(Value, ty.vectorLen());
         for (result_data) |*scalar, i| {
-            const res_bool = compareScalar(lhs.indexVectorlike(i), op, rhs.indexVectorlike(i), ty.scalarType(), target);
+            const res_bool = compareScalar(lhs.indexVectorlike(i), op, rhs.indexVectorlike(i), ty.scalarType(), mod);
             scalar.* = if (res_bool) Value.@"true" else Value.@"false";
         }
         return Value.Tag.aggregate.create(allocator, result_data);
@@ -2032,7 +2047,8 @@ pub const Value = extern union {
     /// for `a`. This function must act *as if* `a` has been coerced to `ty`. This complication
     /// is required in order to make generic function instantiation effecient - specifically
     /// the insertion into the monomorphized function table.
-    pub fn eql(a: Value, b: Value, ty: Type, target: Target) bool {
+    pub fn eql(a: Value, b: Value, ty: Type, mod: *Module) bool {
+        const target = mod.getTarget();
         const a_tag = a.tag();
         const b_tag = b.tag();
         if (a_tag == b_tag) switch (a_tag) {
@@ -2052,31 +2068,31 @@ pub const Value = extern union {
                 const a_payload = a.castTag(.opt_payload).?.data;
                 const b_payload = b.castTag(.opt_payload).?.data;
                 var buffer: Type.Payload.ElemType = undefined;
-                return eql(a_payload, b_payload, ty.optionalChild(&buffer), target);
+                return eql(a_payload, b_payload, ty.optionalChild(&buffer), mod);
             },
             .slice => {
                 const a_payload = a.castTag(.slice).?.data;
                 const b_payload = b.castTag(.slice).?.data;
-                if (!eql(a_payload.len, b_payload.len, Type.usize, target)) return false;
+                if (!eql(a_payload.len, b_payload.len, Type.usize, mod)) return false;
 
                 var ptr_buf: Type.SlicePtrFieldTypeBuffer = undefined;
                 const ptr_ty = ty.slicePtrFieldType(&ptr_buf);
 
-                return eql(a_payload.ptr, b_payload.ptr, ptr_ty, target);
+                return eql(a_payload.ptr, b_payload.ptr, ptr_ty, mod);
             },
             .elem_ptr => {
                 const a_payload = a.castTag(.elem_ptr).?.data;
                 const b_payload = b.castTag(.elem_ptr).?.data;
                 if (a_payload.index != b_payload.index) return false;
 
-                return eql(a_payload.array_ptr, b_payload.array_ptr, ty, target);
+                return eql(a_payload.array_ptr, b_payload.array_ptr, ty, mod);
             },
             .field_ptr => {
                 const a_payload = a.castTag(.field_ptr).?.data;
                 const b_payload = b.castTag(.field_ptr).?.data;
                 if (a_payload.field_index != b_payload.field_index) return false;
 
-                return eql(a_payload.container_ptr, b_payload.container_ptr, ty, target);
+                return eql(a_payload.container_ptr, b_payload.container_ptr, ty, mod);
             },
             .@"error" => {
                 const a_name = a.castTag(.@"error").?.data.name;
@@ -2086,7 +2102,7 @@ pub const Value = extern union {
             .eu_payload => {
                 const a_payload = a.castTag(.eu_payload).?.data;
                 const b_payload = b.castTag(.eu_payload).?.data;
-                return eql(a_payload, b_payload, ty.errorUnionPayload(), target);
+                return eql(a_payload, b_payload, ty.errorUnionPayload(), mod);
             },
             .eu_payload_ptr => @panic("TODO: Implement more pointer eql cases"),
             .opt_payload_ptr => @panic("TODO: Implement more pointer eql cases"),
@@ -2104,7 +2120,7 @@ pub const Value = extern union {
                     const types = ty.tupleFields().types;
                     assert(types.len == a_field_vals.len);
                     for (types) |field_ty, i| {
-                        if (!eql(a_field_vals[i], b_field_vals[i], field_ty, target)) return false;
+                        if (!eql(a_field_vals[i], b_field_vals[i], field_ty, mod)) return false;
                     }
                     return true;
                 }
@@ -2113,7 +2129,7 @@ pub const Value = extern union {
                     const fields = ty.structFields().values();
                     assert(fields.len == a_field_vals.len);
                     for (fields) |field, i| {
-                        if (!eql(a_field_vals[i], b_field_vals[i], field.ty, target)) return false;
+                        if (!eql(a_field_vals[i], b_field_vals[i], field.ty, mod)) return false;
                     }
                     return true;
                 }
@@ -2122,7 +2138,7 @@ pub const Value = extern union {
                 for (a_field_vals) |a_elem, i| {
                     const b_elem = b_field_vals[i];
 
-                    if (!eql(a_elem, b_elem, elem_ty, target)) return false;
+                    if (!eql(a_elem, b_elem, elem_ty, mod)) return false;
                 }
                 return true;
             },
@@ -2132,7 +2148,7 @@ pub const Value = extern union {
                 switch (ty.containerLayout()) {
                     .Packed, .Extern => {
                         const tag_ty = ty.unionTagTypeHypothetical();
-                        if (!a_union.tag.eql(b_union.tag, tag_ty, target)) {
+                        if (!a_union.tag.eql(b_union.tag, tag_ty, mod)) {
                             // In this case, we must disregard mismatching tags and compare
                             // based on the in-memory bytes of the payloads.
                             @panic("TODO comptime comparison of extern union values with mismatching tags");
@@ -2140,13 +2156,13 @@ pub const Value = extern union {
                     },
                     .Auto => {
                         const tag_ty = ty.unionTagTypeHypothetical();
-                        if (!a_union.tag.eql(b_union.tag, tag_ty, target)) {
+                        if (!a_union.tag.eql(b_union.tag, tag_ty, mod)) {
                             return false;
                         }
                     },
                 }
-                const active_field_ty = ty.unionFieldType(a_union.tag, target);
-                return a_union.val.eql(b_union.val, active_field_ty, target);
+                const active_field_ty = ty.unionFieldType(a_union.tag, mod);
+                return a_union.val.eql(b_union.val, active_field_ty, mod);
             },
             else => {},
         } else if (a_tag == .null_value or b_tag == .null_value) {
@@ -2171,7 +2187,7 @@ pub const Value = extern union {
                 var buf_b: ToTypeBuffer = undefined;
                 const a_type = a.toType(&buf_a);
                 const b_type = b.toType(&buf_b);
-                return a_type.eql(b_type, target);
+                return a_type.eql(b_type, mod);
             },
             .Enum => {
                 var buf_a: Payload.U64 = undefined;
@@ -2180,7 +2196,7 @@ pub const Value = extern union {
                 const b_val = b.enumToInt(ty, &buf_b);
                 var buf_ty: Type.Payload.Bits = undefined;
                 const int_ty = ty.intTagType(&buf_ty);
-                return eql(a_val, b_val, int_ty, target);
+                return eql(a_val, b_val, int_ty, mod);
             },
             .Array, .Vector => {
                 const len = ty.arrayLen();
@@ -2189,9 +2205,9 @@ pub const Value = extern union {
                 var a_buf: ElemValueBuffer = undefined;
                 var b_buf: ElemValueBuffer = undefined;
                 while (i < len) : (i += 1) {
-                    const a_elem = elemValueBuffer(a, i, &a_buf);
-                    const b_elem = elemValueBuffer(b, i, &b_buf);
-                    if (!eql(a_elem, b_elem, elem_ty, target)) return false;
+                    const a_elem = elemValueBuffer(a, mod, i, &a_buf);
+                    const b_elem = elemValueBuffer(b, mod, i, &b_buf);
+                    if (!eql(a_elem, b_elem, elem_ty, mod)) return false;
                 }
                 return true;
             },
@@ -2215,7 +2231,7 @@ pub const Value = extern union {
                         .base = .{ .tag = .opt_payload },
                         .data = a,
                     };
-                    return eql(Value.initPayload(&buffer.base), b, ty, target);
+                    return eql(Value.initPayload(&buffer.base), b, ty, mod);
                 }
             },
             else => {},
@@ -2225,7 +2241,7 @@ pub const Value = extern union {
 
     /// This function is used by hash maps and so treats floating-point NaNs as equal
     /// to each other, and not equal to other floating-point values.
-    pub fn hash(val: Value, ty: Type, hasher: *std.hash.Wyhash, target: Target) void {
+    pub fn hash(val: Value, ty: Type, hasher: *std.hash.Wyhash, mod: *Module) void {
         const zig_ty_tag = ty.zigTypeTag();
         std.hash.autoHash(hasher, zig_ty_tag);
         if (val.isUndef()) return;
@@ -2242,7 +2258,7 @@ pub const Value = extern union {
 
             .Type => {
                 var buf: ToTypeBuffer = undefined;
-                return val.toType(&buf).hashWithHasher(hasher, target);
+                return val.toType(&buf).hashWithHasher(hasher, mod);
             },
             .Float, .ComptimeFloat => {
                 // Normalize the float here because this hash must match eql semantics.
@@ -2263,11 +2279,11 @@ pub const Value = extern union {
                     const slice = val.castTag(.slice).?.data;
                     var ptr_buf: Type.SlicePtrFieldTypeBuffer = undefined;
                     const ptr_ty = ty.slicePtrFieldType(&ptr_buf);
-                    hash(slice.ptr, ptr_ty, hasher, target);
-                    hash(slice.len, Type.usize, hasher, target);
+                    hash(slice.ptr, ptr_ty, hasher, mod);
+                    hash(slice.len, Type.usize, hasher, mod);
                 },
 
-                else => return hashPtr(val, hasher, target),
+                else => return hashPtr(val, hasher, mod.getTarget()),
             },
             .Array, .Vector => {
                 const len = ty.arrayLen();
@@ -2275,15 +2291,15 @@ pub const Value = extern union {
                 var index: usize = 0;
                 var elem_value_buf: ElemValueBuffer = undefined;
                 while (index < len) : (index += 1) {
-                    const elem_val = val.elemValueBuffer(index, &elem_value_buf);
-                    elem_val.hash(elem_ty, hasher, target);
+                    const elem_val = val.elemValueBuffer(mod, index, &elem_value_buf);
+                    elem_val.hash(elem_ty, hasher, mod);
                 }
             },
             .Struct => {
                 if (ty.isTupleOrAnonStruct()) {
                     const fields = ty.tupleFields();
                     for (fields.values) |field_val, i| {
-                        field_val.hash(fields.types[i], hasher, target);
+                        field_val.hash(fields.types[i], hasher, mod);
                     }
                     return;
                 }
@@ -2292,13 +2308,13 @@ pub const Value = extern union {
                 switch (val.tag()) {
                     .empty_struct_value => {
                         for (fields) |field| {
-                            field.default_val.hash(field.ty, hasher, target);
+                            field.default_val.hash(field.ty, hasher, mod);
                         }
                     },
                     .aggregate => {
                         const field_values = val.castTag(.aggregate).?.data;
                         for (field_values) |field_val, i| {
-                            field_val.hash(fields[i].ty, hasher, target);
+                            field_val.hash(fields[i].ty, hasher, mod);
                         }
                     },
                     else => unreachable,
@@ -2310,7 +2326,7 @@ pub const Value = extern union {
                     const sub_val = payload.data;
                     var buffer: Type.Payload.ElemType = undefined;
                     const sub_ty = ty.optionalChild(&buffer);
-                    sub_val.hash(sub_ty, hasher, target);
+                    sub_val.hash(sub_ty, hasher, mod);
                 } else {
                     std.hash.autoHash(hasher, false); // non-null
                 }
@@ -2319,14 +2335,14 @@ pub const Value = extern union {
                 if (val.tag() == .@"error") {
                     std.hash.autoHash(hasher, false); // error
                     const sub_ty = ty.errorUnionSet();
-                    val.hash(sub_ty, hasher, target);
+                    val.hash(sub_ty, hasher, mod);
                     return;
                 }
 
                 if (val.castTag(.eu_payload)) |payload| {
                     std.hash.autoHash(hasher, true); // payload
                     const sub_ty = ty.errorUnionPayload();
-                    payload.data.hash(sub_ty, hasher, target);
+                    payload.data.hash(sub_ty, hasher, mod);
                     return;
                 } else unreachable;
             },
@@ -2339,15 +2355,15 @@ pub const Value = extern union {
             .Enum => {
                 var enum_space: Payload.U64 = undefined;
                 const int_val = val.enumToInt(ty, &enum_space);
-                hashInt(int_val, hasher, target);
+                hashInt(int_val, hasher, mod.getTarget());
             },
             .Union => {
                 const union_obj = val.cast(Payload.Union).?.data;
                 if (ty.unionTagType()) |tag_ty| {
-                    union_obj.tag.hash(tag_ty, hasher, target);
+                    union_obj.tag.hash(tag_ty, hasher, mod);
                 }
-                const active_field_ty = ty.unionFieldType(union_obj.tag, target);
-                union_obj.val.hash(active_field_ty, hasher, target);
+                const active_field_ty = ty.unionFieldType(union_obj.tag, mod);
+                union_obj.val.hash(active_field_ty, hasher, mod);
             },
             .Fn => {
                 const func: *Module.Fn = val.castTag(.function).?.data;
@@ -2372,30 +2388,30 @@ pub const Value = extern union {
 
     pub const ArrayHashContext = struct {
         ty: Type,
-        target: Target,
+        mod: *Module,
 
         pub fn hash(self: @This(), val: Value) u32 {
-            const other_context: HashContext = .{ .ty = self.ty, .target = self.target };
+            const other_context: HashContext = .{ .ty = self.ty, .mod = self.mod };
             return @truncate(u32, other_context.hash(val));
         }
         pub fn eql(self: @This(), a: Value, b: Value, b_index: usize) bool {
             _ = b_index;
-            return a.eql(b, self.ty, self.target);
+            return a.eql(b, self.ty, self.mod);
         }
     };
 
     pub const HashContext = struct {
         ty: Type,
-        target: Target,
+        mod: *Module,
 
         pub fn hash(self: @This(), val: Value) u64 {
             var hasher = std.hash.Wyhash.init(0);
-            val.hash(self.ty, &hasher, self.target);
+            val.hash(self.ty, &hasher, self.mod);
             return hasher.final();
         }
 
         pub fn eql(self: @This(), a: Value, b: Value) bool {
-            return a.eql(b, self.ty, self.target);
+            return a.eql(b, self.ty, self.mod);
         }
     };
 
@@ -2434,9 +2450,9 @@ pub const Value = extern union {
     /// Gets the decl referenced by this pointer.  If the pointer does not point
     /// to a decl, or if it points to some part of a decl (like field_ptr or element_ptr),
     /// this function returns null.
-    pub fn pointerDecl(val: Value) ?*Module.Decl {
+    pub fn pointerDecl(val: Value) ?Module.Decl.Index {
         return switch (val.tag()) {
-            .decl_ref_mut => val.castTag(.decl_ref_mut).?.data.decl,
+            .decl_ref_mut => val.castTag(.decl_ref_mut).?.data.decl_index,
             .extern_fn => val.castTag(.extern_fn).?.data.owner_decl,
             .function => val.castTag(.function).?.data.owner_decl,
             .variable => val.castTag(.variable).?.data.owner_decl,
@@ -2462,7 +2478,7 @@ pub const Value = extern union {
             .function,
             .variable,
             => {
-                const decl: *Module.Decl = ptr_val.pointerDecl().?;
+                const decl: Module.Decl.Index = ptr_val.pointerDecl().?;
                 std.hash.autoHash(hasher, decl);
             },
 
@@ -2505,53 +2521,6 @@ pub const Value = extern union {
         }
     }
 
-    pub fn markReferencedDeclsAlive(val: Value) void {
-        switch (val.tag()) {
-            .decl_ref_mut => return val.castTag(.decl_ref_mut).?.data.decl.markAlive(),
-            .extern_fn => return val.castTag(.extern_fn).?.data.owner_decl.markAlive(),
-            .function => return val.castTag(.function).?.data.owner_decl.markAlive(),
-            .variable => return val.castTag(.variable).?.data.owner_decl.markAlive(),
-            .decl_ref => return val.cast(Payload.Decl).?.data.markAlive(),
-
-            .repeated,
-            .eu_payload,
-            .opt_payload,
-            .empty_array_sentinel,
-            => return markReferencedDeclsAlive(val.cast(Payload.SubValue).?.data),
-
-            .eu_payload_ptr,
-            .opt_payload_ptr,
-            => return markReferencedDeclsAlive(val.cast(Payload.PayloadPtr).?.data.container_ptr),
-
-            .slice => {
-                const slice = val.cast(Payload.Slice).?.data;
-                markReferencedDeclsAlive(slice.ptr);
-                markReferencedDeclsAlive(slice.len);
-            },
-
-            .elem_ptr => {
-                const elem_ptr = val.cast(Payload.ElemPtr).?.data;
-                return markReferencedDeclsAlive(elem_ptr.array_ptr);
-            },
-            .field_ptr => {
-                const field_ptr = val.cast(Payload.FieldPtr).?.data;
-                return markReferencedDeclsAlive(field_ptr.container_ptr);
-            },
-            .aggregate => {
-                for (val.castTag(.aggregate).?.data) |field_val| {
-                    markReferencedDeclsAlive(field_val);
-                }
-            },
-            .@"union" => {
-                const data = val.cast(Payload.Union).?.data;
-                markReferencedDeclsAlive(data.tag);
-                markReferencedDeclsAlive(data.val);
-            },
-
-            else => {},
-        }
-    }
-
     pub fn slicePtr(val: Value) Value {
         return switch (val.tag()) {
             .slice => val.castTag(.slice).?.data.ptr,
@@ -2561,11 +2530,12 @@ pub const Value = extern union {
         };
     }
 
-    pub fn sliceLen(val: Value, target: Target) u64 {
+    pub fn sliceLen(val: Value, mod: *Module) u64 {
         return switch (val.tag()) {
-            .slice => val.castTag(.slice).?.data.len.toUnsignedInt(target),
+            .slice => val.castTag(.slice).?.data.len.toUnsignedInt(mod.getTarget()),
             .decl_ref => {
-                const decl = val.castTag(.decl_ref).?.data;
+                const decl_index = val.castTag(.decl_ref).?.data;
+                const decl = mod.declPtr(decl_index);
                 if (decl.ty.zigTypeTag() == .Array) {
                     return decl.ty.arrayLen();
                 } else {
@@ -2599,18 +2569,19 @@ pub const Value = extern union {
 
     /// Asserts the value is a single-item pointer to an array, or an array,
     /// or an unknown-length pointer, and returns the element value at the index.
-    pub fn elemValue(val: Value, arena: Allocator, index: usize) !Value {
-        return elemValueAdvanced(val, index, arena, undefined);
+    pub fn elemValue(val: Value, mod: *Module, arena: Allocator, index: usize) !Value {
+        return elemValueAdvanced(val, mod, index, arena, undefined);
     }
 
     pub const ElemValueBuffer = Payload.U64;
 
-    pub fn elemValueBuffer(val: Value, index: usize, buffer: *ElemValueBuffer) Value {
-        return elemValueAdvanced(val, index, null, buffer) catch unreachable;
+    pub fn elemValueBuffer(val: Value, mod: *Module, index: usize, buffer: *ElemValueBuffer) Value {
+        return elemValueAdvanced(val, mod, index, null, buffer) catch unreachable;
     }
 
     pub fn elemValueAdvanced(
         val: Value,
+        mod: *Module,
         index: usize,
         arena: ?Allocator,
         buffer: *ElemValueBuffer,
@@ -2643,13 +2614,13 @@ pub const Value = extern union {
             .repeated => return val.castTag(.repeated).?.data,
 
             .aggregate => return val.castTag(.aggregate).?.data[index],
-            .slice => return val.castTag(.slice).?.data.ptr.elemValueAdvanced(index, arena, buffer),
+            .slice => return val.castTag(.slice).?.data.ptr.elemValueAdvanced(mod, index, arena, buffer),
 
-            .decl_ref => return val.castTag(.decl_ref).?.data.val.elemValueAdvanced(index, arena, buffer),
-            .decl_ref_mut => return val.castTag(.decl_ref_mut).?.data.decl.val.elemValueAdvanced(index, arena, buffer),
+            .decl_ref => return mod.declPtr(val.castTag(.decl_ref).?.data).val.elemValueAdvanced(mod, index, arena, buffer),
+            .decl_ref_mut => return mod.declPtr(val.castTag(.decl_ref_mut).?.data.decl_index).val.elemValueAdvanced(mod, index, arena, buffer),
             .elem_ptr => {
                 const data = val.castTag(.elem_ptr).?.data;
-                return data.array_ptr.elemValueAdvanced(index + data.index, arena, buffer);
+                return data.array_ptr.elemValueAdvanced(mod, index + data.index, arena, buffer);
             },
 
             // The child type of arrays which have only one possible value need
@@ -2661,18 +2632,24 @@ pub const Value = extern union {
     }
 
     // Asserts that the provided start/end are in-bounds.
-    pub fn sliceArray(val: Value, arena: Allocator, start: usize, end: usize) error{OutOfMemory}!Value {
+    pub fn sliceArray(
+        val: Value,
+        mod: *Module,
+        arena: Allocator,
+        start: usize,
+        end: usize,
+    ) error{OutOfMemory}!Value {
         return switch (val.tag()) {
             .empty_array_sentinel => if (start == 0 and end == 1) val else Value.initTag(.empty_array),
             .bytes => Tag.bytes.create(arena, val.castTag(.bytes).?.data[start..end]),
             .aggregate => Tag.aggregate.create(arena, val.castTag(.aggregate).?.data[start..end]),
-            .slice => sliceArray(val.castTag(.slice).?.data.ptr, arena, start, end),
+            .slice => sliceArray(val.castTag(.slice).?.data.ptr, mod, arena, start, end),
 
-            .decl_ref => sliceArray(val.castTag(.decl_ref).?.data.val, arena, start, end),
-            .decl_ref_mut => sliceArray(val.castTag(.decl_ref_mut).?.data.decl.val, arena, start, end),
+            .decl_ref => sliceArray(mod.declPtr(val.castTag(.decl_ref).?.data).val, mod, arena, start, end),
+            .decl_ref_mut => sliceArray(mod.declPtr(val.castTag(.decl_ref_mut).?.data.decl_index).val, mod, arena, start, end),
             .elem_ptr => blk: {
                 const elem_ptr = val.castTag(.elem_ptr).?.data;
-                break :blk sliceArray(elem_ptr.array_ptr, arena, start + elem_ptr.index, end + elem_ptr.index);
+                break :blk sliceArray(elem_ptr.array_ptr, mod, arena, start + elem_ptr.index, end + elem_ptr.index);
             },
 
             .repeated,
@@ -2718,7 +2695,13 @@ pub const Value = extern union {
     }
 
     /// Returns a pointer to the element value at the index.
-    pub fn elemPtr(val: Value, ty: Type, arena: Allocator, index: usize, target: Target) Allocator.Error!Value {
+    pub fn elemPtr(
+        val: Value,
+        ty: Type,
+        arena: Allocator,
+        index: usize,
+        mod: *Module,
+    ) Allocator.Error!Value {
         const elem_ty = ty.elemType2();
         const ptr_val = switch (val.tag()) {
             .slice => val.castTag(.slice).?.data.ptr,
@@ -2727,7 +2710,7 @@ pub const Value = extern union {
 
         if (ptr_val.tag() == .elem_ptr) {
             const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;
-            if (elem_ptr.elem_ty.eql(elem_ty, target)) {
+            if (elem_ptr.elem_ty.eql(elem_ty, mod)) {
                 return Tag.elem_ptr.create(arena, .{
                     .array_ptr = elem_ptr.array_ptr,
                     .elem_ty = elem_ptr.elem_ty,
@@ -5059,7 +5042,7 @@ pub const Value = extern union {
 
         pub const Decl = struct {
             base: Payload,
-            data: *Module.Decl,
+            data: Module.Decl.Index,
         };
 
         pub const Variable = struct {
@@ -5079,7 +5062,7 @@ pub const Value = extern union {
             data: Data,
 
             pub const Data = struct {
-                decl: *Module.Decl,
+                decl_index: Module.Decl.Index,
                 runtime_index: u32,
             };
         };
@@ -5215,7 +5198,7 @@ pub const Value = extern union {
 
             base: Payload = .{ .tag = base_tag },
             data: struct {
-                decl: *Module.Decl,
+                decl_index: Module.Decl.Index,
                 /// 0 means ABI-aligned.
                 alignment: u16,
             },