Commit c95b1bf2d3

Jacob Young <jacobly0@users.noreply.github.com>
2025-06-07 10:51:26
x86_64: remove air references from mir
1 parent c4ec382
lib/std/zig/Zir.zig
@@ -4861,6 +4861,15 @@ pub fn getParamBody(zir: Zir, fn_inst: Inst.Index) []const Zir.Inst.Index {
     }
 }
 
+pub fn getParamName(zir: Zir, param_inst: Inst.Index) ?NullTerminatedString {
+    const inst = zir.instructions.get(@intFromEnum(param_inst));
+    return switch (inst.tag) {
+        .param, .param_comptime => zir.extraData(Inst.Param, inst.data.pl_tok.payload_index).data.name,
+        .param_anytype, .param_anytype_comptime => inst.data.str_tok.start,
+        else => null,
+    };
+}
+
 pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo {
     const tags = zir.instructions.items(.tag);
     const datas = zir.instructions.items(.data);
src/Air/print.zig
@@ -363,10 +363,7 @@ const Writer = struct {
     fn writeArg(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
         const arg = w.air.instructions.items(.data)[@intFromEnum(inst)].arg;
         try w.writeType(s, arg.ty.toType());
-        switch (arg.name) {
-            .none => {},
-            _ => try s.print(", \"{}\"", .{std.zig.fmtEscapes(arg.name.toSlice(w.air))}),
-        }
+        try s.print(", {d}", .{arg.zir_param_index});
     }
 
     fn writeTyOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
src/arch/aarch64/CodeGen.zig
@@ -4208,15 +4208,22 @@ fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!void {
     while (self.args[arg_index] == .none) arg_index += 1;
     self.arg_index = arg_index + 1;
 
-    const ty = self.typeOfIndex(inst);
-    const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)];
-    const name = self.air.instructions.items(.data)[@intFromEnum(inst)].arg.name;
-    if (name != .none) try self.dbg_info_relocs.append(self.gpa, .{
-        .tag = tag,
-        .ty = ty,
-        .name = name.toSlice(self.air),
-        .mcv = self.args[arg_index],
-    });
+    const zcu = self.pt.zcu;
+    const func_zir = zcu.funcInfo(self.func_index).zir_body_inst.resolveFull(&zcu.intern_pool).?;
+    const file = zcu.fileByIndex(func_zir.file);
+    if (!file.mod.?.strip) {
+        const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)];
+        const arg = self.air.instructions.items(.data)[@intFromEnum(inst)].arg;
+        const ty = self.typeOfIndex(inst);
+        const zir = &file.zir.?;
+        const name = zir.nullTerminatedString(zir.getParamName(zir.getParamBody(func_zir.inst)[arg.zir_param_index]).?);
+        try self.dbg_info_relocs.append(self.gpa, .{
+            .tag = tag,
+            .ty = ty,
+            .name = name,
+            .mcv = self.args[arg_index],
+        });
+    }
 
     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else self.args[arg_index];
     return self.finishAir(inst, result, .{ .none, .none, .none });
src/arch/aarch64/Mir.zig
@@ -514,9 +514,7 @@ pub fn emit(
     func_index: InternPool.Index,
     code: *std.ArrayListUnmanaged(u8),
     debug_output: link.File.DebugInfoOutput,
-    air: *const @import("../../Air.zig"),
 ) codegen.CodeGenError!void {
-    _ = air; // using this would be a bug
     const zcu = pt.zcu;
     const func = zcu.funcInfo(func_index);
     const nav = func.owner_nav;
src/arch/arm/CodeGen.zig
@@ -4191,16 +4191,22 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
     while (self.args[arg_index] == .none) arg_index += 1;
     self.arg_index = arg_index + 1;
 
-    const ty = self.typeOfIndex(inst);
-    const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)];
-
-    const name = self.air.instructions.items(.data)[@intFromEnum(inst)].arg.name;
-    if (name != .none) try self.dbg_info_relocs.append(self.gpa, .{
-        .tag = tag,
-        .ty = ty,
-        .name = name.toSlice(self.air),
-        .mcv = self.args[arg_index],
-    });
+    const zcu = self.pt.zcu;
+    const func_zir = zcu.funcInfo(self.func_index).zir_body_inst.resolveFull(&zcu.intern_pool).?;
+    const file = zcu.fileByIndex(func_zir.file);
+    if (!file.mod.?.strip) {
+        const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)];
+        const arg = self.air.instructions.items(.data)[@intFromEnum(inst)].arg;
+        const ty = self.typeOfIndex(inst);
+        const zir = &file.zir.?;
+        const name = zir.nullTerminatedString(zir.getParamName(zir.getParamBody(func_zir.inst)[arg.zir_param_index]).?);
+        try self.dbg_info_relocs.append(self.gpa, .{
+            .tag = tag,
+            .ty = ty,
+            .name = name,
+            .mcv = self.args[arg_index],
+        });
+    }
 
     const result: MCValue = if (self.liveness.isUnused(inst)) .dead else self.args[arg_index];
     return self.finishAir(inst, result, .{ .none, .none, .none });
src/arch/arm/Mir.zig
@@ -294,9 +294,7 @@ pub fn emit(
     func_index: InternPool.Index,
     code: *std.ArrayListUnmanaged(u8),
     debug_output: link.File.DebugInfoOutput,
-    air: *const @import("../../Air.zig"),
 ) codegen.CodeGenError!void {
-    _ = air; // using this would be a bug
     const zcu = pt.zcu;
     const func = zcu.funcInfo(func_index);
     const nav = func.owner_nav;
src/arch/riscv64/CodeGen.zig
@@ -70,6 +70,7 @@ mod: *Package.Module,
 target: *const std.Target,
 args: []MCValue,
 ret_mcv: InstTracking,
+func_index: InternPool.Index,
 fn_type: Type,
 arg_index: usize,
 src_loc: Zcu.LazySrcLoc,
@@ -774,6 +775,7 @@ pub fn generate(
         .owner = .{ .nav_index = func.owner_nav },
         .args = undefined, // populated after `resolveCallingConventionValues`
         .ret_mcv = undefined, // populated after `resolveCallingConventionValues`
+        .func_index = func_index,
         .fn_type = fn_type,
         .arg_index = 0,
         .branch_stack = &branch_stack,
@@ -877,6 +879,7 @@ pub fn generateLazy(
         .owner = .{ .lazy_sym = lazy_sym },
         .args = undefined, // populated after `resolveCallingConventionValues`
         .ret_mcv = undefined, // populated after `resolveCallingConventionValues`
+        .func_index = undefined,
         .fn_type = undefined,
         .arg_index = 0,
         .branch_stack = undefined,
@@ -4724,10 +4727,8 @@ fn airFieldParentPtr(func: *Func, inst: Air.Inst.Index) !void {
     return func.fail("TODO implement codegen airFieldParentPtr", .{});
 }
 
-fn genArgDbgInfo(func: *const Func, inst: Air.Inst.Index, mcv: MCValue) InnerError!void {
-    const arg = func.air.instructions.items(.data)[@intFromEnum(inst)].arg;
-    const ty = arg.ty.toType();
-    if (arg.name == .none) return;
+fn genArgDbgInfo(func: *const Func, name: []const u8, ty: Type, mcv: MCValue) InnerError!void {
+    assert(!func.mod.strip);
 
     // TODO: Add a pseudo-instruction or something to defer this work until Emit.
     //       We aren't allowed to interact with linker state here.
@@ -4736,7 +4737,7 @@ fn genArgDbgInfo(func: *const Func, inst: Air.Inst.Index, mcv: MCValue) InnerErr
         .dwarf => |dw| switch (mcv) {
             .register => |reg| dw.genLocalDebugInfo(
                 .local_arg,
-                arg.name.toSlice(func.air),
+                name,
                 ty,
                 .{ .reg = reg.dwarfNum() },
             ) catch |err| return func.fail("failed to generate debug info: {s}", .{@errorName(err)}),
@@ -4749,6 +4750,8 @@ fn genArgDbgInfo(func: *const Func, inst: Air.Inst.Index, mcv: MCValue) InnerErr
 }
 
 fn airArg(func: *Func, inst: Air.Inst.Index) InnerError!void {
+    const zcu = func.pt.zcu;
+
     var arg_index = func.arg_index;
 
     // we skip over args that have no bits
@@ -4765,7 +4768,14 @@ fn airArg(func: *Func, inst: Air.Inst.Index) InnerError!void {
 
         try func.genCopy(arg_ty, dst_mcv, src_mcv);
 
-        try func.genArgDbgInfo(inst, src_mcv);
+        const arg = func.air.instructions.items(.data)[@intFromEnum(inst)].arg;
+        // can delete `func.func_index` if this logic is moved to emit
+        const func_zir = zcu.funcInfo(func.func_index).zir_body_inst.resolveFull(&zcu.intern_pool).?;
+        const file = zcu.fileByIndex(func_zir.file);
+        const zir = &file.zir.?;
+        const name = zir.nullTerminatedString(zir.getParamName(zir.getParamBody(func_zir.inst)[arg.zir_param_index]).?);
+
+        try func.genArgDbgInfo(name, arg_ty, src_mcv);
         break :result dst_mcv;
     };
 
src/arch/riscv64/Mir.zig
@@ -117,9 +117,7 @@ pub fn emit(
     func_index: InternPool.Index,
     code: *std.ArrayListUnmanaged(u8),
     debug_output: link.File.DebugInfoOutput,
-    air: *const @import("../../Air.zig"),
 ) codegen.CodeGenError!void {
-    _ = air; // using this would be a bug
     const zcu = pt.zcu;
     const comp = zcu.comp;
     const gpa = comp.gpa;
src/arch/sparc64/CodeGen.zig
@@ -995,23 +995,29 @@ fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!void {
     self.arg_index += 1;
 
     const ty = self.typeOfIndex(inst);
-
-    const arg = self.args[arg_index];
-    const mcv = blk: {
-        switch (arg) {
+    const mcv: MCValue = blk: {
+        switch (self.args[arg_index]) {
             .stack_offset => |off| {
                 const abi_size = math.cast(u32, ty.abiSize(zcu)) orelse {
                     return self.fail("type '{}' too big to fit into stack frame", .{ty.fmt(pt)});
                 };
                 const offset = off + abi_size;
-                break :blk MCValue{ .stack_offset = offset };
+                break :blk .{ .stack_offset = offset };
             },
-            else => break :blk arg,
+            else => |mcv| break :blk mcv,
         }
     };
 
-    self.genArgDbgInfo(inst, mcv) catch |err|
-        return self.fail("failed to generate debug info for parameter: {s}", .{@errorName(err)});
+    const func_zir = zcu.funcInfo(self.func_index).zir_body_inst.resolveFull(&zcu.intern_pool).?;
+    const file = zcu.fileByIndex(func_zir.file);
+    if (!file.mod.?.strip) {
+        const arg = self.air.instructions.items(.data)[@intFromEnum(inst)].arg;
+        const zir = &file.zir.?;
+        const name = zir.nullTerminatedString(zir.getParamName(zir.getParamBody(func_zir.inst)[arg.zir_param_index]).?);
+
+        self.genArgDbgInfo(name, ty, mcv) catch |err|
+            return self.fail("failed to generate debug info for parameter: {s}", .{@errorName(err)});
+    }
 
     if (self.liveness.isUnused(inst))
         return self.finishAirBookkeeping();
@@ -3539,11 +3545,7 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Air.
     self.finishAirBookkeeping();
 }
 
-fn genArgDbgInfo(self: Self, inst: Air.Inst.Index, mcv: MCValue) !void {
-    const arg = self.air.instructions.items(.data)[@intFromEnum(inst)].arg;
-    const ty = arg.ty.toType();
-    if (arg.name == .none) return;
-
+fn genArgDbgInfo(self: Self, name: []const u8, ty: Type, mcv: MCValue) !void {
     // TODO: Add a pseudo-instruction or something to defer this work until Emit.
     //       We aren't allowed to interact with linker state here.
     if (true) return;
@@ -3551,7 +3553,7 @@ fn genArgDbgInfo(self: Self, inst: Air.Inst.Index, mcv: MCValue) !void {
         .dwarf => |dw| switch (mcv) {
             .register => |reg| try dw.genLocalDebugInfo(
                 .local_arg,
-                arg.name.toSlice(self.air),
+                name,
                 ty,
                 .{ .reg = reg.dwarfNum() },
             ),
src/arch/sparc64/Mir.zig
@@ -382,9 +382,7 @@ pub fn emit(
     func_index: InternPool.Index,
     code: *std.ArrayListUnmanaged(u8),
     debug_output: link.File.DebugInfoOutput,
-    air: *const @import("../../Air.zig"),
 ) codegen.CodeGenError!void {
-    _ = air; // using this would be a bug
     const zcu = pt.zcu;
     const func = zcu.funcInfo(func_index);
     const nav = func.owner_nav;
src/arch/wasm/CodeGen.zig
@@ -1877,7 +1877,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
         .dbg_inline_block => cg.airDbgInlineBlock(inst),
         .dbg_var_ptr => cg.airDbgVar(inst, .local_var, true),
         .dbg_var_val => cg.airDbgVar(inst, .local_var, false),
-        .dbg_arg_inline => cg.airDbgVar(inst, .local_arg, false),
+        .dbg_arg_inline => cg.airDbgVar(inst, .arg, false),
 
         .call => cg.airCall(inst, .auto),
         .call_always_tail => cg.airCall(inst, .always_tail),
@@ -6427,7 +6427,7 @@ fn airDbgInlineBlock(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
 fn airDbgVar(
     cg: *CodeGen,
     inst: Air.Inst.Index,
-    local_tag: link.File.Dwarf.WipNav.LocalTag,
+    local_tag: link.File.Dwarf.WipNav.LocalVarTag,
     is_ptr: bool,
 ) InnerError!void {
     _ = is_ptr;
src/arch/x86_64/CodeGen.zig
@@ -129,7 +129,6 @@ target: *const std.Target,
 owner: Owner,
 inline_func: InternPool.Index,
 mod: *Module,
-arg_index: u32,
 args: []MCValue,
 va_info: union {
     sysv: struct {
@@ -151,6 +150,8 @@ eflags_inst: ?Air.Inst.Index = null,
 mir_instructions: std.MultiArrayList(Mir.Inst) = .empty,
 /// MIR extra data
 mir_extra: std.ArrayListUnmanaged(u32) = .empty,
+mir_local_name_bytes: std.ArrayListUnmanaged(u8) = .empty,
+mir_local_types: std.ArrayListUnmanaged(InternPool.Index) = .empty,
 mir_table: std.ArrayListUnmanaged(Mir.Inst.Index) = .empty,
 
 /// The value is an offset into the `Function` `code` from the beginning.
@@ -978,8 +979,10 @@ pub fn generate(
     const gpa = zcu.gpa;
     const ip = &zcu.intern_pool;
     const func = zcu.funcInfo(func_index);
+    const func_zir = func.zir_body_inst.resolveFull(ip).?;
+    const file = zcu.fileByIndex(func_zir.file);
     const fn_type: Type = .fromInterned(func.ty);
-    const mod = zcu.navFileScope(func.owner_nav).mod.?;
+    const mod = file.mod.?;
 
     var function: CodeGen = .{
         .gpa = gpa,
@@ -991,7 +994,6 @@ pub fn generate(
         .bin_file = bin_file,
         .owner = .{ .nav_index = func.owner_nav },
         .inline_func = func_index,
-        .arg_index = undefined,
         .args = undefined, // populated after `resolveCallingConventionValues`
         .va_info = undefined, // populated after `resolveCallingConventionValues`
         .ret_mcv = undefined, // populated after `resolveCallingConventionValues`
@@ -1011,6 +1013,8 @@ pub fn generate(
         function.inst_tracking.deinit(gpa);
         function.epilogue_relocs.deinit(gpa);
         function.mir_instructions.deinit(gpa);
+        function.mir_local_name_bytes.deinit(gpa);
+        function.mir_local_types.deinit(gpa);
         function.mir_extra.deinit(gpa);
         function.mir_table.deinit(gpa);
     }
@@ -1078,7 +1082,7 @@ pub fn generate(
         );
     }
 
-    function.gen() catch |err| switch (err) {
+    function.gen(&file.zir.?, func_zir.inst, func.comptime_args, call_info.air_arg_count) catch |err| switch (err) {
         error.CodegenFail => return error.CodegenFail,
         error.OutOfRegisters => return function.fail("ran out of registers (Zig compiler bug)", .{}),
         else => |e| return e,
@@ -1097,17 +1101,32 @@ pub fn generate(
     var mir: Mir = .{
         .instructions = .empty,
         .extra = &.{},
+        .local_name_bytes = &.{},
+        .local_types = &.{},
         .table = &.{},
         .frame_locs = .empty,
     };
     errdefer mir.deinit(gpa);
     mir.instructions = function.mir_instructions.toOwnedSlice();
     mir.extra = try function.mir_extra.toOwnedSlice(gpa);
+    mir.local_name_bytes = try function.mir_local_name_bytes.toOwnedSlice(gpa);
+    mir.local_types = try function.mir_local_types.toOwnedSlice(gpa);
     mir.table = try function.mir_table.toOwnedSlice(gpa);
     mir.frame_locs = function.frame_locs.toOwnedSlice();
     return mir;
 }
 
+pub fn toTmpMir(cg: *CodeGen) Mir {
+    return .{
+        .instructions = cg.mir_instructions.slice(),
+        .extra = cg.mir_extra.items,
+        .local_name_bytes = cg.mir_local_name_bytes.items,
+        .local_types = cg.mir_local_types.items,
+        .table = cg.mir_table.items,
+        .frame_locs = cg.frame_locs.slice(),
+    };
+}
+
 pub fn generateLazy(
     bin_file: *link.File,
     pt: Zcu.PerThread,
@@ -1130,7 +1149,6 @@ pub fn generateLazy(
         .bin_file = bin_file,
         .owner = .{ .lazy_sym = lazy_sym },
         .inline_func = undefined,
-        .arg_index = undefined,
         .args = undefined,
         .va_info = undefined,
         .ret_mcv = undefined,
@@ -1141,6 +1159,8 @@ pub fn generateLazy(
     defer {
         function.inst_tracking.deinit(gpa);
         function.mir_instructions.deinit(gpa);
+        function.mir_local_name_bytes.deinit(gpa);
+        function.mir_local_types.deinit(gpa);
         function.mir_extra.deinit(gpa);
         function.mir_table.deinit(gpa);
     }
@@ -1156,21 +1176,12 @@ pub fn generateLazy(
         else => |e| return e,
     };
 
-    var mir: Mir = .{
-        .instructions = function.mir_instructions.toOwnedSlice(),
-        .extra = try function.mir_extra.toOwnedSlice(gpa),
-        .table = try function.mir_table.toOwnedSlice(gpa),
-        .frame_locs = function.frame_locs.toOwnedSlice(),
-    };
-    defer mir.deinit(gpa);
-
     var emit: Emit = .{
-        .air = function.air,
         .lower = .{
             .bin_file = bin_file,
             .target = function.target,
             .allocator = gpa,
-            .mir = mir,
+            .mir = function.toTmpMir(),
             .cc = .auto,
             .src_loc = src_loc,
             .output_mode = comp.config.output_mode,
@@ -1240,22 +1251,16 @@ fn formatWipMir(
     writer: anytype,
 ) @TypeOf(writer).Error!void {
     const comp = data.self.bin_file.comp;
-    const mod = comp.root_mod;
     var lower: Lower = .{
         .bin_file = data.self.bin_file,
         .target = data.self.target,
         .allocator = data.self.gpa,
-        .mir = .{
-            .instructions = data.self.mir_instructions.slice(),
-            .extra = data.self.mir_extra.items,
-            .table = data.self.mir_table.items,
-            .frame_locs = (std.MultiArrayList(Mir.FrameLoc){}).slice(),
-        },
+        .mir = data.self.toTmpMir(),
         .cc = .auto,
         .src_loc = data.self.src_loc,
         .output_mode = comp.config.output_mode,
         .link_mode = comp.config.link_mode,
-        .pic = mod.pic,
+        .pic = data.self.mod.pic,
     };
     var first = true;
     for ((lower.lowerMir(data.inst) catch |err| switch (err) {
@@ -1291,7 +1296,9 @@ fn formatWipMir(
             .pseudo_dbg_epilogue_begin_none,
             .pseudo_dbg_enter_block_none,
             .pseudo_dbg_leave_block_none,
+            .pseudo_dbg_arg_none,
             .pseudo_dbg_var_args_none,
+            .pseudo_dbg_var_none,
             .pseudo_dead_none,
             => {},
             .pseudo_dbg_line_stmt_line_column, .pseudo_dbg_line_line_column => try writer.print(
@@ -1299,57 +1306,47 @@ fn formatWipMir(
                 mir_inst.data.line_column,
             ),
             .pseudo_dbg_enter_inline_func, .pseudo_dbg_leave_inline_func => try writer.print(" {}", .{
-                ip.getNav(ip.indexToKey(mir_inst.data.func).func.owner_nav).name.fmt(ip),
+                ip.getNav(ip.indexToKey(mir_inst.data.ip_index).func.owner_nav).name.fmt(ip),
             }),
-            .pseudo_dbg_local_a => try writer.print(" {}", .{mir_inst.data.a.air_inst}),
-            .pseudo_dbg_local_ai_s => try writer.print(" {}, {d}", .{
-                mir_inst.data.ai.air_inst,
-                @as(i32, @bitCast(mir_inst.data.ai.i)),
+            .pseudo_dbg_arg_i_s, .pseudo_dbg_var_i_s => try writer.print(" {d}", .{
+                @as(i32, @bitCast(mir_inst.data.i.i)),
             }),
-            .pseudo_dbg_local_ai_u => try writer.print(" {}, {d}", .{
-                mir_inst.data.ai.air_inst,
-                mir_inst.data.ai.i,
+            .pseudo_dbg_arg_i_u, .pseudo_dbg_var_i_u => try writer.print(" {d}", .{
+                mir_inst.data.i.i,
             }),
-            .pseudo_dbg_local_ai_64 => try writer.print(" {}, {d}", .{
-                mir_inst.data.ai.air_inst,
-                lower.mir.extraData(Mir.Imm64, mir_inst.data.ai.i).data.decode(),
+            .pseudo_dbg_arg_i_64, .pseudo_dbg_var_i_64 => try writer.print(" {d}", .{
+                mir_inst.data.i64,
             }),
-            .pseudo_dbg_local_as => {
+            .pseudo_dbg_arg_reloc, .pseudo_dbg_var_reloc => {
                 const mem_op: encoder.Instruction.Operand = .{ .mem = .initSib(.qword, .{
-                    .base = .{ .reloc = mir_inst.data.as.sym_index },
+                    .base = .{ .reloc = mir_inst.data.reloc.sym_index },
+                    .disp = mir_inst.data.reloc.off,
                 }) };
-                try writer.print(" {}, {}", .{ mir_inst.data.as.air_inst, mem_op.fmt(.m) });
+                try writer.print(" {}", .{mem_op.fmt(.m)});
             },
-            .pseudo_dbg_local_aso => {
-                const sym_off = lower.mir.extraData(bits.SymbolOffset, mir_inst.data.ax.payload).data;
+            .pseudo_dbg_arg_ro, .pseudo_dbg_var_ro => {
                 const mem_op: encoder.Instruction.Operand = .{ .mem = .initSib(.qword, .{
-                    .base = .{ .reloc = sym_off.sym_index },
-                    .disp = sym_off.off,
+                    .base = .{ .reg = mir_inst.data.ro.reg },
+                    .disp = mir_inst.data.ro.off,
                 }) };
-                try writer.print(" {}, {}", .{ mir_inst.data.ax.air_inst, mem_op.fmt(.m) });
+                try writer.print(" {}", .{mem_op.fmt(.m)});
             },
-            .pseudo_dbg_local_aro => {
-                const air_off = lower.mir.extraData(Mir.AirOffset, mir_inst.data.rx.payload).data;
+            .pseudo_dbg_arg_fa, .pseudo_dbg_var_fa => {
                 const mem_op: encoder.Instruction.Operand = .{ .mem = .initSib(.qword, .{
-                    .base = .{ .reg = mir_inst.data.rx.r1 },
-                    .disp = air_off.off,
+                    .base = .{ .frame = mir_inst.data.fa.index },
+                    .disp = mir_inst.data.fa.off,
                 }) };
-                try writer.print(" {}, {}", .{ air_off.air_inst, mem_op.fmt(.m) });
+                try writer.print(" {}", .{mem_op.fmt(.m)});
             },
-            .pseudo_dbg_local_af => {
-                const frame_addr = lower.mir.extraData(bits.FrameAddr, mir_inst.data.ax.payload).data;
-                const mem_op: encoder.Instruction.Operand = .{ .mem = .initSib(.qword, .{
-                    .base = .{ .frame = frame_addr.index },
-                    .disp = frame_addr.off,
-                }) };
-                try writer.print(" {}, {}", .{ mir_inst.data.ax.air_inst, mem_op.fmt(.m) });
-            },
-            .pseudo_dbg_local_am => {
+            .pseudo_dbg_arg_m, .pseudo_dbg_var_m => {
                 const mem_op: encoder.Instruction.Operand = .{
-                    .mem = lower.mir.extraData(Mir.Memory, mir_inst.data.ax.payload).data.decode(),
+                    .mem = lower.mir.extraData(Mir.Memory, mir_inst.data.x.payload).data.decode(),
                 };
-                try writer.print(" {}, {}", .{ mir_inst.data.ax.air_inst, mem_op.fmt(.m) });
+                try writer.print(" {}", .{mem_op.fmt(.m)});
             },
+            .pseudo_dbg_arg_val, .pseudo_dbg_var_val => try writer.print(" {}", .{
+                Value.fromInterned(mir_inst.data.ip_index).fmtValue(data.self.pt),
+            }),
         }
     }
 }
@@ -1640,124 +1637,6 @@ fn asmPlaceholder(self: *CodeGen) !Mir.Inst.Index {
     });
 }
 
-const MirTagAir = enum { dbg_local };
-
-fn asmAir(self: *CodeGen, tag: MirTagAir, inst: Air.Inst.Index) !void {
-    _ = try self.addInst(.{
-        .tag = .pseudo,
-        .ops = switch (tag) {
-            .dbg_local => .pseudo_dbg_local_a,
-        },
-        .data = .{ .a = .{ .air_inst = inst } },
-    });
-}
-
-fn asmAirImmediate(self: *CodeGen, tag: MirTagAir, inst: Air.Inst.Index, imm: Immediate) !void {
-    switch (imm) {
-        .signed => |s| _ = try self.addInst(.{
-            .tag = .pseudo,
-            .ops = switch (tag) {
-                .dbg_local => .pseudo_dbg_local_ai_s,
-            },
-            .data = .{ .ai = .{
-                .air_inst = inst,
-                .i = @bitCast(s),
-            } },
-        }),
-        .unsigned => |u| _ = if (std.math.cast(u32, u)) |small| try self.addInst(.{
-            .tag = .pseudo,
-            .ops = switch (tag) {
-                .dbg_local => .pseudo_dbg_local_ai_u,
-            },
-            .data = .{ .ai = .{
-                .air_inst = inst,
-                .i = small,
-            } },
-        }) else try self.addInst(.{
-            .tag = .pseudo,
-            .ops = switch (tag) {
-                .dbg_local => .pseudo_dbg_local_ai_64,
-            },
-            .data = .{ .ai = .{
-                .air_inst = inst,
-                .i = try self.addExtra(Mir.Imm64.encode(u)),
-            } },
-        }),
-        .reloc => |sym_off| _ = if (sym_off.off == 0) try self.addInst(.{
-            .tag = .pseudo,
-            .ops = switch (tag) {
-                .dbg_local => .pseudo_dbg_local_as,
-            },
-            .data = .{ .as = .{
-                .air_inst = inst,
-                .sym_index = sym_off.sym_index,
-            } },
-        }) else try self.addInst(.{
-            .tag = .pseudo,
-            .ops = switch (tag) {
-                .dbg_local => .pseudo_dbg_local_aso,
-            },
-            .data = .{ .ax = .{
-                .air_inst = inst,
-                .payload = try self.addExtra(sym_off),
-            } },
-        }),
-    }
-}
-
-fn asmAirRegisterImmediate(
-    self: *CodeGen,
-    tag: MirTagAir,
-    inst: Air.Inst.Index,
-    reg: Register,
-    imm: Immediate,
-) !void {
-    _ = try self.addInst(.{
-        .tag = .pseudo,
-        .ops = switch (tag) {
-            .dbg_local => .pseudo_dbg_local_aro,
-        },
-        .data = .{ .rx = .{
-            .r1 = reg,
-            .payload = try self.addExtra(Mir.AirOffset{
-                .air_inst = inst,
-                .off = imm.signed,
-            }),
-        } },
-    });
-}
-
-fn asmAirFrameAddress(
-    self: *CodeGen,
-    tag: MirTagAir,
-    inst: Air.Inst.Index,
-    frame_addr: bits.FrameAddr,
-) !void {
-    _ = try self.addInst(.{
-        .tag = .pseudo,
-        .ops = switch (tag) {
-            .dbg_local => .pseudo_dbg_local_af,
-        },
-        .data = .{ .ax = .{
-            .air_inst = inst,
-            .payload = try self.addExtra(frame_addr),
-        } },
-    });
-}
-
-fn asmAirMemory(self: *CodeGen, tag: MirTagAir, inst: Air.Inst.Index, m: Memory) !void {
-    _ = try self.addInst(.{
-        .tag = .pseudo,
-        .ops = switch (tag) {
-            .dbg_local => .pseudo_dbg_local_am,
-        },
-        .data = .{ .ax = .{
-            .air_inst = inst,
-            .payload = try self.addExtra(Mir.Memory.encode(m)),
-        } },
-    });
-}
-
 fn asmOpOnly(self: *CodeGen, tag: Mir.Inst.FixedTag) !void {
     _ = try self.addInst(.{
         .tag = tag[1],
@@ -2233,7 +2112,13 @@ fn asmMemoryRegisterImmediate(
     });
 }
 
-fn gen(self: *CodeGen) InnerError!void {
+fn gen(
+    self: *CodeGen,
+    zir: *const std.zig.Zir,
+    func_zir_inst: std.zig.Zir.Inst.Index,
+    comptime_args: InternPool.Index.Slice,
+    air_arg_count: u32,
+) InnerError!void {
     const pt = self.pt;
     const zcu = pt.zcu;
     const fn_info = zcu.typeToFunc(self.fn_type).?;
@@ -2303,7 +2188,7 @@ fn gen(self: *CodeGen) InnerError!void {
 
         if (!self.mod.strip) try self.asmPseudo(.pseudo_dbg_prologue_end_none);
 
-        try self.genBody(self.air.getMainBody());
+        try self.genMainBody(zir, func_zir_inst, comptime_args, air_arg_count);
 
         const epilogue = if (self.epilogue_relocs.items.len > 0) epilogue: {
             var last_inst: Mir.Inst.Index = @intCast(self.mir_instructions.len - 1);
@@ -2438,20 +2323,81 @@ fn gen(self: *CodeGen) InnerError!void {
         }
     } else {
         if (!self.mod.strip) try self.asmPseudo(.pseudo_dbg_prologue_end_none);
-        try self.genBody(self.air.getMainBody());
+        try self.genMainBody(zir, func_zir_inst, comptime_args, air_arg_count);
         if (!self.mod.strip) try self.asmPseudo(.pseudo_dbg_epilogue_begin_none);
     }
 }
 
-fn checkInvariantsAfterAirInst(self: *CodeGen) void {
-    assert(!self.register_manager.lockedRegsExist());
+fn genMainBody(
+    cg: *CodeGen,
+    zir: *const std.zig.Zir,
+    func_zir_inst: std.zig.Zir.Inst.Index,
+    comptime_args: InternPool.Index.Slice,
+    air_arg_count: u32,
+) InnerError!void {
+    const pt = cg.pt;
+    const zcu = pt.zcu;
+    const ip = &zcu.intern_pool;
+
+    const main_body = cg.air.getMainBody();
+    const air_args_body = main_body[0..air_arg_count];
+    try cg.genBody(air_args_body);
+
+    if (!cg.mod.strip) {
+        var air_arg_index: usize = 0;
+        const fn_info = zcu.typeToFunc(cg.fn_type).?;
+        var fn_param_index: usize = 0;
+        try cg.mir_local_types.ensureTotalCapacity(cg.gpa, fn_info.param_types.len);
+        var zir_param_index: usize = 0;
+        for (zir.getParamBody(func_zir_inst)) |zir_param_inst| {
+            const name = zir.nullTerminatedString(zir.getParamName(zir_param_inst) orelse continue);
+            defer zir_param_index += 1;
+            try cg.mir_local_name_bytes.appendSlice(cg.gpa, name[0 .. name.len + 1]);
+
+            if (comptime_args.len > 0) switch (comptime_args.get(ip)[zir_param_index]) {
+                .none => {},
+                else => |comptime_arg| {
+                    _ = try cg.addInst(.{
+                        .tag = .pseudo,
+                        .ops = .pseudo_dbg_arg_val,
+                        .data = .{ .ip_index = comptime_arg },
+                    });
+                    continue;
+                },
+            };
+
+            const arg_ty: Type = .fromInterned(fn_info.param_types.get(ip)[fn_param_index]);
+            fn_param_index += 1;
+            cg.mir_local_types.appendAssumeCapacity(arg_ty.toIntern());
+
+            if (air_arg_index == air_args_body.len) {
+                try cg.asmPseudo(.pseudo_dbg_arg_none);
+                continue;
+            }
+            const air_arg_inst = air_args_body[air_arg_index];
+            const air_arg_data = cg.air.instructions.items(.data)[air_arg_index].arg;
+            if (air_arg_data.zir_param_index != zir_param_index) {
+                try cg.asmPseudo(.pseudo_dbg_arg_none);
+                continue;
+            }
+            air_arg_index += 1;
+            try cg.genLocalDebugInfo(.arg, arg_ty, cg.getResolvedInstValue(air_arg_inst).short);
+        }
+        if (fn_info.is_var_args) try cg.asmPseudo(.pseudo_dbg_var_args_none);
+    }
+
+    try cg.genBody(main_body[air_arg_count..]);
+}
+
+fn checkInvariantsAfterAirInst(cg: *CodeGen) void {
+    assert(!cg.register_manager.lockedRegsExist());
 
     if (std.debug.runtime_safety) {
         // check consistency of tracked registers
-        var it = self.register_manager.free_registers.iterator(.{ .kind = .unset });
+        var it = cg.register_manager.free_registers.iterator(.{ .kind = .unset });
         while (it.next()) |index| {
-            const tracked_inst = self.register_manager.registers[index];
-            const tracking = self.getResolvedInstValue(tracked_inst);
+            const tracked_inst = cg.register_manager.registers[index];
+            const tracking = cg.getResolvedInstValue(tracked_inst);
             for (tracking.getRegs()) |reg| {
                 if (RegisterManager.indexOfRegIntoTracked(reg).? == index) break;
             } else unreachable; // tracked register not in use
@@ -2459,10 +2405,10 @@ fn checkInvariantsAfterAirInst(self: *CodeGen) void {
     }
 }
 
-fn genBodyBlock(self: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
-    if (!self.mod.strip) try self.asmPseudo(.pseudo_dbg_enter_block_none);
-    try self.genBody(body);
-    if (!self.mod.strip) try self.asmPseudo(.pseudo_dbg_leave_block_none);
+fn genBodyBlock(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
+    if (!cg.mod.strip) try cg.asmPseudo(.pseudo_dbg_enter_block_none);
+    try cg.genBody(body);
+    if (!cg.mod.strip) try cg.asmPseudo(.pseudo_dbg_leave_block_none);
 }
 
 fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
@@ -2474,25 +2420,6 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
     const air_datas = cg.air.instructions.items(.data);
     const use_old = cg.target.ofmt == .coff;
 
-    cg.arg_index = 0;
-    for (body) |inst| switch (air_tags[@intFromEnum(inst)]) {
-        .arg => {
-            wip_mir_log.debug("{}", .{cg.fmtAir(inst)});
-            verbose_tracking_log.debug("{}", .{cg.fmtTracking()});
-
-            cg.reused_operands = .initEmpty();
-            try cg.inst_tracking.ensureUnusedCapacity(cg.gpa, 1);
-
-            try cg.airArg(inst);
-
-            try cg.resetTemps(@enumFromInt(0));
-            cg.checkInvariantsAfterAirInst();
-        },
-        else => break,
-    };
-
-    if (cg.arg_index == 0) try cg.airDbgVarArgs();
-    cg.arg_index = 0;
     for (body) |inst| {
         if (cg.liveness.isUnused(inst) and !cg.air.mustLower(inst, ip)) continue;
         wip_mir_log.debug("{}", .{cg.fmtAir(inst)});
@@ -2506,20 +2433,7 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
             .shuffle_one, .shuffle_two => @panic("x86_64 TODO: shuffle_one/shuffle_two"),
             // zig fmt: on
 
-            .arg => if (!cg.mod.strip) {
-                // skip zero-bit arguments as they don't have a corresponding arg instruction
-                var arg_index = cg.arg_index;
-                while (cg.args[arg_index] == .none) arg_index += 1;
-                cg.arg_index = arg_index + 1;
-
-                const name = air_datas[@intFromEnum(inst)].arg.name;
-                if (name != .none) try cg.genLocalDebugInfo(inst, cg.getResolvedInstValue(inst).short);
-                if (cg.liveness.isUnused(inst)) try cg.processDeath(inst);
-
-                for (cg.args[arg_index + 1 ..]) |arg| {
-                    if (arg != .none) break;
-                } else try cg.airDbgVarArgs();
-            },
+            .arg => try cg.airArg(inst),
             .add, .add_optimized, .add_wrap => |air_tag| if (use_old) try cg.airBinOp(inst, switch (air_tag) {
                 else => unreachable,
                 .add, .add_optimized => .add,
@@ -85181,19 +85095,19 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
                 if (!cg.mod.strip) _ = try cg.addInst(.{
                     .tag = .pseudo,
                     .ops = .pseudo_dbg_enter_inline_func,
-                    .data = .{ .func = dbg_inline_block.data.func },
+                    .data = .{ .ip_index = dbg_inline_block.data.func },
                 });
                 try cg.lowerBlock(inst, @ptrCast(cg.air.extra.items[dbg_inline_block.end..][0..dbg_inline_block.data.body_len]));
                 if (!cg.mod.strip) _ = try cg.addInst(.{
                     .tag = .pseudo,
                     .ops = .pseudo_dbg_leave_inline_func,
-                    .data = .{ .func = old_inline_func },
+                    .data = .{ .ip_index = old_inline_func },
                 });
             },
             .dbg_var_ptr,
             .dbg_var_val,
             .dbg_arg_inline,
-            => if (use_old) try cg.airDbgVar(inst) else if (!cg.mod.strip) {
+            => |air_tag| if (use_old) try cg.airDbgVar(inst) else if (!cg.mod.strip) {
                 const pl_op = air_datas[@intFromEnum(inst)].pl_op;
                 var ops = try cg.tempsFromOperands(inst, .{pl_op.operand});
                 var mcv = ops[0].tracking(cg).short;
@@ -85209,7 +85123,16 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
                         },
                     },
                 }
-                try cg.genLocalDebugInfo(inst, ops[0].tracking(cg).short);
+
+                const name_nts: Air.NullTerminatedString = @enumFromInt(pl_op.payload);
+                assert(name_nts != .none);
+                const name = name_nts.toSlice(cg.air);
+                try cg.mir_local_name_bytes.appendSlice(cg.gpa, name[0 .. name.len + 1]);
+
+                const ty = cg.typeOf(pl_op.operand);
+                try cg.mir_local_types.append(cg.gpa, ty.toIntern());
+
+                try cg.genLocalDebugInfo(air_tag, ty, ops[0].tracking(cg).short);
                 try ops[0].die(cg);
             },
             .is_null => if (use_old) try cg.airIsNull(inst) else {
@@ -173321,16 +173244,14 @@ fn genIntMulComplexOpMir(self: *CodeGen, dst_ty: Type, dst_mcv: MCValue, src_mcv
 }
 
 fn airArg(self: *CodeGen, inst: Air.Inst.Index) !void {
-    const pt = self.pt;
-    const zcu = pt.zcu;
-    // skip zero-bit arguments as they don't have a corresponding arg instruction
-    var arg_index = self.arg_index;
-    while (self.args[arg_index] == .none) arg_index += 1;
-    self.arg_index = arg_index + 1;
-
+    const zcu = self.pt.zcu;
+    const arg_index = for (self.args, 0..) |arg, arg_index| {
+        if (arg != .none) break arg_index;
+    } else unreachable;
+    const src_mcv = self.args[arg_index];
+    self.args = self.args[arg_index + 1 ..];
     const result: MCValue = if (self.mod.strip and self.liveness.isUnused(inst)) .unreach else result: {
         const arg_ty = self.typeOfIndex(inst);
-        const src_mcv = self.args[arg_index];
         switch (src_mcv) {
             .register, .register_pair, .load_frame => {
                 for (src_mcv.getRegs()) |reg| self.register_manager.getRegAssumeFree(reg, inst);
@@ -173429,68 +173350,108 @@ fn airArg(self: *CodeGen, inst: Air.Inst.Index) !void {
     return self.finishAir(inst, result, .{ .none, .none, .none });
 }
 
-fn airDbgVarArgs(self: *CodeGen) !void {
-    if (self.mod.strip) return;
-    if (!self.pt.zcu.typeToFunc(self.fn_type).?.is_var_args) return;
-    try self.asmPseudo(.pseudo_dbg_var_args_none);
-}
-
-fn genLocalDebugInfo(
-    self: *CodeGen,
-    inst: Air.Inst.Index,
-    mcv: MCValue,
-) !void {
-    if (self.mod.strip) return;
-    switch (self.air.instructions.items(.tag)[@intFromEnum(inst)]) {
+fn genLocalDebugInfo(cg: *CodeGen, air_tag: Air.Inst.Tag, ty: Type, mcv: MCValue) !void {
+    assert(!cg.mod.strip);
+    _ = switch (air_tag) {
         else => unreachable,
-        .arg, .dbg_arg_inline, .dbg_var_val => |tag| {
-            switch (mcv) {
-                .none => try self.asmAir(.dbg_local, inst),
-                .unreach, .dead, .elementwise_args, .reserved_frame, .air_ref => unreachable,
-                .immediate => |imm| try self.asmAirImmediate(.dbg_local, inst, .u(imm)),
-                .lea_frame => |frame_addr| try self.asmAirFrameAddress(.dbg_local, inst, frame_addr),
-                .lea_symbol => |sym_off| try self.asmAirImmediate(.dbg_local, inst, .rel(sym_off)),
-                else => {
-                    const ty = switch (tag) {
-                        else => unreachable,
-                        .arg => self.typeOfIndex(inst),
-                        .dbg_arg_inline, .dbg_var_val => self.typeOf(
-                            self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op.operand,
-                        ),
-                    };
-                    const frame_index = try self.allocFrameIndex(.initSpill(ty, self.pt.zcu));
-                    try self.genSetMem(.{ .frame = frame_index }, 0, ty, mcv, .{});
-                    try self.asmAirMemory(.dbg_local, inst, .{
-                        .base = .{ .frame = frame_index },
-                        .mod = .{ .rm = .{ .size = .qword } },
-                    });
+        .arg, .dbg_var_val, .dbg_arg_inline => switch (mcv) {
+            .none, .unreach, .dead, .elementwise_args, .reserved_frame, .air_ref => unreachable,
+            .immediate => |imm| if (std.math.cast(u32, imm)) |small| try cg.addInst(.{
+                .tag = .pseudo,
+                .ops = switch (air_tag) {
+                    else => unreachable,
+                    .arg, .dbg_arg_inline => .pseudo_dbg_arg_i_u,
+                    .dbg_var_val => .pseudo_dbg_var_i_u,
                 },
-            }
+                .data = .{ .i = .{ .i = small } },
+            }) else try cg.addInst(.{
+                .tag = .pseudo,
+                .ops = switch (air_tag) {
+                    else => unreachable,
+                    .arg, .dbg_arg_inline => .pseudo_dbg_arg_i_64,
+                    .dbg_var_val => .pseudo_dbg_var_i_64,
+                },
+                .data = .{ .i64 = imm },
+            }),
+            .lea_frame => |frame_addr| try cg.addInst(.{
+                .tag = .pseudo,
+                .ops = switch (air_tag) {
+                    else => unreachable,
+                    .arg, .dbg_arg_inline => .pseudo_dbg_arg_fa,
+                    .dbg_var_val => .pseudo_dbg_var_fa,
+                },
+                .data = .{ .fa = frame_addr },
+            }),
+            .lea_symbol => |sym_off| try cg.addInst(.{
+                .tag = .pseudo,
+                .ops = switch (air_tag) {
+                    else => unreachable,
+                    .arg, .dbg_arg_inline => .pseudo_dbg_arg_reloc,
+                    .dbg_var_val => .pseudo_dbg_var_reloc,
+                },
+                .data = .{ .reloc = sym_off },
+            }),
+            else => {
+                const frame_index = try cg.allocFrameIndex(.initSpill(ty, cg.pt.zcu));
+                try cg.genSetMem(.{ .frame = frame_index }, 0, ty, mcv, .{});
+                _ = try cg.addInst(.{
+                    .tag = .pseudo,
+                    .ops = switch (air_tag) {
+                        else => unreachable,
+                        .arg, .dbg_arg_inline => .pseudo_dbg_arg_m,
+                        .dbg_var_val => .pseudo_dbg_var_m,
+                    },
+                    .data = .{ .x = .{
+                        .payload = try cg.addExtra(Mir.Memory.encode(.{
+                            .base = .{ .frame = frame_index },
+                            .mod = .{ .rm = .{ .size = .qword } },
+                        })),
+                    } },
+                });
+            },
         },
         .dbg_var_ptr => switch (mcv) {
             else => unreachable,
-            .unreach, .dead, .elementwise_args, .reserved_frame, .air_ref => unreachable,
-            .lea_frame => |frame_addr| try self.asmAirMemory(.dbg_local, inst, .{
-                .base = .{ .frame = frame_addr.index },
-                .mod = .{ .rm = .{
-                    .size = .qword,
-                    .disp = frame_addr.off,
+            .none, .unreach, .dead, .elementwise_args, .reserved_frame, .air_ref => unreachable,
+            .lea_frame => |frame_addr| try cg.addInst(.{
+                .tag = .pseudo,
+                .ops = .pseudo_dbg_var_m,
+                .data = .{ .x = .{
+                    .payload = try cg.addExtra(Mir.Memory.encode(.{
+                        .base = .{ .frame = frame_addr.index },
+                        .mod = .{ .rm = .{
+                            .size = .qword,
+                            .disp = frame_addr.off,
+                        } },
+                    })),
                 } },
             }),
             // debug info should explicitly ignore pcrel requirements
-            .lea_symbol, .lea_pcrel => |sym_off| try self.asmAirMemory(.dbg_local, inst, .{
-                .base = .{ .reloc = sym_off.sym_index },
-                .mod = .{ .rm = .{
-                    .size = .qword,
-                    .disp = sym_off.off,
+            .lea_symbol, .lea_pcrel => |sym_off| try cg.addInst(.{
+                .tag = .pseudo,
+                .ops = .pseudo_dbg_var_m,
+                .data = .{ .x = .{
+                    .payload = try cg.addExtra(Mir.Memory.encode(.{
+                        .base = .{ .reloc = sym_off.sym_index },
+                        .mod = .{ .rm = .{
+                            .size = .qword,
+                            .disp = sym_off.off,
+                        } },
+                    })),
                 } },
             }),
-            .lea_direct, .lea_got => |sym_index| try self.asmAirMemory(.dbg_local, inst, .{
-                .base = .{ .reloc = sym_index },
-                .mod = .{ .rm = .{ .size = .qword } },
+            .lea_direct, .lea_got => |sym_index| try cg.addInst(.{
+                .tag = .pseudo,
+                .ops = .pseudo_dbg_var_m,
+                .data = .{ .x = .{
+                    .payload = try cg.addExtra(Mir.Memory.encode(.{
+                        .base = .{ .reloc = sym_index },
+                        .mod = .{ .rm = .{ .size = .qword } },
+                    })),
+                } },
             }),
         },
-    }
+    };
 }
 
 fn airRetAddr(self: *CodeGen, inst: Air.Inst.Index) !void {
@@ -173514,8 +173475,8 @@ fn airCall(self: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif
         @ptrCast(self.air.extra.items[extra.end..][0..extra.data.args_len]);
 
     const ExpectedContents = extern struct {
-        tys: [16][@sizeOf(Type)]u8 align(@alignOf(Type)),
-        vals: [16][@sizeOf(MCValue)]u8 align(@alignOf(MCValue)),
+        tys: [32][@sizeOf(Type)]u8 align(@alignOf(Type)),
+        vals: [32][@sizeOf(MCValue)]u8 align(@alignOf(MCValue)),
     };
     var stack align(@max(@alignOf(ExpectedContents), @alignOf(std.heap.StackFallbackAllocator(0)))) =
         std.heap.stackFallback(@sizeOf(ExpectedContents), self.gpa);
@@ -173570,9 +173531,9 @@ fn genCall(self: *CodeGen, info: union(enum) {
     const fn_info = zcu.typeToFunc(fn_ty).?;
 
     const ExpectedContents = extern struct {
-        var_args: [16][@sizeOf(Type)]u8 align(@alignOf(Type)),
-        frame_indices: [16]FrameIndex,
-        reg_locks: [16][@sizeOf(?RegisterLock)]u8 align(@alignOf(?RegisterLock)),
+        var_args: [32][@sizeOf(Type)]u8 align(@alignOf(Type)),
+        frame_indices: [32]FrameIndex,
+        reg_locks: [32][@sizeOf(?RegisterLock)]u8 align(@alignOf(?RegisterLock)),
     };
     var stack align(@max(@alignOf(ExpectedContents), @alignOf(std.heap.StackFallbackAllocator(0)))) =
         std.heap.stackFallback(@sizeOf(ExpectedContents), self.gpa);
@@ -174488,10 +174449,21 @@ fn genTry(
     return result;
 }
 
-fn airDbgVar(self: *CodeGen, inst: Air.Inst.Index) !void {
-    const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
-    try self.genLocalDebugInfo(inst, try self.resolveInst(pl_op.operand));
-    return self.finishAir(inst, .unreach, .{ pl_op.operand, .none, .none });
+fn airDbgVar(cg: *CodeGen, inst: Air.Inst.Index) !void {
+    if (cg.mod.strip) return;
+    const air_tag = cg.air.instructions.items(.tag)[@intFromEnum(inst)];
+    const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
+
+    const name_nts: Air.NullTerminatedString = @enumFromInt(pl_op.payload);
+    assert(name_nts != .none);
+    const name = name_nts.toSlice(cg.air);
+    try cg.mir_local_name_bytes.appendSlice(cg.gpa, name[0 .. name.len + 1]);
+
+    const ty = cg.typeOf(pl_op.operand);
+    try cg.mir_local_types.append(cg.gpa, ty.toIntern());
+
+    try cg.genLocalDebugInfo(air_tag, ty, try cg.resolveInst(pl_op.operand));
+    return cg.finishAir(inst, .unreach, .{ pl_op.operand, .none, .none });
 }
 
 fn genCondBrMir(self: *CodeGen, ty: Type, mcv: MCValue) !Mir.Inst.Index {
@@ -181477,6 +181449,7 @@ fn lowerUav(self: *CodeGen, val: Value, alignment: InternPool.Alignment) InnerEr
 
 const CallMCValues = struct {
     args: []MCValue,
+    air_arg_count: u32,
     return_value: InstTracking,
     stack_byte_count: u31,
     stack_align: InternPool.Alignment,
@@ -181512,13 +181485,14 @@ fn resolveCallingConventionValues(
     const param_types = try allocator.alloc(Type, fn_info.param_types.len + var_args.len);
     defer allocator.free(param_types);
 
-    for (param_types[0..fn_info.param_types.len], fn_info.param_types.get(ip)) |*dest, src|
-        dest.* = .fromInterned(src);
+    for (param_types[0..fn_info.param_types.len], fn_info.param_types.get(ip)) |*param_ty, arg_ty|
+        param_ty.* = .fromInterned(arg_ty);
     for (param_types[fn_info.param_types.len..], var_args) |*param_ty, arg_ty|
         param_ty.* = self.promoteVarArg(arg_ty);
 
     var result: CallMCValues = .{
         .args = try self.gpa.alloc(MCValue, param_types.len),
+        .air_arg_count = 0,
         // These undefined values must be populated before returning from this function.
         .return_value = undefined,
         .stack_byte_count = 0,
@@ -181640,6 +181614,7 @@ fn resolveCallingConventionValues(
             // Input params
             for (param_types, result.args) |ty, *arg| {
                 assert(ty.hasRuntimeBitsIgnoreComptime(zcu));
+                result.air_arg_count += 1;
                 switch (cc) {
                     .x86_64_sysv => {},
                     .x86_64_win => {
@@ -181812,6 +181787,7 @@ fn resolveCallingConventionValues(
                     arg.* = .none;
                     continue;
                 }
+                result.air_arg_count += 1;
                 const param_size: u31 = @intCast(param_ty.abiSize(zcu));
                 if (abi.zigcc.params_in_regs) switch (self.regClassForType(param_ty)) {
                     .general_purpose, .gphi => if (param_gpr.len >= 1 and param_size <= @as(u4, switch (self.target.cpu.arch) {
src/arch/x86_64/Emit.zig
@@ -1,6 +1,5 @@
 //! This file contains the functionality for emitting x86_64 MIR as machine code
 
-air: Air,
 lower: Lower,
 atom_index: u32,
 debug_output: link.File.DebugInfoOutput,
@@ -22,6 +21,8 @@ pub fn emitMir(emit: *Emit) Error!void {
     defer relocs.deinit(emit.lower.allocator);
     var table_relocs: std.ArrayListUnmanaged(TableReloc) = .empty;
     defer table_relocs.deinit(emit.lower.allocator);
+    var local_name_index: usize = 0;
+    var local_index: usize = 0;
     for (0..emit.lower.mir.instructions.len) |mir_i| {
         const mir_index: Mir.Inst.Index = @intCast(mir_i);
         code_offset_mapping[mir_index] = @intCast(emit.code.items.len);
@@ -338,7 +339,7 @@ pub fn emitMir(emit: *Emit) Error!void {
                             log.debug("mirDbgEnterInline (line={d}, col={d})", .{
                                 emit.prev_di_loc.line, emit.prev_di_loc.column,
                             });
-                            try dwarf.enterInlineFunc(mir_inst.data.func, emit.code.items.len, emit.prev_di_loc.line, emit.prev_di_loc.column);
+                            try dwarf.enterInlineFunc(mir_inst.data.ip_index, emit.code.items.len, emit.prev_di_loc.line, emit.prev_di_loc.column);
                         },
                         .plan9 => {},
                         .none => {},
@@ -348,77 +349,61 @@ pub fn emitMir(emit: *Emit) Error!void {
                             log.debug("mirDbgLeaveInline (line={d}, col={d})", .{
                                 emit.prev_di_loc.line, emit.prev_di_loc.column,
                             });
-                            try dwarf.leaveInlineFunc(mir_inst.data.func, emit.code.items.len);
+                            try dwarf.leaveInlineFunc(mir_inst.data.ip_index, emit.code.items.len);
                         },
                         .plan9 => {},
                         .none => {},
                     },
-                    .pseudo_dbg_local_a,
-                    .pseudo_dbg_local_ai_s,
-                    .pseudo_dbg_local_ai_u,
-                    .pseudo_dbg_local_ai_64,
-                    .pseudo_dbg_local_as,
-                    .pseudo_dbg_local_aso,
-                    .pseudo_dbg_local_aro,
-                    .pseudo_dbg_local_af,
-                    .pseudo_dbg_local_am,
+                    .pseudo_dbg_arg_none,
+                    .pseudo_dbg_arg_i_s,
+                    .pseudo_dbg_arg_i_u,
+                    .pseudo_dbg_arg_i_64,
+                    .pseudo_dbg_arg_reloc,
+                    .pseudo_dbg_arg_ro,
+                    .pseudo_dbg_arg_fa,
+                    .pseudo_dbg_arg_m,
+                    .pseudo_dbg_var_none,
+                    .pseudo_dbg_var_i_s,
+                    .pseudo_dbg_var_i_u,
+                    .pseudo_dbg_var_i_64,
+                    .pseudo_dbg_var_reloc,
+                    .pseudo_dbg_var_ro,
+                    .pseudo_dbg_var_fa,
+                    .pseudo_dbg_var_m,
                     => switch (emit.debug_output) {
                         .dwarf => |dwarf| {
                             var loc_buf: [2]link.File.Dwarf.Loc = undefined;
-                            const air_inst_index, const loc: link.File.Dwarf.Loc = switch (mir_inst.ops) {
+                            const loc: link.File.Dwarf.Loc = loc: switch (mir_inst.ops) {
                                 else => unreachable,
-                                .pseudo_dbg_local_a => .{ mir_inst.data.a.air_inst, .empty },
-                                .pseudo_dbg_local_ai_s,
-                                .pseudo_dbg_local_ai_u,
-                                .pseudo_dbg_local_ai_64,
-                                => .{ mir_inst.data.ai.air_inst, .{ .stack_value = stack_value: {
-                                    loc_buf[0] = switch (emit.lower.imm(mir_inst.ops, mir_inst.data.ai.i)) {
+                                .pseudo_dbg_arg_none, .pseudo_dbg_var_none => .empty,
+                                .pseudo_dbg_arg_i_s,
+                                .pseudo_dbg_arg_i_u,
+                                .pseudo_dbg_var_i_s,
+                                .pseudo_dbg_var_i_u,
+                                => .{ .stack_value = stack_value: {
+                                    loc_buf[0] = switch (emit.lower.imm(mir_inst.ops, mir_inst.data.i.i)) {
                                         .signed => |s| .{ .consts = s },
                                         .unsigned => |u| .{ .constu = u },
                                     };
                                     break :stack_value &loc_buf[0];
-                                } } },
-                                .pseudo_dbg_local_as => .{ mir_inst.data.as.air_inst, .{
-                                    .addr_reloc = mir_inst.data.as.sym_index,
                                 } },
-                                .pseudo_dbg_local_aso => loc: {
-                                    const sym_off = emit.lower.mir.extraData(
-                                        bits.SymbolOffset,
-                                        mir_inst.data.ax.payload,
-                                    ).data;
-                                    break :loc .{ mir_inst.data.ax.air_inst, .{ .plus = .{
-                                        sym: {
-                                            loc_buf[0] = .{ .addr_reloc = sym_off.sym_index };
-                                            break :sym &loc_buf[0];
-                                        },
-                                        off: {
-                                            loc_buf[1] = .{ .consts = sym_off.off };
-                                            break :off &loc_buf[1];
-                                        },
-                                    } } };
-                                },
-                                .pseudo_dbg_local_aro => loc: {
-                                    const air_off = emit.lower.mir.extraData(
-                                        Mir.AirOffset,
-                                        mir_inst.data.rx.payload,
-                                    ).data;
-                                    break :loc .{ air_off.air_inst, .{ .plus = .{
-                                        reg: {
-                                            loc_buf[0] = .{ .breg = mir_inst.data.rx.r1.dwarfNum() };
-                                            break :reg &loc_buf[0];
-                                        },
-                                        off: {
-                                            loc_buf[1] = .{ .consts = air_off.off };
-                                            break :off &loc_buf[1];
-                                        },
-                                    } } };
-                                },
-                                .pseudo_dbg_local_af => loc: {
-                                    const reg_off = emit.lower.mir.resolveFrameAddr(emit.lower.mir.extraData(
-                                        bits.FrameAddr,
-                                        mir_inst.data.ax.payload,
-                                    ).data);
-                                    break :loc .{ mir_inst.data.ax.air_inst, .{ .plus = .{
+                                .pseudo_dbg_arg_i_64, .pseudo_dbg_var_i_64 => .{ .stack_value = stack_value: {
+                                    loc_buf[0] = .{ .constu = mir_inst.data.i64 };
+                                    break :stack_value &loc_buf[0];
+                                } },
+                                .pseudo_dbg_arg_reloc, .pseudo_dbg_var_reloc => .{ .plus = .{
+                                    sym: {
+                                        loc_buf[0] = .{ .addr_reloc = mir_inst.data.reloc.sym_index };
+                                        break :sym &loc_buf[0];
+                                    },
+                                    off: {
+                                        loc_buf[1] = .{ .consts = mir_inst.data.reloc.off };
+                                        break :off &loc_buf[1];
+                                    },
+                                } },
+                                .pseudo_dbg_arg_fa, .pseudo_dbg_var_fa => {
+                                    const reg_off = emit.lower.mir.resolveFrameAddr(mir_inst.data.fa);
+                                    break :loc .{ .plus = .{
                                         reg: {
                                             loc_buf[0] = .{ .breg = reg_off.reg.dwarfNum() };
                                             break :reg &loc_buf[0];
@@ -427,11 +412,11 @@ pub fn emitMir(emit: *Emit) Error!void {
                                             loc_buf[1] = .{ .consts = reg_off.off };
                                             break :off &loc_buf[1];
                                         },
-                                    } } };
+                                    } };
                                 },
-                                .pseudo_dbg_local_am => loc: {
-                                    const mem = emit.lower.mem(undefined, mir_inst.data.ax.payload);
-                                    break :loc .{ mir_inst.data.ax.air_inst, .{ .plus = .{
+                                .pseudo_dbg_arg_m, .pseudo_dbg_var_m => {
+                                    const mem = emit.lower.mem(undefined, mir_inst.data.x.payload);
+                                    break :loc .{ .plus = .{
                                         base: {
                                             loc_buf[0] = switch (mem.base()) {
                                                 .none => .{ .constu = 0 },
@@ -449,30 +434,64 @@ pub fn emitMir(emit: *Emit) Error!void {
                                             };
                                             break :disp &loc_buf[1];
                                         },
-                                    } } };
+                                    } };
                                 },
                             };
-                            const ip = &emit.lower.bin_file.comp.zcu.?.intern_pool;
-                            const air_inst = emit.air.instructions.get(@intFromEnum(air_inst_index));
-                            const name: Air.NullTerminatedString = switch (air_inst.tag) {
-                                else => unreachable,
-                                .arg => air_inst.data.arg.name,
-                                .dbg_var_ptr, .dbg_var_val, .dbg_arg_inline => @enumFromInt(air_inst.data.pl_op.payload),
-                            };
-                            try dwarf.genLocalDebugInfo(
-                                switch (air_inst.tag) {
+
+                            const local_name_bytes = emit.lower.mir.local_name_bytes[local_name_index..];
+                            const local_name = local_name_bytes[0..std.mem.indexOfScalar(u8, local_name_bytes, 0).? :0];
+                            local_name_index += local_name.len + 1;
+
+                            const local_type = emit.lower.mir.local_types[local_index];
+                            local_index += 1;
+
+                            try dwarf.genLocalVarDebugInfo(
+                                switch (mir_inst.ops) {
                                     else => unreachable,
-                                    .arg, .dbg_arg_inline => .local_arg,
-                                    .dbg_var_ptr, .dbg_var_val => .local_var,
+                                    .pseudo_dbg_arg_none,
+                                    .pseudo_dbg_arg_i_s,
+                                    .pseudo_dbg_arg_i_u,
+                                    .pseudo_dbg_arg_i_64,
+                                    .pseudo_dbg_arg_reloc,
+                                    .pseudo_dbg_arg_ro,
+                                    .pseudo_dbg_arg_fa,
+                                    .pseudo_dbg_arg_m,
+                                    .pseudo_dbg_arg_val,
+                                    => .arg,
+                                    .pseudo_dbg_var_none,
+                                    .pseudo_dbg_var_i_s,
+                                    .pseudo_dbg_var_i_u,
+                                    .pseudo_dbg_var_i_64,
+                                    .pseudo_dbg_var_reloc,
+                                    .pseudo_dbg_var_ro,
+                                    .pseudo_dbg_var_fa,
+                                    .pseudo_dbg_var_m,
+                                    .pseudo_dbg_var_val,
+                                    => .local_var,
                                 },
-                                name.toSlice(emit.air),
-                                switch (air_inst.tag) {
+                                local_name,
+                                .fromInterned(local_type),
+                                loc,
+                            );
+                        },
+                        .plan9 => {},
+                        .none => {},
+                    },
+                    .pseudo_dbg_arg_val, .pseudo_dbg_var_val => switch (emit.debug_output) {
+                        .dwarf => |dwarf| {
+                            const local_name_bytes = emit.lower.mir.local_name_bytes[local_name_index..];
+                            const local_name = local_name_bytes[0..std.mem.indexOfScalar(u8, local_name_bytes, 0).? :0];
+                            local_name_index += local_name.len + 1;
+
+                            try dwarf.genLocalConstDebugInfo(
+                                emit.lower.src_loc,
+                                switch (mir_inst.ops) {
                                     else => unreachable,
-                                    .arg => emit.air.typeOfIndex(air_inst_index, ip),
-                                    .dbg_var_ptr => emit.air.typeOf(air_inst.data.pl_op.operand, ip).childTypeIp(ip),
-                                    .dbg_var_val, .dbg_arg_inline => emit.air.typeOf(air_inst.data.pl_op.operand, ip),
+                                    .pseudo_dbg_arg_val => .comptime_arg,
+                                    .pseudo_dbg_var_val => .local_const,
                                 },
-                                loc,
+                                local_name,
+                                .fromInterned(mir_inst.data.ip_index),
                             );
                         },
                         .plan9 => {},
@@ -611,11 +630,10 @@ fn dbgAdvancePCAndLine(emit: *Emit, loc: Loc) Error!void {
 }
 
 const bits = @import("bits.zig");
+const Emit = @This();
+const InternPool = @import("../../InternPool.zig");
 const link = @import("../../link.zig");
 const log = std.log.scoped(.emit);
-const std = @import("std");
-
-const Air = @import("../../Air.zig");
-const Emit = @This();
 const Lower = @import("Lower.zig");
 const Mir = @import("Mir.zig");
+const std = @import("std");
src/arch/x86_64/Lower.zig
@@ -327,16 +327,25 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
             .pseudo_dbg_leave_block_none,
             .pseudo_dbg_enter_inline_func,
             .pseudo_dbg_leave_inline_func,
-            .pseudo_dbg_local_a,
-            .pseudo_dbg_local_ai_s,
-            .pseudo_dbg_local_ai_u,
-            .pseudo_dbg_local_ai_64,
-            .pseudo_dbg_local_as,
-            .pseudo_dbg_local_aso,
-            .pseudo_dbg_local_aro,
-            .pseudo_dbg_local_af,
-            .pseudo_dbg_local_am,
+            .pseudo_dbg_arg_none,
+            .pseudo_dbg_arg_i_s,
+            .pseudo_dbg_arg_i_u,
+            .pseudo_dbg_arg_i_64,
+            .pseudo_dbg_arg_reloc,
+            .pseudo_dbg_arg_ro,
+            .pseudo_dbg_arg_fa,
+            .pseudo_dbg_arg_m,
+            .pseudo_dbg_arg_val,
             .pseudo_dbg_var_args_none,
+            .pseudo_dbg_var_none,
+            .pseudo_dbg_var_i_s,
+            .pseudo_dbg_var_i_u,
+            .pseudo_dbg_var_i_64,
+            .pseudo_dbg_var_reloc,
+            .pseudo_dbg_var_ro,
+            .pseudo_dbg_var_fa,
+            .pseudo_dbg_var_m,
+            .pseudo_dbg_var_val,
 
             .pseudo_dead_none,
             => {},
@@ -364,7 +373,8 @@ pub fn imm(lower: *const Lower, ops: Mir.Inst.Ops, i: u32) Immediate {
         .i_s,
         .mi_s,
         .rmi_s,
-        .pseudo_dbg_local_ai_s,
+        .pseudo_dbg_arg_i_s,
+        .pseudo_dbg_var_i_s,
         => .s(@bitCast(i)),
 
         .ii,
@@ -379,13 +389,17 @@ pub fn imm(lower: *const Lower, ops: Mir.Inst.Ops, i: u32) Immediate {
         .mri,
         .rrm,
         .rrmi,
-        .pseudo_dbg_local_ai_u,
+        .pseudo_dbg_arg_i_u,
+        .pseudo_dbg_var_i_u,
         => .u(i),
 
         .ri_64,
-        .pseudo_dbg_local_ai_64,
         => .u(lower.mir.extraData(Mir.Imm64, i).data.decode()),
 
+        .pseudo_dbg_arg_i_64,
+        .pseudo_dbg_var_i_64,
+        => unreachable,
+
         else => unreachable,
     };
 }
src/arch/x86_64/Mir.zig
@@ -9,6 +9,8 @@
 instructions: std.MultiArrayList(Inst).Slice,
 /// The meaning of this data is determined by `Inst.Tag` value.
 extra: []const u32,
+local_name_bytes: []const u8,
+local_types: []const InternPool.Index,
 table: []const Inst.Index,
 frame_locs: std.MultiArrayList(FrameLoc).Slice,
 
@@ -1522,6 +1524,7 @@ pub const Inst = struct {
         pseudo_cfi_escape_bytes,
 
         /// End of prologue
+        /// Uses `none` payload.
         pseudo_dbg_prologue_end_none,
         /// Update debug line with is_stmt register set
         /// Uses `line_column` payload.
@@ -1530,44 +1533,76 @@ pub const Inst = struct {
         /// Uses `line_column` payload.
         pseudo_dbg_line_line_column,
         /// Start of epilogue
+        /// Uses `none` payload.
         pseudo_dbg_epilogue_begin_none,
         /// Start of lexical block
+        /// Uses `none` payload.
         pseudo_dbg_enter_block_none,
         /// End of lexical block
+        /// Uses `none` payload.
         pseudo_dbg_leave_block_none,
         /// Start of inline function
+        /// Uses `ip_index` payload.
         pseudo_dbg_enter_inline_func,
         /// End of inline function
+        /// Uses `ip_index` payload.
         pseudo_dbg_leave_inline_func,
-        /// Local argument or variable.
-        /// Uses `a` payload.
-        pseudo_dbg_local_a,
-        /// Local argument or variable.
-        /// Uses `ai` payload.
-        pseudo_dbg_local_ai_s,
-        /// Local argument or variable.
-        /// Uses `ai` payload.
-        pseudo_dbg_local_ai_u,
-        /// Local argument or variable.
-        /// Uses `ai` payload with extra data of type `Imm64`.
-        pseudo_dbg_local_ai_64,
-        /// Local argument or variable.
-        /// Uses `as` payload.
-        pseudo_dbg_local_as,
-        /// Local argument or variable.
-        /// Uses `ax` payload with extra data of type `bits.SymbolOffset`.
-        pseudo_dbg_local_aso,
-        /// Local argument or variable.
-        /// Uses `rx` payload with extra data of type `AirOffset`.
-        pseudo_dbg_local_aro,
-        /// Local argument or variable.
-        /// Uses `ax` payload with extra data of type `bits.FrameAddr`.
-        pseudo_dbg_local_af,
-        /// Local argument or variable.
-        /// Uses `ax` payload with extra data of type `Memory`.
-        pseudo_dbg_local_am,
+        /// Local argument.
+        /// Uses `none` payload.
+        pseudo_dbg_arg_none,
+        /// Local argument.
+        /// Uses `i` payload.
+        pseudo_dbg_arg_i_s,
+        /// Local argument.
+        /// Uses `i` payload.
+        pseudo_dbg_arg_i_u,
+        /// Local argument.
+        /// Uses `i64` payload.
+        pseudo_dbg_arg_i_64,
+        /// Local argument.
+        /// Uses `reloc` payload.
+        pseudo_dbg_arg_reloc,
+        /// Local argument.
+        /// Uses `ro` payload.
+        pseudo_dbg_arg_ro,
+        /// Local argument.
+        /// Uses `fa` payload.
+        pseudo_dbg_arg_fa,
+        /// Local argument.
+        /// Uses `x` payload with extra data of type `Memory`.
+        pseudo_dbg_arg_m,
+        /// Local argument.
+        /// Uses `ip_index` payload.
+        pseudo_dbg_arg_val,
         /// Remaining arguments are varargs.
         pseudo_dbg_var_args_none,
+        /// Local variable.
+        /// Uses `none` payload.
+        pseudo_dbg_var_none,
+        /// Local variable.
+        /// Uses `i` payload.
+        pseudo_dbg_var_i_s,
+        /// Local variable.
+        /// Uses `i` payload.
+        pseudo_dbg_var_i_u,
+        /// Local variable.
+        /// Uses `i64` payload.
+        pseudo_dbg_var_i_64,
+        /// Local variable.
+        /// Uses `reloc` payload.
+        pseudo_dbg_var_reloc,
+        /// Local variable.
+        /// Uses `ro` payload.
+        pseudo_dbg_var_ro,
+        /// Local variable.
+        /// Uses `fa` payload.
+        pseudo_dbg_var_fa,
+        /// Local variable.
+        /// Uses `x` payload with extra data of type `Memory`.
+        pseudo_dbg_var_m,
+        /// Local variable.
+        /// Uses `ip_index` payload.
+        pseudo_dbg_var_val,
 
         /// Tombstone
         /// Emitter should skip this instruction.
@@ -1584,6 +1619,7 @@ pub const Inst = struct {
             inst: Index,
         },
         /// A 32-bit immediate value.
+        i64: u64,
         i: struct {
             fixes: Fixes = ._,
             i: u32,
@@ -1683,31 +1719,18 @@ pub const Inst = struct {
                 return std.mem.sliceAsBytes(mir.extra[bytes.payload..])[0..bytes.len];
             }
         },
-        a: struct {
-            air_inst: Air.Inst.Index,
-        },
-        ai: struct {
-            air_inst: Air.Inst.Index,
-            i: u32,
-        },
-        as: struct {
-            air_inst: Air.Inst.Index,
-            sym_index: u32,
-        },
-        ax: struct {
-            air_inst: Air.Inst.Index,
-            payload: u32,
-        },
         /// Relocation for the linker where:
         /// * `sym_index` is the index of the target
         /// * `off` is the offset from the target
         reloc: bits.SymbolOffset,
+        fa: bits.FrameAddr,
+        ro: bits.RegisterOffset,
         /// Debug line and column position
         line_column: struct {
             line: u32,
             column: u32,
         },
-        func: InternPool.Index,
+        ip_index: InternPool.Index,
         /// Register list
         reg_list: RegisterList,
     };
@@ -1760,8 +1783,6 @@ pub const Inst = struct {
     }
 };
 
-pub const AirOffset = struct { air_inst: Air.Inst.Index, off: i32 };
-
 /// Used in conjunction with payload to transfer a list of used registers in a compact manner.
 pub const RegisterList = struct {
     bitset: BitSet,
@@ -1924,6 +1945,8 @@ pub const Memory = struct {
 pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void {
     mir.instructions.deinit(gpa);
     gpa.free(mir.extra);
+    gpa.free(mir.local_name_bytes);
+    gpa.free(mir.local_types);
     gpa.free(mir.table);
     mir.frame_locs.deinit(gpa);
     mir.* = undefined;
@@ -1937,8 +1960,6 @@ pub fn emit(
     func_index: InternPool.Index,
     code: *std.ArrayListUnmanaged(u8),
     debug_output: link.File.DebugInfoOutput,
-    /// TODO: remove dependency on this argument. This blocks enabling `Zcu.Feature.separate_thread`.
-    air: *const Air,
 ) codegen.CodeGenError!void {
     const zcu = pt.zcu;
     const comp = zcu.comp;
@@ -1948,7 +1969,6 @@ pub fn emit(
     const nav = func.owner_nav;
     const mod = zcu.navFileScope(nav).mod.?;
     var e: Emit = .{
-        .air = air.*,
         .lower = .{
             .bin_file = lf,
             .target = &mod.resolved_target.result,
@@ -1998,7 +2018,7 @@ pub fn extraData(mir: Mir, comptime T: type, index: u32) struct { data: T, end:
         @field(result, field.name) = switch (field.type) {
             u32 => mir.extra[i],
             i32, Memory.Info => @bitCast(mir.extra[i]),
-            bits.FrameIndex, Air.Inst.Index => @enumFromInt(mir.extra[i]),
+            bits.FrameIndex => @enumFromInt(mir.extra[i]),
             else => @compileError("bad field type: " ++ field.name ++ ": " ++ @typeName(field.type)),
         };
         i += 1;
@@ -2043,7 +2063,6 @@ const builtin = @import("builtin");
 const encoder = @import("encoder.zig");
 const std = @import("std");
 
-const Air = @import("../../Air.zig");
 const IntegerBitSet = std.bit_set.IntegerBitSet;
 const InternPool = @import("../../InternPool.zig");
 const Mir = @This();
src/codegen/llvm.zig
@@ -9509,15 +9509,21 @@ pub const FuncGen = struct {
 
         const inst_ty = self.typeOfIndex(inst);
 
-        const name = self.air.instructions.items(.data)[@intFromEnum(inst)].arg.name;
-        if (name == .none) return arg_val;
-
         const func = zcu.funcInfo(zcu.navValue(self.ng.nav_index).toIntern());
+        const func_zir = func.zir_body_inst.resolveFull(&zcu.intern_pool).?;
+        const file = zcu.fileByIndex(func_zir.file);
+
+        const mod = file.mod.?;
+        if (mod.strip) return arg_val;
+        const arg = self.air.instructions.items(.data)[@intFromEnum(inst)].arg;
+        const zir = &file.zir.?;
+        const name = zir.nullTerminatedString(zir.getParamName(zir.getParamBody(func_zir.inst)[arg.zir_param_index]).?);
+
         const lbrace_line = zcu.navSrcLine(func.owner_nav) + func.lbrace_line + 1;
         const lbrace_col = func.lbrace_column + 1;
 
         const debug_parameter = try o.builder.debugParameter(
-            try o.builder.metadataString(name.toSlice(self.air)),
+            try o.builder.metadataString(name),
             self.file,
             self.scope,
             lbrace_line,
@@ -9535,7 +9541,6 @@ pub const FuncGen = struct {
             },
         };
 
-        const mod = self.ng.ownerModule();
         if (isByRef(inst_ty, zcu)) {
             _ = try self.wip.callIntrinsic(
                 .normal,
src/link/Elf/ZigObject.zig
@@ -1417,9 +1417,6 @@ pub fn updateFunc(
     pt: Zcu.PerThread,
     func_index: InternPool.Index,
     mir: *const codegen.AnyMir,
-    /// This may be `undefined`; only pass it to `emitFunction`.
-    /// This parameter will eventually be removed.
-    maybe_undef_air: *const Air,
 ) link.File.UpdateNavError!void {
     const tracy = trace(@src());
     defer tracy.end();
@@ -1448,7 +1445,6 @@ pub fn updateFunc(
         mir,
         &code_buffer,
         if (debug_wip_nav) |*dn| .{ .dwarf = dn } else .none,
-        maybe_undef_air,
     );
     const code = code_buffer.items;
 
@@ -2363,7 +2359,6 @@ const trace = @import("../../tracy.zig").trace;
 const std = @import("std");
 const Allocator = std.mem.Allocator;
 
-const Air = @import("../../Air.zig");
 const Archive = @import("Archive.zig");
 const Atom = @import("Atom.zig");
 const Dwarf = @import("../Dwarf.zig");
src/link/MachO/ZigObject.zig
@@ -778,9 +778,6 @@ pub fn updateFunc(
     pt: Zcu.PerThread,
     func_index: InternPool.Index,
     mir: *const codegen.AnyMir,
-    /// This may be `undefined`; only pass it to `emitFunction`.
-    /// This parameter will eventually be removed.
-    maybe_undef_air: *const Air,
 ) link.File.UpdateNavError!void {
     const tracy = trace(@src());
     defer tracy.end();
@@ -806,7 +803,6 @@ pub fn updateFunc(
         mir,
         &code_buffer,
         if (debug_wip_nav) |*wip_nav| .{ .dwarf = wip_nav } else .none,
-        maybe_undef_air,
     );
     const code = code_buffer.items;
 
@@ -1815,7 +1811,6 @@ const target_util = @import("../../target.zig");
 const trace = @import("../../tracy.zig").trace;
 const std = @import("std");
 
-const Air = @import("../../Air.zig");
 const Allocator = std.mem.Allocator;
 const Archive = @import("Archive.zig");
 const Atom = @import("Atom.zig");
src/link/C.zig
@@ -17,7 +17,6 @@ const link = @import("../link.zig");
 const trace = @import("../tracy.zig").trace;
 const Type = @import("../Type.zig");
 const Value = @import("../Value.zig");
-const Air = @import("../Air.zig");
 const AnyMir = @import("../codegen.zig").AnyMir;
 
 pub const zig_h = "#include \"zig.h\"\n";
@@ -182,12 +181,7 @@ pub fn updateFunc(
     pt: Zcu.PerThread,
     func_index: InternPool.Index,
     mir: *AnyMir,
-    /// This may be `undefined`; only pass it to `emitFunction`.
-    /// This parameter will eventually be removed.
-    maybe_undef_air: *const Air,
 ) link.File.UpdateNavError!void {
-    _ = maybe_undef_air; // It would be a bug to use this argument.
-
     const zcu = pt.zcu;
     const gpa = zcu.gpa;
     const func = zcu.funcInfo(func_index);
src/link/Coff.zig
@@ -1053,9 +1053,6 @@ pub fn updateFunc(
     pt: Zcu.PerThread,
     func_index: InternPool.Index,
     mir: *const codegen.AnyMir,
-    /// This may be `undefined`; only pass it to `emitFunction`.
-    /// This parameter will eventually be removed.
-    maybe_undef_air: *const Air,
 ) link.File.UpdateNavError!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");
@@ -1084,7 +1081,6 @@ pub fn updateFunc(
         mir,
         &code_buffer,
         .none,
-        maybe_undef_air,
     );
 
     try coff.updateNavCode(pt, nav_index, code_buffer.items, .FUNCTION);
@@ -3123,7 +3119,6 @@ const link = @import("../link.zig");
 const target_util = @import("../target.zig");
 const trace = @import("../tracy.zig").trace;
 
-const Air = @import("../Air.zig");
 const Compilation = @import("../Compilation.zig");
 const Zcu = @import("../Zcu.zig");
 const InternPool = @import("../InternPool.zig");
src/link/Dwarf.zig
@@ -1474,17 +1474,18 @@ pub const WipNav = struct {
         try cfa.write(wip_nav);
     }
 
-    pub const LocalTag = enum { local_arg, local_var };
-    pub fn genLocalDebugInfo(
+    pub const LocalVarTag = enum { arg, local_var };
+    pub fn genLocalVarDebugInfo(
         wip_nav: *WipNav,
-        tag: LocalTag,
+        tag: LocalVarTag,
         name: []const u8,
         ty: Type,
         loc: Loc,
     ) UpdateError!void {
         assert(wip_nav.func != .none);
         try wip_nav.abbrevCode(switch (tag) {
-            inline else => |ct_tag| @field(AbbrevCode, @tagName(ct_tag)),
+            .arg => .arg,
+            .local_var => .local_var,
         });
         try wip_nav.strp(name);
         try wip_nav.refType(ty);
@@ -1492,6 +1493,40 @@ pub const WipNav = struct {
         wip_nav.any_children = true;
     }
 
+    pub const LocalConstTag = enum { comptime_arg, local_const };
+    pub fn genLocalConstDebugInfo(
+        wip_nav: *WipNav,
+        src_loc: Zcu.LazySrcLoc,
+        tag: LocalConstTag,
+        name: []const u8,
+        val: Value,
+    ) UpdateError!void {
+        assert(wip_nav.func != .none);
+        const pt = wip_nav.pt;
+        const zcu = pt.zcu;
+        const ty = val.typeOf(zcu);
+        const has_runtime_bits = ty.hasRuntimeBits(zcu);
+        const has_comptime_state = ty.comptimeOnly(zcu) and try ty.onePossibleValue(pt) == null;
+        try wip_nav.abbrevCode(if (has_runtime_bits and has_comptime_state) switch (tag) {
+            .comptime_arg => .comptime_arg_runtime_bits_comptime_state,
+            .local_const => .local_const_runtime_bits_comptime_state,
+        } else if (has_comptime_state) switch (tag) {
+            .comptime_arg => .comptime_arg_comptime_state,
+            .local_const => .local_const_comptime_state,
+        } else if (has_runtime_bits) switch (tag) {
+            .comptime_arg => .comptime_arg_runtime_bits,
+            .local_const => .local_const_runtime_bits,
+        } else switch (tag) {
+            .comptime_arg => .comptime_arg,
+            .local_const => .local_const,
+        });
+        try wip_nav.strp(name);
+        try wip_nav.refType(ty);
+        if (has_runtime_bits) try wip_nav.blockValue(src_loc, val);
+        if (has_comptime_state) try wip_nav.refValue(val);
+        wip_nav.any_children = true;
+    }
+
     pub fn genVarArgsDebugInfo(wip_nav: *WipNav) UpdateError!void {
         assert(wip_nav.func != .none);
         try wip_nav.abbrevCode(.is_var_args);
@@ -1825,7 +1860,8 @@ pub const WipNav = struct {
     fn getNavEntry(wip_nav: *WipNav, nav_index: InternPool.Nav.Index) UpdateError!struct { Unit.Index, Entry.Index } {
         const zcu = wip_nav.pt.zcu;
         const ip = &zcu.intern_pool;
-        const unit = try wip_nav.dwarf.getUnit(zcu.fileByIndex(ip.getNav(nav_index).srcInst(ip).resolveFile(ip)).mod.?);
+        const nav = ip.getNav(nav_index);
+        const unit = try wip_nav.dwarf.getUnit(zcu.fileByIndex(nav.srcInst(ip).resolveFile(ip)).mod.?);
         const gop = try wip_nav.dwarf.navs.getOrPut(wip_nav.dwarf.gpa, nav_index);
         if (gop.found_existing) return .{ unit, gop.value_ptr.* };
         const entry = try wip_nav.dwarf.addCommonEntry(unit);
@@ -1842,10 +1878,16 @@ pub const WipNav = struct {
         const zcu = wip_nav.pt.zcu;
         const ip = &zcu.intern_pool;
         const maybe_inst_index = ty.typeDeclInst(zcu);
-        const unit = if (maybe_inst_index) |inst_index|
-            try wip_nav.dwarf.getUnit(zcu.fileByIndex(inst_index.resolveFile(ip)).mod.?)
-        else
-            .main;
+        const unit = if (maybe_inst_index) |inst_index| switch (switch (ip.indexToKey(ty.toIntern())) {
+            else => unreachable,
+            .struct_type => ip.loadStructType(ty.toIntern()).name_nav,
+            .union_type => ip.loadUnionType(ty.toIntern()).name_nav,
+            .enum_type => ip.loadEnumType(ty.toIntern()).name_nav,
+            .opaque_type => ip.loadOpaqueType(ty.toIntern()).name_nav,
+        }) {
+            .none => try wip_nav.dwarf.getUnit(zcu.fileByIndex(inst_index.resolveFile(ip)).mod.?),
+            else => |name_nav| return wip_nav.getNavEntry(name_nav.unwrap().?),
+        } else .main;
         const gop = try wip_nav.dwarf.types.getOrPut(wip_nav.dwarf.gpa, ty.toIntern());
         if (gop.found_existing) return .{ unit, gop.value_ptr.* };
         const entry = try wip_nav.dwarf.addCommonEntry(unit);
@@ -1864,10 +1906,8 @@ pub const WipNav = struct {
         const ip = &zcu.intern_pool;
         const ty = value.typeOf(zcu);
         if (std.debug.runtime_safety) assert(ty.comptimeOnly(zcu) and try ty.onePossibleValue(wip_nav.pt) == null);
-        if (!value.isUndef(zcu)) {
-            if (ty.toIntern() == .type_type) return wip_nav.getTypeEntry(value.toType());
-            if (ip.isFunctionType(ty.toIntern())) return wip_nav.getNavEntry(zcu.funcInfo(value.toIntern()).owner_nav);
-        }
+        if (ty.toIntern() == .type_type) return wip_nav.getTypeEntry(value.toType());
+        if (ip.isFunctionType(ty.toIntern()) and !value.isUndef(zcu)) return wip_nav.getNavEntry(zcu.funcInfo(value.toIntern()).owner_nav);
         const gop = try wip_nav.dwarf.values.getOrPut(wip_nav.dwarf.gpa, value.toIntern());
         const unit: Unit.Index = .main;
         if (gop.found_existing) return .{ unit, gop.value_ptr.* };
@@ -1916,7 +1956,10 @@ pub const WipNav = struct {
             &wip_nav.debug_info,
             .{ .debug_output = .{ .dwarf = wip_nav } },
         );
-        assert(old_len + bytes == wip_nav.debug_info.items.len);
+        if (old_len + bytes != wip_nav.debug_info.items.len) {
+            std.debug.print("{} [{}]: {} != {}\n", .{ ty.fmt(wip_nav.pt), ty.toIntern(), bytes, wip_nav.debug_info.items.len - old_len });
+            unreachable;
+        }
     }
 
     const AbbrevCodeForForm = struct {
@@ -2788,6 +2831,7 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo
             const type_gop = try dwarf.types.getOrPut(dwarf.gpa, nav_val.toIntern());
             if (type_gop.found_existing) {
                 if (dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(type_gop.value_ptr.*).len > 0) break :tag .decl_alias;
+                assert(!nav_gop.found_existing);
                 nav_gop.value_ptr.* = type_gop.value_ptr.*;
             } else {
                 if (nav_gop.found_existing)
@@ -2890,6 +2934,7 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo
             const type_gop = try dwarf.types.getOrPut(dwarf.gpa, nav_val.toIntern());
             if (type_gop.found_existing) {
                 if (dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(type_gop.value_ptr.*).len > 0) break :tag .decl_alias;
+                assert(!nav_gop.found_existing);
                 nav_gop.value_ptr.* = type_gop.value_ptr.*;
             } else {
                 if (nav_gop.found_existing)
@@ -2928,6 +2973,7 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo
             const type_gop = try dwarf.types.getOrPut(dwarf.gpa, nav_val.toIntern());
             if (type_gop.found_existing) {
                 if (dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(type_gop.value_ptr.*).len > 0) break :tag .decl_alias;
+                assert(!nav_gop.found_existing);
                 nav_gop.value_ptr.* = type_gop.value_ptr.*;
             } else {
                 if (nav_gop.found_existing)
@@ -2998,6 +3044,7 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo
             const type_gop = try dwarf.types.getOrPut(dwarf.gpa, nav_val.toIntern());
             if (type_gop.found_existing) {
                 if (dwarf.debug_info.section.getUnit(wip_nav.unit).getEntry(type_gop.value_ptr.*).len > 0) break :tag .decl_alias;
+                assert(!nav_gop.found_existing);
                 nav_gop.value_ptr.* = type_gop.value_ptr.*;
             } else {
                 if (nav_gop.found_existing)
@@ -3164,6 +3211,7 @@ fn updateLazyType(
 ) UpdateError!void {
     const zcu = pt.zcu;
     const ip = &zcu.intern_pool;
+    assert(ip.typeOf(type_index) == .type_type);
     const ty: Type = .fromInterned(type_index);
     switch (type_index) {
         .generic_poison_type => log.debug("updateLazyType({s})", .{"anytype"}),
@@ -3200,6 +3248,10 @@ fn updateLazyType(
     defer dwarf.gpa.free(name);
 
     switch (ip.indexToKey(type_index)) {
+        .undef => {
+            try wip_nav.abbrevCode(.undefined_comptime_value);
+            try wip_nav.refType(.type);
+        },
         .int_type => |int_type| {
             try wip_nav.abbrevCode(.numeric_type);
             try wip_nav.strp(name);
@@ -3633,7 +3685,6 @@ fn updateLazyType(
         },
 
         // values, not types
-        .undef,
         .simple_value,
         .variable,
         .@"extern",
@@ -3666,7 +3717,11 @@ fn updateLazyValue(
 ) UpdateError!void {
     const zcu = pt.zcu;
     const ip = &zcu.intern_pool;
-    log.debug("updateLazyValue({})", .{Value.fromInterned(value_index).fmtValue(pt)});
+    assert(ip.typeOf(value_index) != .type_type);
+    log.debug("updateLazyValue(@as({}, {}))", .{
+        Value.fromInterned(value_index).typeOf(zcu).fmt(pt),
+        Value.fromInterned(value_index).fmtValue(pt),
+    });
     var wip_nav: WipNav = .{
         .dwarf = dwarf,
         .pt = pt,
@@ -3710,9 +3765,8 @@ fn updateLazyValue(
         .inferred_error_set_type,
         => unreachable, // already handled
         .undef => |ty| {
-            try wip_nav.abbrevCode(.aggregate_comptime_value);
+            try wip_nav.abbrevCode(.undefined_comptime_value);
             try wip_nav.refType(.fromInterned(ty));
-            try uleb128(diw, @intFromEnum(AbbrevCode.null));
         },
         .simple_value => unreachable, // opv state
         .variable, .@"extern" => unreachable, // not a value
@@ -4890,8 +4944,17 @@ const AbbrevCode = enum {
     block,
     empty_inlined_func,
     inlined_func,
-    local_arg,
+    arg,
+    comptime_arg,
+    comptime_arg_runtime_bits,
+    comptime_arg_comptime_state,
+    comptime_arg_runtime_bits_comptime_state,
     local_var,
+    local_const,
+    local_const_runtime_bits,
+    local_const_comptime_state,
+    local_const_runtime_bits_comptime_state,
+    undefined_comptime_value,
     data2_comptime_value,
     data4_comptime_value,
     data8_comptime_value,
@@ -5663,7 +5726,7 @@ const AbbrevCode = enum {
                 .{ .high_pc, .data4 },
             },
         },
-        .local_arg = .{
+        .arg = .{
             .tag = .formal_parameter,
             .attrs = &.{
                 .{ .name, .strp },
@@ -5671,6 +5734,42 @@ const AbbrevCode = enum {
                 .{ .location, .exprloc },
             },
         },
+        .comptime_arg = .{
+            .tag = .formal_parameter,
+            .attrs = &.{
+                .{ .const_expr, .flag_present },
+                .{ .name, .strp },
+                .{ .type, .ref_addr },
+            },
+        },
+        .comptime_arg_runtime_bits = .{
+            .tag = .formal_parameter,
+            .attrs = &.{
+                .{ .const_expr, .flag_present },
+                .{ .name, .strp },
+                .{ .type, .ref_addr },
+                .{ .const_value, .block },
+            },
+        },
+        .comptime_arg_comptime_state = .{
+            .tag = .formal_parameter,
+            .attrs = &.{
+                .{ .const_expr, .flag_present },
+                .{ .name, .strp },
+                .{ .type, .ref_addr },
+                .{ .ZIG_comptime_value, .ref_addr },
+            },
+        },
+        .comptime_arg_runtime_bits_comptime_state = .{
+            .tag = .formal_parameter,
+            .attrs = &.{
+                .{ .const_expr, .flag_present },
+                .{ .name, .strp },
+                .{ .type, .ref_addr },
+                .{ .const_value, .block },
+                .{ .ZIG_comptime_value, .ref_addr },
+            },
+        },
         .local_var = .{
             .tag = .variable,
             .attrs = &.{
@@ -5679,6 +5778,44 @@ const AbbrevCode = enum {
                 .{ .location, .exprloc },
             },
         },
+        .local_const = .{
+            .tag = .constant,
+            .attrs = &.{
+                .{ .name, .strp },
+                .{ .type, .ref_addr },
+            },
+        },
+        .local_const_runtime_bits = .{
+            .tag = .constant,
+            .attrs = &.{
+                .{ .name, .strp },
+                .{ .type, .ref_addr },
+                .{ .const_value, .block },
+            },
+        },
+        .local_const_comptime_state = .{
+            .tag = .constant,
+            .attrs = &.{
+                .{ .name, .strp },
+                .{ .type, .ref_addr },
+                .{ .ZIG_comptime_value, .ref_addr },
+            },
+        },
+        .local_const_runtime_bits_comptime_state = .{
+            .tag = .constant,
+            .attrs = &.{
+                .{ .name, .strp },
+                .{ .type, .ref_addr },
+                .{ .const_value, .block },
+                .{ .ZIG_comptime_value, .ref_addr },
+            },
+        },
+        .undefined_comptime_value = .{
+            .tag = .ZIG_comptime_value,
+            .attrs = &.{
+                .{ .type, .ref_addr },
+            },
+        },
         .data2_comptime_value = .{
             .tag = .ZIG_comptime_value,
             .attrs = &.{
src/link/Elf.zig
@@ -1683,12 +1683,11 @@ pub fn updateFunc(
     pt: Zcu.PerThread,
     func_index: InternPool.Index,
     mir: *const codegen.AnyMir,
-    maybe_undef_air: *const Air,
 ) link.File.UpdateNavError!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");
     }
-    return self.zigObjectPtr().?.updateFunc(self, pt, func_index, mir, maybe_undef_air);
+    return self.zigObjectPtr().?.updateFunc(self, pt, func_index, mir);
 }
 
 pub fn updateNav(
@@ -4516,7 +4515,6 @@ const trace = @import("../tracy.zig").trace;
 const synthetic_sections = @import("Elf/synthetic_sections.zig");
 
 const Merge = @import("Elf/Merge.zig");
-const Air = @import("../Air.zig");
 const Archive = @import("Elf/Archive.zig");
 const AtomList = @import("Elf/AtomList.zig");
 const Compilation = @import("../Compilation.zig");
src/link/Goff.zig
@@ -17,7 +17,6 @@ const codegen = @import("../codegen.zig");
 const link = @import("../link.zig");
 const trace = @import("../tracy.zig").trace;
 const build_options = @import("build_options");
-const Air = @import("../Air.zig");
 
 base: link.File,
 
@@ -74,13 +73,11 @@ pub fn updateFunc(
     pt: Zcu.PerThread,
     func_index: InternPool.Index,
     mir: *const codegen.AnyMir,
-    maybe_undef_air: *const Air,
 ) link.File.UpdateNavError!void {
     _ = self;
     _ = pt;
     _ = func_index;
     _ = mir;
-    _ = maybe_undef_air;
     unreachable; // we always use llvm
 }
 
src/link/MachO.zig
@@ -3040,12 +3040,11 @@ pub fn updateFunc(
     pt: Zcu.PerThread,
     func_index: InternPool.Index,
     mir: *const codegen.AnyMir,
-    maybe_undef_air: *const Air,
 ) link.File.UpdateNavError!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");
     }
-    return self.getZigObject().?.updateFunc(self, pt, func_index, mir, maybe_undef_air);
+    return self.getZigObject().?.updateFunc(self, pt, func_index, mir);
 }
 
 pub fn updateNav(self: *MachO, pt: Zcu.PerThread, nav: InternPool.Nav.Index) link.File.UpdateNavError!void {
@@ -5431,7 +5430,6 @@ const target_util = @import("../target.zig");
 const trace = @import("../tracy.zig").trace;
 const synthetic = @import("MachO/synthetic.zig");
 
-const Air = @import("../Air.zig");
 const Alignment = Atom.Alignment;
 const Allocator = mem.Allocator;
 const Archive = @import("MachO/Archive.zig");
src/link/Plan9.zig
@@ -387,9 +387,6 @@ pub fn updateFunc(
     pt: Zcu.PerThread,
     func_index: InternPool.Index,
     mir: *const codegen.AnyMir,
-    /// This may be `undefined`; only pass it to `emitFunction`.
-    /// This parameter will eventually be removed.
-    maybe_undef_air: *const Air,
 ) link.File.UpdateNavError!void {
     if (build_options.skip_non_native and builtin.object_format != .plan9) {
         @panic("Attempted to compile for object format that was disabled by build configuration");
@@ -422,7 +419,6 @@ pub fn updateFunc(
         mir,
         &code_buffer,
         .{ .plan9 = &dbg_info_output },
-        maybe_undef_air,
     );
     const code = try code_buffer.toOwnedSlice(gpa);
     self.getAtomPtr(atom_idx).code = .{
src/link/Wasm.zig
@@ -29,7 +29,6 @@ const leb = std.leb;
 const log = std.log.scoped(.link);
 const mem = std.mem;
 
-const Air = @import("../Air.zig");
 const Mir = @import("../arch/wasm/Mir.zig");
 const CodeGen = @import("../arch/wasm/CodeGen.zig");
 const abi = @import("../arch/wasm/abi.zig");
@@ -3182,14 +3181,12 @@ pub fn updateFunc(
     pt: Zcu.PerThread,
     func_index: InternPool.Index,
     any_mir: *const codegen.AnyMir,
-    maybe_undef_air: *const Air,
 ) !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");
     }
 
     dev.check(.wasm_backend);
-    _ = maybe_undef_air; // we (correctly) do not need this
 
     // This linker implementation only works with codegen backend `.stage2_wasm`.
     const mir = &any_mir.wasm;
src/link/Xcoff.zig
@@ -17,7 +17,6 @@ const codegen = @import("../codegen.zig");
 const link = @import("../link.zig");
 const trace = @import("../tracy.zig").trace;
 const build_options = @import("build_options");
-const Air = @import("../Air.zig");
 
 base: link.File,
 
@@ -74,13 +73,11 @@ pub fn updateFunc(
     pt: Zcu.PerThread,
     func_index: InternPool.Index,
     mir: *const codegen.AnyMir,
-    maybe_undef_air: *const Air,
 ) link.File.UpdateNavError!void {
     _ = self;
     _ = pt;
     _ = func_index;
     _ = mir;
-    _ = maybe_undef_air;
     unreachable; // we always use llvm
 }
 
src/Zcu/PerThread.zig
@@ -2893,17 +2893,10 @@ fn analyzeFnBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaE
         runtime_params_len;
 
     var runtime_param_index: usize = 0;
-    for (fn_info.param_body[0..src_params_len]) |inst| {
+    for (fn_info.param_body[0..src_params_len], 0..) |inst, zir_param_index| {
         const gop = sema.inst_map.getOrPutAssumeCapacity(inst);
         if (gop.found_existing) continue; // provided above by comptime arg
 
-        const param_inst_info = sema.code.instructions.get(@intFromEnum(inst));
-        const param_name: Zir.NullTerminatedString = switch (param_inst_info.tag) {
-            .param_anytype => param_inst_info.data.str_tok.start,
-            .param => sema.code.extraData(Zir.Inst.Param, param_inst_info.data.pl_tok.payload_index).data.name,
-            else => unreachable,
-        };
-
         const param_ty = fn_ty_info.param_types.get(ip)[runtime_param_index];
         runtime_param_index += 1;
 
@@ -2923,10 +2916,7 @@ fn analyzeFnBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaE
             .tag = .arg,
             .data = .{ .arg = .{
                 .ty = Air.internedToRef(param_ty),
-                .name = if (inner_block.ownerModule().strip)
-                    .none
-                else
-                    try sema.appendAirString(sema.code.nullTerminatedString(param_name)),
+                .zir_param_index = @intCast(zir_param_index),
             } },
         });
     }
src/Air.zig
@@ -1153,9 +1153,7 @@ pub const Inst = struct {
         ty: Type,
         arg: struct {
             ty: Ref,
-            /// Index into `extra` of a null-terminated string representing the parameter name.
-            /// This is `.none` if debug info is stripped.
-            name: NullTerminatedString,
+            zir_param_index: u32,
         },
         ty_op: struct {
             ty: Ref,
src/codegen.zig
@@ -180,10 +180,6 @@ pub fn emitFunction(
     any_mir: *const AnyMir,
     code: *std.ArrayListUnmanaged(u8),
     debug_output: link.File.DebugInfoOutput,
-    /// TODO: this parameter needs to be removed. We should not still hold AIR this late
-    /// in the pipeline. Any information needed to call emit must be stored in MIR.
-    /// This is `undefined` if the backend supports the `separate_thread` feature.
-    air: *const Air,
 ) CodeGenError!void {
     const zcu = pt.zcu;
     const func = zcu.funcInfo(func_index);
@@ -199,7 +195,7 @@ pub fn emitFunction(
         => |backend| {
             dev.check(devFeatureForBackend(backend));
             const mir = &@field(any_mir, AnyMir.tag(backend));
-            return mir.emit(lf, pt, src_loc, func_index, code, debug_output, air);
+            return mir.emit(lf, pt, src_loc, func_index, code, debug_output);
         },
     }
 }
src/Compilation.zig
@@ -4589,10 +4589,8 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void {
                 comp.dispatchZcuLinkTask(tid, .{ .link_func = .{
                     .func = func.func,
                     .mir = shared_mir,
-                    .air = undefined,
                 } });
             } else {
-                const emit_needs_air = !zcu.backendSupportsFeature(.separate_thread);
                 {
                     const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));
                     defer pt.deactivate();
@@ -4602,7 +4600,6 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job) JobError!void {
                 comp.dispatchZcuLinkTask(tid, .{ .link_func = .{
                     .func = func.func,
                     .mir = shared_mir,
-                    .air = if (emit_needs_air) &air else undefined,
                 } });
                 air.deinit(gpa);
             }
src/link.zig
@@ -8,7 +8,6 @@ const log = std.log.scoped(.link);
 const trace = @import("tracy.zig").trace;
 const wasi_libc = @import("libs/wasi_libc.zig");
 
-const Air = @import("Air.zig");
 const Allocator = std.mem.Allocator;
 const Cache = std.Build.Cache;
 const Path = std.Build.Cache.Path;
@@ -752,9 +751,6 @@ pub const File = struct {
         /// that `mir.deinit` remains legal for the caller. For instance, the callee can
         /// take ownership of an embedded slice and replace it with `&.{}` in `mir`.
         mir: *codegen.AnyMir,
-        /// This may be `undefined`; only pass it to `emitFunction`.
-        /// This parameter will eventually be removed.
-        maybe_undef_air: *const Air,
     ) UpdateNavError!void {
         assert(base.comp.zcu.?.llvm_object == null);
         switch (base.tag) {
@@ -762,7 +758,7 @@ pub const File = struct {
             .spirv => unreachable, // see corresponding special case in `Zcu.PerThread.runCodegenInner`
             inline else => |tag| {
                 dev.check(tag.devFeature());
-                return @as(*tag.Type(), @fieldParentPtr("base", base)).updateFunc(pt, func_index, mir, maybe_undef_air);
+                return @as(*tag.Type(), @fieldParentPtr("base", base)).updateFunc(pt, func_index, mir);
             },
         }
     }
@@ -1271,11 +1267,6 @@ pub const ZcuTask = union(enum) {
         /// the codegen job to ensure that the linker receives functions in a deterministic order,
         /// allowing reproducible builds.
         mir: *SharedMir,
-        /// This field exists only due to deficiencies in some codegen implementations; it should
-        /// be removed when the corresponding parameter of `CodeGen.emitFunction` can be removed.
-        /// This is `undefined` if `Zcu.Feature.separate_thread` is supported.
-        /// If this is defined, its memory is owned externally; do not `deinit` this `air`.
-        air: *const Air,
 
         pub const SharedMir = struct {
             /// This is initially `.pending`. When `value` is populated, the codegen thread will set
@@ -1458,7 +1449,7 @@ pub fn doZcuTask(comp: *Compilation, tid: usize, task: ZcuTask) void {
             assert(zcu.llvm_object == null); // LLVM codegen doesn't produce MIR
             const mir = &func.mir.value;
             if (comp.bin_file) |lf| {
-                lf.updateFunc(pt, func.func, mir, func.air) catch |err| switch (err) {
+                lf.updateFunc(pt, func.func, mir) catch |err| switch (err) {
                     error.OutOfMemory => return diags.setAllocFailure(),
                     error.CodegenFail => return zcu.assertCodegenFailed(nav),
                     error.Overflow, error.RelocationNotByteAligned => {
src/Sema.zig
@@ -35088,24 +35088,24 @@ pub fn resolveUnionLayout(sema: *Sema, ty: Type) SemaError!void {
     var max_align: Alignment = .@"1";
     for (0..union_type.field_types.len) |field_index| {
         const field_ty: Type = .fromInterned(union_type.field_types.get(ip)[field_index]);
+        if (field_ty.isNoReturn(pt.zcu)) continue;
 
-        if (try field_ty.comptimeOnlySema(pt) or field_ty.zigTypeTag(pt.zcu) == .noreturn) continue; // TODO: should this affect alignment?
-
-        max_size = @max(max_size, field_ty.abiSizeSema(pt) catch |err| switch (err) {
-            error.AnalysisFail => {
-                const msg = sema.err orelse return err;
-                try sema.addFieldErrNote(ty, field_index, msg, "while checking this field", .{});
-                return err;
-            },
-            else => return err,
-        });
+        if (try field_ty.hasRuntimeBitsSema(pt)) {
+            max_size = @max(max_size, field_ty.abiSizeSema(pt) catch |err| switch (err) {
+                error.AnalysisFail => {
+                    const msg = sema.err orelse return err;
+                    try sema.addFieldErrNote(ty, field_index, msg, "while checking this field", .{});
+                    return err;
+                },
+                else => return err,
+            });
+        }
 
         const explicit_align = union_type.fieldAlign(ip, field_index);
         const field_align = if (explicit_align != .none)
             explicit_align
         else
             try field_ty.abiAlignmentSema(pt);
-
         max_align = max_align.max(field_align);
     }
 
src/Type.zig
@@ -177,6 +177,7 @@ pub fn print(ty: Type, writer: anytype, pt: Zcu.PerThread) @TypeOf(writer).Error
     const zcu = pt.zcu;
     const ip = &zcu.intern_pool;
     switch (ip.indexToKey(ty.toIntern())) {
+        .undef => return writer.writeAll("@as(type, undefined)"),
         .int_type => |int_type| {
             const sign_char: u8 = switch (int_type.signedness) {
                 .signed => 'i',
@@ -398,7 +399,6 @@ pub fn print(ty: Type, writer: anytype, pt: Zcu.PerThread) @TypeOf(writer).Error
         },
 
         // values, not types
-        .undef,
         .simple_value,
         .variable,
         .@"extern",
@@ -3921,23 +3921,25 @@ pub fn getUnionLayout(loaded_union: InternPool.LoadedUnionType, zcu: *const Zcu)
     var payload_size: u64 = 0;
     var payload_align: InternPool.Alignment = .@"1";
     for (loaded_union.field_types.get(ip), 0..) |field_ty, field_index| {
-        if (!Type.fromInterned(field_ty).hasRuntimeBitsIgnoreComptime(zcu)) continue;
+        if (Type.fromInterned(field_ty).isNoReturn(zcu)) continue;
 
         const explicit_align = loaded_union.fieldAlign(ip, field_index);
         const field_align = if (explicit_align != .none)
             explicit_align
         else
             Type.fromInterned(field_ty).abiAlignment(zcu);
-        const field_size = Type.fromInterned(field_ty).abiSize(zcu);
-        if (field_size > payload_size) {
-            payload_size = field_size;
-            biggest_field = @intCast(field_index);
-        }
-        if (field_align.compare(.gte, payload_align)) {
-            payload_align = field_align;
-            most_aligned_field = @intCast(field_index);
-            most_aligned_field_size = field_size;
+        if (Type.fromInterned(field_ty).hasRuntimeBits(zcu)) {
+            const field_size = Type.fromInterned(field_ty).abiSize(zcu);
+            if (field_size > payload_size) {
+                payload_size = field_size;
+                biggest_field = @intCast(field_index);
+            }
+            if (field_align.compare(.gte, payload_align)) {
+                most_aligned_field = @intCast(field_index);
+                most_aligned_field_size = field_size;
+            }
         }
+        payload_align = payload_align.max(field_align);
     }
     const have_tag = loaded_union.flagsUnordered(ip).runtime_tag.hasTag();
     if (!have_tag or !Type.fromInterned(loaded_union.enum_tag_ty).hasRuntimeBits(zcu)) {