Commit 62f7276501

Jacob Young <jacobly0@users.noreply.github.com>
2024-08-19 20:56:17
Dwarf: emit info about inline call sites
1 parent ef90eb0
src/Air/types_resolved.zig
@@ -339,6 +339,7 @@ fn checkBody(air: Air, body: []const Air.Inst.Index, zcu: *Zcu) bool {
 
             .dbg_var_ptr,
             .dbg_var_val,
+            .dbg_arg_inline,
             => {
                 if (!checkRef(data.pl_op.operand, zcu)) return false;
             },
src/arch/aarch64/CodeGen.zig
@@ -170,7 +170,9 @@ const DbgInfoReloc = struct {
 
     fn genDbgInfo(reloc: DbgInfoReloc, function: Self) !void {
         switch (reloc.tag) {
-            .arg => try reloc.genArgDbgInfo(function),
+            .arg,
+            .dbg_arg_inline,
+            => try reloc.genArgDbgInfo(function),
 
             .dbg_var_ptr,
             .dbg_var_val,
@@ -201,7 +203,7 @@ const DbgInfoReloc = struct {
                     else => unreachable, // not a possible argument
 
                 };
-                try dw.genVarDebugInfo(.local_arg, reloc.name, reloc.ty, loc);
+                try dw.genLocalDebugInfo(.local_arg, reloc.name, reloc.ty, loc);
             },
             .plan9 => {},
             .none => {},
@@ -237,7 +239,7 @@ const DbgInfoReloc = struct {
                         break :blk .empty;
                     },
                 };
-                try dwarf.genVarDebugInfo(.local_var, reloc.name, reloc.ty, loc);
+                try dwarf.genLocalDebugInfo(.local_var, reloc.name, reloc.ty, loc);
             },
             .plan9 => {},
             .none => {},
@@ -799,6 +801,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .dbg_inline_block => try self.airDbgInlineBlock(inst),
             .dbg_var_ptr,
             .dbg_var_val,
+            .dbg_arg_inline,
             => try self.airDbgVar(inst),
 
             .call              => try self.airCall(inst, .auto),
@@ -4220,17 +4223,13 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
 
     const ty = self.typeOfIndex(inst);
     const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)];
-
-    const name_nts = self.air.instructions.items(.data)[@intFromEnum(inst)].arg.name;
-    if (name_nts != .none) {
-        const name = self.air.nullTerminatedString(@intFromEnum(name_nts));
-        try self.dbg_info_relocs.append(self.gpa, .{
-            .tag = tag,
-            .ty = ty,
-            .name = name,
-            .mcv = self.args[arg_index],
-        });
-    }
+    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 result: MCValue = if (self.liveness.isUnused(inst)) .dead else self.args[arg_index];
     return self.finishAir(inst, result, .{ .none, .none, .none });
@@ -4644,14 +4643,14 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
     const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)];
     const ty = self.typeOf(operand);
     const mcv = try self.resolveInst(operand);
-    const name = self.air.nullTerminatedString(pl_op.payload);
+    const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload);
 
     log.debug("airDbgVar: %{d}: {}, {}", .{ inst, ty.fmtDebug(), mcv });
 
     try self.dbg_info_relocs.append(self.gpa, .{
         .tag = tag,
         .ty = ty,
-        .name = name,
+        .name = name.toSlice(self.air),
         .mcv = mcv,
     });
 
src/arch/arm/CodeGen.zig
@@ -248,7 +248,9 @@ const DbgInfoReloc = struct {
 
     fn genDbgInfo(reloc: DbgInfoReloc, function: Self) !void {
         switch (reloc.tag) {
-            .arg => try reloc.genArgDbgInfo(function),
+            .arg,
+            .dbg_arg_inline,
+            => try reloc.genArgDbgInfo(function),
 
             .dbg_var_ptr,
             .dbg_var_val,
@@ -279,7 +281,7 @@ const DbgInfoReloc = struct {
                     else => unreachable, // not a possible argument
                 };
 
-                try dw.genVarDebugInfo(.local_arg, reloc.name, reloc.ty, loc);
+                try dw.genLocalDebugInfo(.local_arg, reloc.name, reloc.ty, loc);
             },
             .plan9 => {},
             .none => {},
@@ -315,7 +317,7 @@ const DbgInfoReloc = struct {
                         break :blk .empty;
                     },
                 };
-                try dw.genVarDebugInfo(.local_var, reloc.name, reloc.ty, loc);
+                try dw.genLocalDebugInfo(.local_var, reloc.name, reloc.ty, loc);
             },
             .plan9 => {},
             .none => {},
@@ -786,6 +788,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .dbg_inline_block => try self.airDbgInlineBlock(inst),
             .dbg_var_ptr,
             .dbg_var_val,
+            .dbg_arg_inline,
             => try self.airDbgVar(inst),
 
             .call              => try self.airCall(inst, .auto),
@@ -4199,16 +4202,13 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
     const ty = self.typeOfIndex(inst);
     const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)];
 
-    const name_nts = self.air.instructions.items(.data)[@intFromEnum(inst)].arg.name;
-    if (name_nts != .none) {
-        const name = self.air.nullTerminatedString(@intFromEnum(name_nts));
-        try self.dbg_info_relocs.append(self.gpa, .{
-            .tag = tag,
-            .ty = ty,
-            .name = name,
-            .mcv = self.args[arg_index],
-        });
-    }
+    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 result: MCValue = if (self.liveness.isUnused(inst)) .dead else self.args[arg_index];
     return self.finishAir(inst, result, .{ .none, .none, .none });
@@ -4612,14 +4612,14 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
     const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)];
     const ty = self.typeOf(operand);
     const mcv = try self.resolveInst(operand);
-    const name = self.air.nullTerminatedString(pl_op.payload);
+    const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload);
 
     log.debug("airDbgVar: %{d}: {}, {}", .{ inst, ty.fmtDebug(), mcv });
 
     try self.dbg_info_relocs.append(self.gpa, .{
         .tag = tag,
         .ty = ty,
-        .name = name,
+        .name = name.toSlice(self.air),
         .mcv = mcv,
     });
 
src/arch/riscv64/CodeGen.zig
@@ -1644,6 +1644,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {
 
             .dbg_var_ptr,
             .dbg_var_val,
+            .dbg_arg_inline,
             => try func.airDbgVar(inst),
 
             .dbg_inline_block => try func.airDbgInlineBlock(inst),
@@ -4673,11 +4674,15 @@ fn genArgDbgInfo(func: Func, inst: Air.Inst.Index, mcv: MCValue) !void {
     const arg = func.air.instructions.items(.data)[@intFromEnum(inst)].arg;
     const ty = arg.ty.toType();
     if (arg.name == .none) return;
-    const name = func.air.nullTerminatedString(@intFromEnum(arg.name));
 
     switch (func.debug_output) {
         .dwarf => |dw| switch (mcv) {
-            .register => |reg| try dw.genVarDebugInfo(.local_arg, name, ty, .{ .reg = reg.dwarfNum() }),
+            .register => |reg| try dw.genLocalDebugInfo(
+                .local_arg,
+                arg.name.toSlice(func.air),
+                ty,
+                .{ .reg = reg.dwarfNum() },
+            ),
             .load_frame => {},
             else => {},
         },
@@ -5179,16 +5184,17 @@ fn airDbgVar(func: *Func, inst: Air.Inst.Index) !void {
     const operand = pl_op.operand;
     const ty = func.typeOf(operand);
     const mcv = try func.resolveInst(operand);
+    const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload);
 
-    const name = func.air.nullTerminatedString(pl_op.payload);
-
-    try func.genVarDbgInfo(ty, mcv, name);
+    const tag = func.air.instructions.items(.tag)[@intFromEnum(inst)];
+    try func.genVarDbgInfo(tag, ty, mcv, name.toSlice(func.air));
 
     return func.finishAir(inst, .unreach, .{ operand, .none, .none });
 }
 
 fn genVarDbgInfo(
     func: Func,
+    tag: Air.Inst.Tag,
     ty: Type,
     mcv: MCValue,
     name: []const u8,
@@ -5205,7 +5211,11 @@ fn genVarDbgInfo(
                     break :blk .empty;
                 },
             };
-            try dwarf.genVarDebugInfo(.local_var, name, ty, loc);
+            try dwarf.genLocalDebugInfo(switch (tag) {
+                else => unreachable,
+                .dbg_var_ptr, .dbg_var_val => .local_var,
+                .dbg_arg_inline => .local_arg,
+            }, name, ty, loc);
         },
         .plan9 => {},
         .none => {},
src/arch/sparc64/CodeGen.zig
@@ -643,6 +643,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .dbg_inline_block => try self.airDbgInlineBlock(inst),
             .dbg_var_ptr,
             .dbg_var_val,
+            .dbg_arg_inline,
             => try self.airDbgVar(inst),
 
             .call              => try self.airCall(inst, .auto),
@@ -1662,7 +1663,7 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
 
 fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
     const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
-    const name = self.air.nullTerminatedString(pl_op.payload);
+    const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload);
     const operand = pl_op.operand;
     // TODO emit debug info for this variable
     _ = name;
@@ -3582,13 +3583,15 @@ 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;
-    const name = self.air.nullTerminatedString(@intFromEnum(arg.name));
 
     switch (self.debug_output) {
         .dwarf => |dw| switch (mcv) {
-            .register => |reg| try dw.genVarDebugInfo(.local_arg, name, ty, .{
-                .reg = reg.dwarfNum(),
-            }),
+            .register => |reg| try dw.genLocalDebugInfo(
+                .local_arg,
+                arg.name.toSlice(self.air),
+                ty,
+                .{ .reg = reg.dwarfNum() },
+            ),
             else => {},
         },
         else => {},
src/arch/wasm/CodeGen.zig
@@ -1917,8 +1917,9 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
 
         .dbg_stmt => func.airDbgStmt(inst),
         .dbg_inline_block => func.airDbgInlineBlock(inst),
-        .dbg_var_ptr => func.airDbgVar(inst, true),
-        .dbg_var_val => func.airDbgVar(inst, false),
+        .dbg_var_ptr => func.airDbgVar(inst, .local_var, true),
+        .dbg_var_val => func.airDbgVar(inst, .local_var, false),
+        .dbg_arg_inline => func.airDbgVar(inst, .local_arg, false),
 
         .call => func.airCall(inst, .auto),
         .call_always_tail => func.airCall(inst, .always_tail),
@@ -2585,13 +2586,13 @@ fn airArg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
 
     switch (func.debug_output) {
         .dwarf => |dwarf| {
-            const name_nts = func.air.instructions.items(.data)[@intFromEnum(inst)].arg.name;
-            if (name_nts != .none) {
-                const name = func.air.nullTerminatedString(@intFromEnum(name_nts));
-                try dwarf.genVarDebugInfo(.local_arg, name, arg_ty, .{
-                    .wasm_ext = .{ .local = arg.local.value },
-                });
-            }
+            const name = func.air.instructions.items(.data)[@intFromEnum(inst)].arg.name;
+            if (name != .none) try dwarf.genLocalDebugInfo(
+                .local_arg,
+                name.toSlice(func.air),
+                arg_ty,
+                .{ .wasm_ext = .{ .local = arg.local.value } },
+            );
         },
         else => {},
     }
@@ -6454,7 +6455,12 @@ fn airDbgInlineBlock(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
     try func.lowerBlock(inst, ty_pl.ty.toType(), @ptrCast(func.air.extra[extra.end..][0..extra.data.body_len]));
 }
 
-fn airDbgVar(func: *CodeGen, inst: Air.Inst.Index, is_ptr: bool) InnerError!void {
+fn airDbgVar(
+    func: *CodeGen,
+    inst: Air.Inst.Index,
+    local_tag: link.File.Dwarf.WipNav.LocalTag,
+    is_ptr: bool,
+) InnerError!void {
     _ = is_ptr;
     if (func.debug_output != .dwarf) return func.finishAir(inst, .none, &.{});
 
@@ -6464,8 +6470,8 @@ fn airDbgVar(func: *CodeGen, inst: Air.Inst.Index, is_ptr: bool) InnerError!void
 
     log.debug("airDbgVar: %{d}: {}, {}", .{ inst, ty.fmtDebug(), operand });
 
-    const name = func.air.nullTerminatedString(pl_op.payload);
-    log.debug(" var name = ({s})", .{name});
+    const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload);
+    log.debug(" var name = ({s})", .{name.toSlice(func.air)});
 
     const loc: link.File.Dwarf.Loc = switch (operand) {
         .local => |local| .{ .wasm_ext = .{ .local = local.value } },
@@ -6474,7 +6480,7 @@ fn airDbgVar(func: *CodeGen, inst: Air.Inst.Index, is_ptr: bool) InnerError!void
             break :blk .empty;
         },
     };
-    try func.debug_output.dwarf.genVarDebugInfo(.local_var, name, ty, loc);
+    try func.debug_output.dwarf.genLocalDebugInfo(local_tag, name.toSlice(func.air), ty, loc);
 
     return func.finishAir(inst, .none, &.{});
 }
src/arch/x86_64/CodeGen.zig
@@ -81,9 +81,6 @@ mir_instructions: std.MultiArrayList(Mir.Inst) = .{},
 /// MIR extra data
 mir_extra: std.ArrayListUnmanaged(u32) = .{},
 
-stack_args: std.ArrayListUnmanaged(StackVar) = .{},
-stack_vars: std.ArrayListUnmanaged(StackVar) = .{},
-
 /// Byte offset within the source file of the ending curly.
 end_di_line: u32,
 end_di_column: u32,
@@ -728,12 +725,6 @@ const InstTracking = struct {
     }
 };
 
-const StackVar = struct {
-    name: []const u8,
-    type: Type,
-    frame_addr: FrameAddr,
-};
-
 const FrameAlloc = struct {
     abi_size: u31,
     spill_pad: u3,
@@ -839,8 +830,6 @@ pub fn generate(
         function.exitlude_jump_relocs.deinit(gpa);
         function.mir_instructions.deinit(gpa);
         function.mir_extra.deinit(gpa);
-        function.stack_args.deinit(gpa);
-        function.stack_vars.deinit(gpa);
     }
 
     wip_mir_log.debug("{}:", .{fmtNav(func.owner_nav, ip)});
@@ -913,9 +902,6 @@ pub fn generate(
         else => |e| return e,
     };
 
-    try function.genStackVarDebugInfo(.local_arg, function.stack_args.items);
-    try function.genStackVarDebugInfo(.local_var, function.stack_vars.items);
-
     var mir: Mir = .{
         .instructions = function.mir_instructions.toOwnedSlice(),
         .extra = try function.mir_extra.toOwnedSlice(gpa),
@@ -924,6 +910,7 @@ pub fn generate(
     defer mir.deinit(gpa);
 
     var emit: Emit = .{
+        .air = function.air,
         .lower = .{
             .bin_file = bin_file,
             .allocator = gpa,
@@ -1013,14 +1000,15 @@ pub fn generateLazy(
         else => |e| return e,
     };
 
-    var mir = Mir{
+    var mir: Mir = .{
         .instructions = function.mir_instructions.toOwnedSlice(),
         .extra = try function.mir_extra.toOwnedSlice(gpa),
         .frame_locs = function.frame_locs.toOwnedSlice(),
     };
     defer mir.deinit(gpa);
 
-    var emit = Emit{
+    var emit: Emit = .{
+        .air = function.air,
         .lower = .{
             .bin_file = bin_file,
             .allocator = gpa,
@@ -1116,7 +1104,7 @@ fn formatWipMir(
 ) @TypeOf(writer).Error!void {
     const comp = data.self.bin_file.comp;
     const mod = comp.root_mod;
-    var lower = Lower{
+    var lower: Lower = .{
         .bin_file = data.self.bin_file,
         .allocator = data.self.gpa,
         .mir = .{
@@ -1357,6 +1345,56 @@ fn asmPlaceholder(self: *Self) !Mir.Inst.Index {
     });
 }
 
+const MirTagAir = enum { dbg_local };
+
+fn asmAir(self: *Self, 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: *Self, tag: MirTagAir, inst: Air.Inst.Index, imm: Immediate) !void {
+    const ops: Mir.Inst.Ops, const i: u32 = switch (imm) {
+        .signed => |s| .{ switch (tag) {
+            .dbg_local => .pseudo_dbg_local_ai_s,
+        }, @bitCast(s) },
+        .unsigned => |u| if (math.cast(u32, u)) |small|
+            .{ switch (tag) {
+                .dbg_local => .pseudo_dbg_local_ai_u,
+            }, small }
+        else
+            .{ switch (tag) {
+                .dbg_local => .pseudo_dbg_local_ai_64,
+            }, try self.addExtra(Mir.Imm64.encode(u)) },
+        .reloc => unreachable,
+    };
+    _ = try self.addInst(.{
+        .tag = .pseudo,
+        .ops = ops,
+        .data = .{ .ai = .{
+            .air_inst = inst,
+            .i = i,
+        } },
+    });
+}
+
+fn asmAirMemory(self: *Self, 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: *Self, tag: Mir.Inst.FixedTag) !void {
     _ = try self.addInst(.{
         .tag = tag[1],
@@ -1424,31 +1462,22 @@ fn asmRegisterRegister(self: *Self, tag: Mir.Inst.FixedTag, reg1: Register, reg2
 }
 
 fn asmRegisterImmediate(self: *Self, tag: Mir.Inst.FixedTag, reg: Register, imm: Immediate) !void {
-    const ops: Mir.Inst.Ops = switch (imm) {
-        .signed => .ri_s,
-        .unsigned => |u| if (math.cast(u32, u)) |_| .ri_u else .ri64,
+    const ops: Mir.Inst.Ops, const i: u32 = switch (imm) {
+        .signed => |s| .{ .ri_s, @bitCast(s) },
+        .unsigned => |u| if (math.cast(u32, u)) |small|
+            .{ .ri_u, small }
+        else
+            .{ .ri_64, try self.addExtra(Mir.Imm64.encode(imm.unsigned)) },
         .reloc => unreachable,
     };
     _ = try self.addInst(.{
         .tag = tag[1],
         .ops = ops,
-        .data = switch (ops) {
-            .ri_s, .ri_u => .{ .ri = .{
-                .fixes = tag[0],
-                .r1 = reg,
-                .i = switch (imm) {
-                    .signed => |s| @bitCast(s),
-                    .unsigned => |u| @intCast(u),
-                    .reloc => unreachable,
-                },
-            } },
-            .ri64 => .{ .rx = .{
-                .fixes = tag[0],
-                .r1 = reg,
-                .payload = try self.addExtra(Mir.Imm64.encode(imm.unsigned)),
-            } },
-            else => unreachable,
-        },
+        .data = .{ .ri = .{
+            .fixes = tag[0],
+            .r1 = reg,
+            .i = i,
+        } },
     });
 }
 
@@ -2158,6 +2187,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .dbg_inline_block => try self.airDbgInlineBlock(inst),
             .dbg_var_ptr,
             .dbg_var_val,
+            .dbg_arg_inline,
             => try self.airDbgVar(inst),
 
             .call              => try self.airCall(inst, .auto),
@@ -11951,87 +11981,59 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
 fn airDbgArg(self: *Self, inst: Air.Inst.Index) !void {
     defer self.finishAirBookkeeping();
     if (self.debug_output == .none) return;
-    const name_nts = self.air.instructions.items(.data)[@intFromEnum(inst)].arg.name;
-    const name = self.air.nullTerminatedString(@intFromEnum(name_nts));
-    if (name.len > 0) {
-        const arg_ty = self.typeOfIndex(inst);
-        const arg_mcv = self.getResolvedInstValue(inst).short;
-        try self.genVarDebugInfo(.local_arg, .dbg_var_val, name, arg_ty, arg_mcv);
-    }
+    const name = self.air.instructions.items(.data)[@intFromEnum(inst)].arg.name;
+    if (name != .none) try self.genLocalDebugInfo(inst, self.getResolvedInstValue(inst).short);
     if (self.liveness.isUnused(inst)) try self.processDeath(inst);
 }
 
-fn genVarDebugInfo(
+fn genLocalDebugInfo(
     self: *Self,
-    var_tag: link.File.Dwarf.WipNav.VarTag,
-    tag: Air.Inst.Tag,
-    name: []const u8,
-    ty: Type,
+    inst: Air.Inst.Index,
     mcv: MCValue,
 ) !void {
-    const stack_vars = switch (var_tag) {
-        .local_arg => &self.stack_args,
-        .local_var => &self.stack_vars,
-    };
-    switch (self.debug_output) {
-        .dwarf => |dwarf| switch (tag) {
-            else => unreachable,
-            .dbg_var_ptr => {
-                const var_ty = ty.childType(self.pt.zcu);
-                switch (mcv) {
-                    else => {
-                        log.info("dbg_var_ptr({s}({}))", .{ @tagName(mcv), mcv });
-                        unreachable;
-                    },
-                    .unreach, .dead, .elementwise_regs_then_frame, .reserved_frame, .air_ref => unreachable,
-                    .lea_frame => |frame_addr| try stack_vars.append(self.gpa, .{
-                        .name = name,
-                        .type = var_ty,
-                        .frame_addr = frame_addr,
-                    }),
-                    .lea_symbol => |sym_off| try dwarf.genVarDebugInfo(var_tag, name, var_ty, .{ .plus = .{
-                        &.{ .addr = .{ .sym = sym_off.sym } },
-                        &.{ .consts = sym_off.off },
-                    } }),
-                }
-            },
-            .dbg_var_val => switch (mcv) {
-                .none => try dwarf.genVarDebugInfo(var_tag, name, ty, .empty),
+    if (self.debug_output == .none) return;
+    switch (self.air.instructions.items(.tag)[@intFromEnum(inst)]) {
+        else => unreachable,
+        .arg, .dbg_arg_inline, .dbg_var_val => |tag| {
+            switch (mcv) {
+                .none => try self.asmAir(.dbg_local, inst),
                 .unreach, .dead, .elementwise_regs_then_frame, .reserved_frame, .air_ref => unreachable,
-                .immediate => |immediate| try dwarf.genVarDebugInfo(var_tag, name, ty, .{ .stack_value = &.{
-                    .constu = immediate,
-                } }),
+                .immediate => |imm| try self.asmAirImmediate(.dbg_local, inst, Immediate.u(imm)),
                 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(FrameAlloc.initSpill(ty, self.pt));
                     try self.genSetMem(.{ .frame = frame_index }, 0, ty, mcv, .{});
-                    try stack_vars.append(self.gpa, .{
-                        .name = name,
-                        .type = ty,
-                        .frame_addr = .{ .index = frame_index },
+                    try self.asmAirMemory(.dbg_local, inst, .{
+                        .base = .{ .frame = frame_index },
+                        .mod = .{ .rm = .{ .size = .qword } },
                     });
                 },
-            },
+            }
         },
-        .plan9 => {},
-        .none => {},
-    }
-}
-
-fn genStackVarDebugInfo(
-    self: Self,
-    var_tag: link.File.Dwarf.WipNav.VarTag,
-    stack_vars: []const StackVar,
-) !void {
-    switch (self.debug_output) {
-        .dwarf => |dwarf| for (stack_vars) |stack_var| {
-            const frame_loc = self.frame_locs.get(@intFromEnum(stack_var.frame_addr.index));
-            try dwarf.genVarDebugInfo(var_tag, stack_var.name, stack_var.type, .{ .plus = .{
-                &.{ .breg = frame_loc.base.dwarfNum() },
-                &.{ .consts = @as(i33, frame_loc.disp) + stack_var.frame_addr.off },
-            } });
+        .dbg_var_ptr => switch (mcv) {
+            else => unreachable,
+            .unreach, .dead, .elementwise_regs_then_frame, .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,
+                } },
+            }),
+            .lea_symbol => |sym_off| try self.asmAirMemory(.dbg_local, inst, .{
+                .base = .{ .reloc = .{ .atom_index = undefined, .sym_index = sym_off.sym } },
+                .mod = .{ .rm = .{
+                    .size = .qword,
+                    .disp = sym_off.off,
+                } },
+            }),
         },
-        .plan9 => {},
-        .none => {},
     }
 }
 
@@ -13060,29 +13062,21 @@ fn airDbgInlineBlock(self: *Self, inst: Air.Inst.Index) !void {
     self.inline_func = extra.data.func;
     _ = try self.addInst(.{
         .tag = .pseudo,
-        .ops = .pseudo_dbg_inline_func,
+        .ops = .pseudo_dbg_enter_inline_func,
         .data = .{ .func = extra.data.func },
     });
     try self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]));
     _ = try self.addInst(.{
         .tag = .pseudo,
-        .ops = .pseudo_dbg_inline_func,
+        .ops = .pseudo_dbg_leave_inline_func,
         .data = .{ .func = old_inline_func },
     });
 }
 
 fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
     const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
-    const operand = pl_op.operand;
-    const ty = self.typeOf(operand);
-    const mcv = try self.resolveInst(operand);
-
-    const name = self.air.nullTerminatedString(pl_op.payload);
-
-    const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)];
-    try self.genVarDebugInfo(.local_var, tag, name, ty, mcv);
-
-    return self.finishAir(inst, .unreach, .{ operand, .none, .none });
+    try self.genLocalDebugInfo(inst, try self.resolveInst(pl_op.operand));
+    return self.finishAir(inst, .unreach, .{ pl_op.operand, .none, .none });
 }
 
 fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !Mir.Inst.Index {
src/arch/x86_64/Emit.zig
@@ -1,5 +1,6 @@
 //! This file contains the functionality for emitting x86_64 MIR as machine code
 
+air: Air,
 lower: Lower,
 debug_output: DebugInfoOutput,
 code: *std.ArrayList(u8),
@@ -232,13 +233,96 @@ pub fn emitMir(emit: *Emit) Error!void {
                             .none => {},
                         }
                     },
-                    .pseudo_dbg_inline_func => {
+                    .pseudo_dbg_enter_inline_func => {
                         switch (emit.debug_output) {
                             .dwarf => |dw| {
-                                log.debug("mirDbgInline (line={d}, col={d})", .{
+                                log.debug("mirDbgEnterInline (line={d}, col={d})", .{
                                     emit.prev_di_line, emit.prev_di_column,
                                 });
-                                try dw.setInlineFunc(mir_inst.data.func);
+                                try dw.enterInlineFunc(mir_inst.data.func, emit.code.items.len, emit.prev_di_line, emit.prev_di_column);
+                            },
+                            .plan9 => {},
+                            .none => {},
+                        }
+                    },
+                    .pseudo_dbg_leave_inline_func => {
+                        switch (emit.debug_output) {
+                            .dwarf => |dw| {
+                                log.debug("mirDbgLeaveInline (line={d}, col={d})", .{
+                                    emit.prev_di_line, emit.prev_di_column,
+                                });
+                                try dw.leaveInlineFunc(mir_inst.data.func, 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_am,
+                    => {
+                        switch (emit.debug_output) {
+                            .dwarf => |dw| {
+                                var loc_buf: [2]link.File.Dwarf.Loc = undefined;
+                                const air_inst_index, const loc: link.File.Dwarf.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)) {
+                                            .signed => |s| .{ .consts = s },
+                                            .unsigned => |u| .{ .constu = u },
+                                        };
+                                        break :stack_value &loc_buf[0];
+                                    } } },
+                                    .pseudo_dbg_local_am => loc: {
+                                        const mem = emit.lower.mem(mir_inst.data.ax.payload);
+                                        break :loc .{ mir_inst.data.ax.air_inst, .{ .plus = .{
+                                            base: {
+                                                loc_buf[0] = switch (mem.base()) {
+                                                    .none => .{ .constu = 0 },
+                                                    .reg => |reg| .{ .breg = reg.dwarfNum() },
+                                                    .frame => unreachable,
+                                                    .reloc => |reloc| .{ .addr = .{ .sym = reloc.sym_index } },
+                                                };
+                                                break :base &loc_buf[0];
+                                            },
+                                            disp: {
+                                                loc_buf[1] = switch (mem.disp()) {
+                                                    .signed => |s| .{ .consts = s },
+                                                    .unsigned => |u| .{ .constu = u },
+                                                };
+                                                break :disp &loc_buf[1];
+                                            },
+                                        } } };
+                                    },
+                                };
+                                const ip = &emit.lower.bin_file.comp.module.?.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 dw.genLocalDebugInfo(
+                                    switch (air_inst.tag) {
+                                        else => unreachable,
+                                        .arg, .dbg_arg_inline => .local_arg,
+                                        .dbg_var_ptr, .dbg_var_val => .local_var,
+                                    },
+                                    name.toSlice(emit.air),
+                                    switch (air_inst.tag) {
+                                        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),
+                                    },
+                                    loc,
+                                );
                             },
                             .plan9 => {},
                             .none => {},
@@ -285,7 +369,7 @@ fn fixupRelocs(emit: *Emit) Error!void {
         const target = emit.code_offset_mapping.get(reloc.target) orelse
             return emit.fail("JMP/CALL relocation target not found!", .{});
         const disp = @as(i64, @intCast(target)) - @as(i64, @intCast(reloc.source + reloc.length));
-        mem.writeInt(i32, emit.code.items[reloc.offset..][0..4], @intCast(disp), .little);
+        std.mem.writeInt(i32, emit.code.items[reloc.offset..][0..4], @intCast(disp), .little);
     }
 }
 
@@ -340,9 +424,9 @@ fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) Error!void {
 
 const link = @import("../../link.zig");
 const log = std.log.scoped(.emit);
-const mem = std.mem;
 const std = @import("std");
 
+const Air = @import("../../Air.zig");
 const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
 const Emit = @This();
 const Lower = @import("Lower.zig");
src/arch/x86_64/encoder.zig
@@ -128,8 +128,8 @@ pub const Instruction = struct {
             } };
         }
 
-        pub fn rip(ptr_size: PtrSize, disp: i32) Memory {
-            return .{ .rip = .{ .ptr_size = ptr_size, .disp = disp } };
+        pub fn rip(ptr_size: PtrSize, displacement: i32) Memory {
+            return .{ .rip = .{ .ptr_size = ptr_size, .disp = displacement } };
         }
 
         pub fn isSegmentRegister(mem: Memory) bool {
@@ -158,6 +158,14 @@ pub const Instruction = struct {
             };
         }
 
+        pub fn disp(mem: Memory) Immediate {
+            return switch (mem) {
+                .sib => |s| Immediate.s(s.disp),
+                .rip => |r| Immediate.s(r.disp),
+                .moffs => |m| Immediate.u(m.offset),
+            };
+        }
+
         pub fn bitSize(mem: Memory) u64 {
             return switch (mem) {
                 .rip => |r| r.ptr_size.bitSize(),
src/arch/x86_64/Lower.zig
@@ -4,10 +4,10 @@ bin_file: *link.File,
 output_mode: std.builtin.OutputMode,
 link_mode: std.builtin.LinkMode,
 pic: bool,
-allocator: Allocator,
+allocator: std.mem.Allocator,
 mir: Mir,
 cc: std.builtin.CallingConvention,
-err_msg: ?*ErrorMsg = null,
+err_msg: ?*Zcu.ErrorMsg = null,
 src_loc: Zcu.LazySrcLoc,
 result_insts_len: u8 = undefined,
 result_relocs_len: u8 = undefined,
@@ -267,7 +267,13 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
             .pseudo_dbg_prologue_end_none,
             .pseudo_dbg_line_line_column,
             .pseudo_dbg_epilogue_begin_none,
-            .pseudo_dbg_inline_func,
+            .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_am,
             .pseudo_dead_none,
             => {},
             else => unreachable,
@@ -283,17 +289,18 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
 pub fn fail(lower: *Lower, comptime format: []const u8, args: anytype) Error {
     @setCold(true);
     assert(lower.err_msg == null);
-    lower.err_msg = try ErrorMsg.create(lower.allocator, lower.src_loc, format, args);
+    lower.err_msg = try Zcu.ErrorMsg.create(lower.allocator, lower.src_loc, format, args);
     return error.LowerFail;
 }
 
-fn imm(lower: Lower, ops: Mir.Inst.Ops, i: u32) Immediate {
+pub fn imm(lower: Lower, ops: Mir.Inst.Ops, i: u32) Immediate {
     return switch (ops) {
         .rri_s,
         .ri_s,
         .i_s,
         .mi_s,
         .rmi_s,
+        .pseudo_dbg_local_ai_s,
         => Immediate.s(@bitCast(i)),
 
         .rrri,
@@ -306,15 +313,18 @@ fn imm(lower: Lower, ops: Mir.Inst.Ops, i: u32) Immediate {
         .mri,
         .rrm,
         .rrmi,
+        .pseudo_dbg_local_ai_u,
         => Immediate.u(i),
 
-        .ri64 => Immediate.u(lower.mir.extraData(Mir.Imm64, i).data.decode()),
+        .ri_64,
+        .pseudo_dbg_local_ai_64,
+        => Immediate.u(lower.mir.extraData(Mir.Imm64, i).data.decode()),
 
         else => unreachable,
     };
 }
 
-fn mem(lower: Lower, payload: u32) Memory {
+pub fn mem(lower: Lower, payload: u32) Memory {
     return lower.mir.resolveFrameLoc(lower.mir.extraData(Mir.Memory, payload).data).decode();
 }
 
@@ -490,8 +500,8 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void {
         .rrrr => inst.data.rrrr.fixes,
         .rrri => inst.data.rrri.fixes,
         .rri_s, .rri_u => inst.data.rri.fixes,
-        .ri_s, .ri_u => inst.data.ri.fixes,
-        .ri64, .rm, .rmi_s, .mr => inst.data.rx.fixes,
+        .ri_s, .ri_u, .ri_64 => inst.data.ri.fixes,
+        .rm, .rmi_s, .mr => inst.data.rx.fixes,
         .mrr, .rrm, .rmr => inst.data.rrx.fixes,
         .rmi, .mri => inst.data.rix.fixes,
         .rrmr => inst.data.rrrx.fixes,
@@ -554,14 +564,10 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void {
             .{ .reg = inst.data.rrri.r3 },
             .{ .imm = lower.imm(inst.ops, inst.data.rrri.i) },
         },
-        .ri_s, .ri_u => &.{
+        .ri_s, .ri_u, .ri_64 => &.{
             .{ .reg = inst.data.ri.r1 },
             .{ .imm = lower.imm(inst.ops, inst.data.ri.i) },
         },
-        .ri64 => &.{
-            .{ .reg = inst.data.rx.r1 },
-            .{ .imm = lower.imm(inst.ops, inst.data.rx.payload) },
-        },
         .rri_s, .rri_u => &.{
             .{ .reg = inst.data.rri.r1 },
             .{ .reg = inst.data.rri.r2 },
@@ -670,9 +676,6 @@ const encoder = @import("encoder.zig");
 const link = @import("../../link.zig");
 const std = @import("std");
 
-const Air = @import("../../Air.zig");
-const Allocator = std.mem.Allocator;
-const ErrorMsg = Zcu.ErrorMsg;
 const Immediate = Instruction.Immediate;
 const Instruction = encoder.Instruction;
 const Lower = @This();
src/arch/x86_64/Mir.zig
@@ -760,8 +760,8 @@ pub const Inst = struct {
         /// Uses `ri` payload.
         ri_u,
         /// Register, 64-bit unsigned immediate operands.
-        /// Uses `rx` payload with payload type `Imm64`.
-        ri64,
+        /// Uses `ri` payload with `i` index of extra data of type `Imm64`.
+        ri_64,
         /// Immediate (sign-extended) operand.
         /// Uses `imm` payload.
         i_s,
@@ -796,7 +796,7 @@ pub const Inst = struct {
         /// Uses `rrix` payload with extra data of type `Memory`.
         rrmi,
         /// Single memory operand.
-        /// Uses `x` with extra data of type `Memory`.
+        /// Uses `x` payload with extra data of type `Memory`.
         m,
         /// Memory, immediate (sign-extend) operands.
         /// Uses `x` payload with extra data of type `Imm32` followed by `Memory`.
@@ -868,16 +868,16 @@ pub const Inst = struct {
         pseudo_j_nz_or_p_inst,
 
         /// Probe alignment
-        /// Uses `ri` payload
+        /// Uses `ri` payload.
         pseudo_probe_align_ri_s,
         /// Probe adjust unrolled
-        /// Uses `ri` payload
+        /// Uses `ri` payload.
         pseudo_probe_adjust_unrolled_ri_s,
         /// Probe adjust setup
-        /// Uses `rri` payload
+        /// Uses `rri` payload.
         pseudo_probe_adjust_setup_rri_s,
         /// Probe adjust loop
-        /// Uses `rr` payload
+        /// Uses `rr` payload.
         pseudo_probe_adjust_loop_rr,
         /// Push registers
         /// Uses `reg_list` payload.
@@ -893,8 +893,25 @@ pub const Inst = struct {
         pseudo_dbg_line_line_column,
         /// Start of epilogue
         pseudo_dbg_epilogue_begin_none,
-        /// Start or end of inline function
-        pseudo_dbg_inline_func,
+        /// Start of inline function
+        pseudo_dbg_enter_inline_func,
+        /// End of inline function
+        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 `ax` payload with extra data of type `Imm64`.
+        pseudo_dbg_local_ai_64,
+        /// Local argument or variable.
+        /// Uses `ax` payload with extra data of type `Memory`.
+        pseudo_dbg_local_am,
 
         /// Tombstone
         /// Emitter should skip this instruction.
@@ -997,6 +1014,17 @@ pub const Inst = struct {
             fixes: Fixes = ._,
             payload: u32,
         },
+        a: struct {
+            air_inst: Air.Inst.Index,
+        },
+        ai: struct {
+            air_inst: Air.Inst.Index,
+            i: u32,
+        },
+        ax: struct {
+            air_inst: Air.Inst.Index,
+            payload: u32,
+        },
         /// Relocation for the linker where:
         /// * `atom_index` is the index of the source
         /// * `sym_index` is the index of the target
@@ -1225,6 +1253,7 @@ 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/c.zig
@@ -3293,7 +3293,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
 
             .dbg_stmt => try airDbgStmt(f, inst),
             .dbg_inline_block => try airDbgInlineBlock(f, inst),
-            .dbg_var_ptr, .dbg_var_val => try airDbgVar(f, inst),
+            .dbg_var_ptr, .dbg_var_val, .dbg_arg_inline => try airDbgVar(f, inst),
 
             .call              => try airCall(f, inst, .auto),
             .call_always_tail  => .none,
@@ -4590,14 +4590,15 @@ fn airDbgInlineBlock(f: *Function, inst: Air.Inst.Index) !CValue {
 fn airDbgVar(f: *Function, inst: Air.Inst.Index) !CValue {
     const pt = f.object.dg.pt;
     const zcu = pt.zcu;
+    const tag = f.air.instructions.items(.tag)[@intFromEnum(inst)];
     const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
-    const name = f.air.nullTerminatedString(pl_op.payload);
+    const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload);
     const operand_is_undef = if (try f.air.value(pl_op.operand, pt)) |v| v.isUndefDeep(zcu) else false;
     if (!operand_is_undef) _ = try f.resolveInst(pl_op.operand);
 
     try reap(f, inst, &.{pl_op.operand});
     const writer = f.object.writer();
-    try writer.print("/* var:{s} */\n", .{name});
+    try writer.print("/* {s}:{s} */\n", .{ @tagName(tag), name.toSlice(f.air) });
     return .none;
 }
 
src/codegen/llvm.zig
@@ -1665,6 +1665,7 @@ pub const Object = struct {
             .ret_ptr = ret_ptr,
             .args = args.items,
             .arg_index = 0,
+            .arg_inline_index = 0,
             .func_inst_table = .{},
             .blocks = .{},
             .sync_scope = if (owner_mod.single_threaded) .singlethread else .system,
@@ -4769,7 +4770,8 @@ pub const FuncGen = struct {
     /// it omits 0-bit types. If the function uses sret as the first parameter,
     /// this slice does not include it.
     args: []const Builder.Value,
-    arg_index: usize,
+    arg_index: u32,
+    arg_inline_index: u32,
 
     err_ret_trace: Builder.Value = .none,
 
@@ -5082,7 +5084,8 @@ pub const FuncGen = struct {
                 .dbg_stmt => try self.airDbgStmt(inst),
                 .dbg_inline_block => try self.airDbgInlineBlock(inst),
                 .dbg_var_ptr => try self.airDbgVarPtr(inst),
-                .dbg_var_val => try self.airDbgVarVal(inst),
+                .dbg_var_val => try self.airDbgVarVal(inst, false),
+                .dbg_arg_inline => try self.airDbgVarVal(inst, true),
 
                 .c_va_arg => try self.airCVaArg(inst),
                 .c_va_copy => try self.airCVaCopy(inst),
@@ -6677,6 +6680,7 @@ pub const FuncGen = struct {
     fn airDbgInlineBlock(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value {
         const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
         const extra = self.air.extraData(Air.DbgInlineBlock, ty_pl.payload);
+        self.arg_inline_index = 0;
         return self.lowerBlock(inst, extra.data.func, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]));
     }
 
@@ -6685,11 +6689,11 @@ pub const FuncGen = struct {
         const mod = o.pt.zcu;
         const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
         const operand = try self.resolveInst(pl_op.operand);
-        const name = self.air.nullTerminatedString(pl_op.payload);
+        const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload);
         const ptr_ty = self.typeOf(pl_op.operand);
 
         const debug_local_var = try o.builder.debugLocalVar(
-            try o.builder.metadataString(name),
+            try o.builder.metadataString(name.toSlice(self.air)),
             self.file,
             self.scope,
             self.prev_dbg_line,
@@ -6712,15 +6716,25 @@ pub const FuncGen = struct {
         return .none;
     }
 
-    fn airDbgVarVal(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value {
+    fn airDbgVarVal(self: *FuncGen, inst: Air.Inst.Index, is_arg: bool) !Builder.Value {
         const o = self.ng.object;
         const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
         const operand = try self.resolveInst(pl_op.operand);
         const operand_ty = self.typeOf(pl_op.operand);
-        const name = self.air.nullTerminatedString(pl_op.payload);
+        const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload);
 
-        const debug_local_var = try o.builder.debugLocalVar(
-            try o.builder.metadataString(name),
+        const debug_local_var = if (is_arg) try o.builder.debugParameter(
+            try o.builder.metadataString(name.toSlice(self.air)),
+            self.file,
+            self.scope,
+            self.prev_dbg_line,
+            try o.lowerDebugType(operand_ty),
+            arg_no: {
+                self.arg_inline_index += 1;
+                break :arg_no self.arg_inline_index;
+            },
+        ) else try o.builder.debugLocalVar(
+            try o.builder.metadataString(name.toSlice(self.air)),
             self.file,
             self.scope,
             self.prev_dbg_line,
@@ -8835,12 +8849,12 @@ pub const FuncGen = struct {
         const lbrace_col = func.lbrace_column + 1;
 
         const debug_parameter = try o.builder.debugParameter(
-            try o.builder.metadataString(self.air.nullTerminatedString(@intFromEnum(name))),
+            try o.builder.metadataString(name.toSlice(self.air)),
             self.file,
             self.scope,
             lbrace_line,
             try o.lowerDebugType(inst_ty),
-            @intCast(self.arg_index),
+            self.arg_index,
         );
 
         const old_location = self.wip.debug_location;
src/codegen/spirv.zig
@@ -6366,8 +6366,8 @@ const NavGen = struct {
     fn airDbgVar(self: *NavGen, inst: Air.Inst.Index) !void {
         const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
         const target_id = try self.resolve(pl_op.operand);
-        const name = self.air.nullTerminatedString(pl_op.payload);
-        try self.spv.debugName(target_id, name);
+        const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload);
+        try self.spv.debugName(target_id, name.toSlice(self.air));
     }
 
     fn airAssembly(self: *NavGen, inst: Air.Inst.Index) !?IdRef {
src/link/Dwarf.zig
@@ -986,7 +986,9 @@ pub const WipNav = struct {
     entry: Entry.Index,
     any_children: bool,
     func: InternPool.Index,
+    func_sym_index: u32,
     func_high_reloc: u32,
+    inlined_funcs_high_reloc: std.ArrayListUnmanaged(u32),
     debug_info: std.ArrayListUnmanaged(u8),
     debug_line: std.ArrayListUnmanaged(u8),
     debug_loclists: std.ArrayListUnmanaged(u8),
@@ -994,6 +996,7 @@ pub const WipNav = struct {
 
     pub fn deinit(wip_nav: *WipNav) void {
         const gpa = wip_nav.dwarf.gpa;
+        if (wip_nav.func != .none) wip_nav.inlined_funcs_high_reloc.deinit(gpa);
         wip_nav.debug_info.deinit(gpa);
         wip_nav.debug_line.deinit(gpa);
         wip_nav.debug_loclists.deinit(gpa);
@@ -1004,10 +1007,10 @@ pub const WipNav = struct {
         return wip_nav.debug_info.writer(wip_nav.dwarf.gpa);
     }
 
-    pub const VarTag = enum { local_arg, local_var };
-    pub fn genVarDebugInfo(
+    pub const LocalTag = enum { local_arg, local_var };
+    pub fn genLocalDebugInfo(
         wip_nav: *WipNav,
-        tag: VarTag,
+        tag: LocalTag,
         name: []const u8,
         ty: Type,
         loc: Loc,
@@ -1078,7 +1081,45 @@ pub const WipNav = struct {
         try dlw.writeByte(DW.LNS.set_epilogue_begin);
     }
 
+    pub fn enterInlineFunc(wip_nav: *WipNav, func: InternPool.Index, code_off: u64, line: u32, column: u32) UpdateError!void {
+        const dwarf = wip_nav.dwarf;
+        const zcu = wip_nav.pt.zcu;
+        const diw = wip_nav.debug_info.writer(dwarf.gpa);
+        try wip_nav.inlined_funcs_high_reloc.ensureUnusedCapacity(dwarf.gpa, 1);
+
+        const external_relocs = &dwarf.debug_info.section.getUnit(wip_nav.unit).external_relocs;
+        try external_relocs.ensureUnusedCapacity(dwarf.gpa, 2);
+        try uleb128(diw, @intFromEnum(AbbrevCode.inlined_func));
+        try wip_nav.refNav(zcu.funcInfo(func).owner_nav);
+        try uleb128(diw, zcu.navSrcLine(zcu.funcInfo(wip_nav.func).owner_nav) + line + 1);
+        try uleb128(diw, column);
+        external_relocs.appendAssumeCapacity(.{
+            .source_entry = wip_nav.entry,
+            .source_off = @intCast(wip_nav.debug_info.items.len),
+            .target_sym = wip_nav.func_sym_index,
+            .target_off = code_off,
+        });
+        try diw.writeByteNTimes(0, @intFromEnum(dwarf.address_size));
+        wip_nav.inlined_funcs_high_reloc.appendAssumeCapacity(@intCast(external_relocs.items.len));
+        external_relocs.appendAssumeCapacity(.{
+            .source_entry = wip_nav.entry,
+            .source_off = @intCast(wip_nav.debug_info.items.len),
+            .target_sym = wip_nav.func_sym_index,
+            .target_off = undefined,
+        });
+        try diw.writeByteNTimes(0, @intFromEnum(dwarf.address_size));
+        try wip_nav.setInlineFunc(func);
+    }
+
+    pub fn leaveInlineFunc(wip_nav: *WipNav, func: InternPool.Index, code_off: u64) UpdateError!void {
+        const external_relocs = &wip_nav.dwarf.debug_info.section.getUnit(wip_nav.unit).external_relocs;
+        external_relocs.items[wip_nav.inlined_funcs_high_reloc.pop()].target_off = code_off;
+        try uleb128(wip_nav.debug_info.writer(wip_nav.dwarf.gpa), @intFromEnum(AbbrevCode.null));
+        try wip_nav.setInlineFunc(func);
+    }
+
     pub fn setInlineFunc(wip_nav: *WipNav, func: InternPool.Index) UpdateError!void {
+        wip_nav.any_children = true;
         const zcu = wip_nav.pt.zcu;
         const dwarf = wip_nav.dwarf;
         if (wip_nav.func == func) return;
@@ -1217,6 +1258,15 @@ pub const WipNav = struct {
         try wip_nav.infoSectionOffset(.debug_info, unit, entry, 0);
     }
 
+    fn refNav(wip_nav: *WipNav, nav_index: InternPool.Nav.Index) UpdateError!void {
+        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_gop = try wip_nav.dwarf.navs.getOrPut(wip_nav.dwarf.gpa, nav_index);
+        if (!nav_gop.found_existing) nav_gop.value_ptr.* = try wip_nav.dwarf.addCommonEntry(unit);
+        try wip_nav.infoSectionOffset(.debug_info, unit, nav_gop.value_ptr.*, 0);
+    }
+
     fn refForward(wip_nav: *WipNav) std.mem.Allocator.Error!u32 {
         const dwarf = wip_nav.dwarf;
         const cross_entry_relocs = &dwarf.debug_info.section.getUnit(wip_nav.unit).cross_entry_relocs;
@@ -1554,7 +1604,9 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In
         .entry = nav_gop.value_ptr.*,
         .any_children = false,
         .func = .none,
+        .func_sym_index = undefined,
         .func_high_reloc = undefined,
+        .inlined_funcs_high_reloc = undefined,
         .debug_info = .{},
         .debug_line = .{},
         .debug_loclists = .{},
@@ -1694,6 +1746,8 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In
 
             const func_type = ip.indexToKey(func.ty).func_type;
             wip_nav.func = nav_val.toIntern();
+            wip_nav.func_sym_index = sym_index;
+            wip_nav.inlined_funcs_high_reloc = .{};
 
             const diw = wip_nav.debug_info.writer(dwarf.gpa);
             try uleb128(diw, @intFromEnum(AbbrevCode.decl_func));
@@ -1706,17 +1760,19 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In
             try wip_nav.strp(nav.fqn.toSlice(ip));
             try wip_nav.refType(Type.fromInterned(func_type.return_type));
             const external_relocs = &dwarf.debug_info.section.getUnit(unit).external_relocs;
-            try external_relocs.append(dwarf.gpa, .{
+            try external_relocs.ensureUnusedCapacity(dwarf.gpa, 2);
+            external_relocs.appendAssumeCapacity(.{
                 .source_entry = wip_nav.entry,
                 .source_off = @intCast(wip_nav.debug_info.items.len),
                 .target_sym = sym_index,
             });
             try diw.writeByteNTimes(0, @intFromEnum(dwarf.address_size));
             wip_nav.func_high_reloc = @intCast(external_relocs.items.len);
-            try external_relocs.append(dwarf.gpa, .{
+            external_relocs.appendAssumeCapacity(.{
                 .source_entry = wip_nav.entry,
                 .source_off = @intCast(wip_nav.debug_info.items.len),
                 .target_sym = sym_index,
+                .target_off = undefined,
             });
             try diw.writeByteNTimes(0, @intFromEnum(dwarf.address_size));
             try uleb128(diw, nav.status.resolved.alignment.toByteUnits() orelse
@@ -1779,7 +1835,8 @@ pub fn finishWipNav(
     log.debug("finishWipNav({})", .{nav.fqn.fmt(ip)});
 
     if (wip_nav.func != .none) {
-        dwarf.debug_info.section.getUnit(wip_nav.unit).external_relocs.items[wip_nav.func_high_reloc].target_off = sym.size;
+        const external_relocs = &dwarf.debug_info.section.getUnit(wip_nav.unit).external_relocs;
+        external_relocs.items[wip_nav.func_high_reloc].target_off = sym.size;
         if (wip_nav.any_children) {
             const diw = wip_nav.debug_info.writer(dwarf.gpa);
             try uleb128(diw, @intFromEnum(AbbrevCode.null));
@@ -1864,7 +1921,9 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool
         .entry = undefined,
         .any_children = false,
         .func = .none,
+        .func_sym_index = undefined,
         .func_high_reloc = undefined,
+        .inlined_funcs_high_reloc = undefined,
         .debug_info = .{},
         .debug_line = .{},
         .debug_loclists = .{},
@@ -1875,6 +1934,40 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool
     const nav_gop = try dwarf.navs.getOrPut(dwarf.gpa, nav_index);
     errdefer _ = dwarf.navs.pop();
     switch (ip.indexToKey(nav_val.toIntern())) {
+        .func => |func| {
+            const parent_type, const accessibility: u8 = if (nav.analysis_owner.unwrap()) |cau| parent: {
+                const parent_namespace_ptr = ip.namespacePtr(ip.getCau(cau).namespace);
+                break :parent .{
+                    parent_namespace_ptr.owner_type,
+                    if (parent_namespace_ptr.pub_decls.containsContext(nav_index, .{ .zcu = zcu }))
+                        DW.ACCESS.public
+                    else if (parent_namespace_ptr.priv_decls.containsContext(nav_index, .{ .zcu = zcu }))
+                        DW.ACCESS.private
+                    else
+                        unreachable,
+                };
+            } else .{ zcu.fileRootType(inst_info.file), DW.ACCESS.private };
+
+            if (!nav_gop.found_existing) nav_gop.value_ptr.* = try dwarf.addCommonEntry(unit);
+            wip_nav.entry = nav_gop.value_ptr.*;
+
+            const func_type = ip.indexToKey(func.ty).func_type;
+            const diw = wip_nav.debug_info.writer(dwarf.gpa);
+            try uleb128(diw, @intFromEnum(@as(AbbrevCode, if (func_type.param_types.len > 0 and func_type.is_var_args) .decl_func_generic else .decl_func_generic_empty)));
+            try wip_nav.refType(Type.fromInterned(parent_type));
+            assert(wip_nav.debug_info.items.len == DebugInfo.declEntryLineOff(dwarf));
+            try diw.writeInt(u32, @intCast(loc.line + 1), dwarf.endian);
+            try uleb128(diw, loc.column + 1);
+            try diw.writeByte(accessibility);
+            try wip_nav.strp(nav.name.toSlice(ip));
+            try wip_nav.refType(Type.fromInterned(func_type.return_type));
+            for (0..func_type.param_types.len) |param_index| {
+                try uleb128(diw, @intFromEnum(AbbrevCode.func_type_param));
+                try wip_nav.refType(Type.fromInterned(func_type.param_types.get(ip)[param_index]));
+            }
+            if (func_type.is_var_args) try uleb128(diw, @intFromEnum(AbbrevCode.is_var_args));
+            try uleb128(diw, @intFromEnum(AbbrevCode.null));
+        },
         .struct_type => done: {
             const loaded_struct = ip.loadStructType(nav_val.toIntern());
 
@@ -2277,7 +2370,9 @@ fn updateType(
         .entry = dwarf.types.get(type_index).?,
         .any_children = false,
         .func = .none,
+        .func_sym_index = undefined,
         .func_high_reloc = undefined,
+        .inlined_funcs_high_reloc = undefined,
         .debug_info = .{},
         .debug_line = .{},
         .debug_loclists = .{},
@@ -2678,7 +2773,9 @@ pub fn updateContainerType(dwarf: *Dwarf, pt: Zcu.PerThread, type_index: InternP
             .entry = type_gop.value_ptr.*,
             .any_children = false,
             .func = .none,
+            .func_sym_index = undefined,
             .func_high_reloc = undefined,
+            .inlined_funcs_high_reloc = undefined,
             .debug_info = .{},
             .debug_line = .{},
             .debug_loclists = .{},
@@ -2739,7 +2836,9 @@ pub fn updateContainerType(dwarf: *Dwarf, pt: Zcu.PerThread, type_index: InternP
             .entry = type_gop.value_ptr.*,
             .any_children = false,
             .func = .none,
+            .func_sym_index = undefined,
             .func_high_reloc = undefined,
+            .inlined_funcs_high_reloc = undefined,
             .debug_info = .{},
             .debug_line = .{},
             .debug_loclists = .{},
@@ -2913,7 +3012,9 @@ pub fn flushModule(dwarf: *Dwarf, pt: Zcu.PerThread) FlushError!void {
             .entry = entry,
             .any_children = false,
             .func = .none,
+            .func_sym_index = undefined,
             .func_high_reloc = undefined,
+            .inlined_funcs_high_reloc = undefined,
             .debug_info = .{},
             .debug_line = .{},
             .debug_loclists = .{},
@@ -3283,6 +3384,8 @@ const AbbrevCode = enum(u8) {
     decl_var,
     decl_func,
     decl_func_empty,
+    decl_func_generic,
+    decl_func_generic_empty,
     // the rest are unrestricted
     compile_unit,
     module,
@@ -3317,10 +3420,11 @@ const AbbrevCode = enum(u8) {
     struct_type,
     packed_struct_type,
     union_type,
+    inlined_func,
     local_arg,
     local_var,
 
-    const decl_bytes = uleb128Bytes(@intFromEnum(AbbrevCode.decl_func_empty));
+    const decl_bytes = uleb128Bytes(@intFromEnum(AbbrevCode.decl_func_generic_empty));
 
     const Attr = struct {
         DeclValEnum(DW.AT),
@@ -3424,6 +3528,19 @@ const AbbrevCode = enum(u8) {
                 .{ .noreturn, .flag },
             },
         },
+        .decl_func_generic = .{
+            .tag = .subprogram,
+            .children = true,
+            .attrs = decl_abbrev_common_attrs ++ .{
+                .{ .type, .ref_addr },
+            },
+        },
+        .decl_func_generic_empty = .{
+            .tag = .subprogram,
+            .attrs = decl_abbrev_common_attrs ++ .{
+                .{ .type, .ref_addr },
+            },
+        },
         .compile_unit = .{
             .tag = .compile_unit,
             .children = true,
@@ -3679,6 +3796,17 @@ const AbbrevCode = enum(u8) {
                 .{ .alignment, .udata },
             },
         },
+        .inlined_func = .{
+            .tag = .inlined_subroutine,
+            .children = true,
+            .attrs = &.{
+                .{ .abstract_origin, .ref_addr },
+                .{ .call_line, .udata },
+                .{ .call_column, .udata },
+                .{ .low_pc, .addr },
+                .{ .high_pc, .addr },
+            },
+        },
         .local_arg = .{
             .tag = .formal_parameter,
             .attrs = &.{
src/Liveness/Verify.zig
@@ -157,6 +157,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
             },
             .dbg_var_ptr,
             .dbg_var_val,
+            .dbg_arg_inline,
             .wasm_memory_grow,
             => {
                 const pl_op = data[@intFromEnum(inst)].pl_op;
src/Zcu/PerThread.zig
@@ -1241,7 +1241,10 @@ fn semaCau(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) !SemaCauResult {
     }
 
     const nav_already_populated, const queue_linker_work = switch (ip.indexToKey(decl_val.toIntern())) {
-        .func => |f| .{ f.owner_nav == nav_index, false },
+        .func => |f| status: {
+            const func_type = ip.indexToKey(f.ty).func_type;
+            break :status .{ f.owner_nav == nav_index, func_type.is_generic or func_type.cc == .Inline };
+        },
         .variable => |v| .{ false, v.owner_nav == nav_index },
         .@"extern" => .{ false, false },
         else => .{ false, true },
@@ -2158,7 +2161,7 @@ fn analyzeFnBody(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaError!
                 .name = if (inner_block.ownerModule().strip)
                     .none
                 else
-                    @enumFromInt(try sema.appendAirString(sema.code.nullTerminatedString(param_name))),
+                    try sema.appendAirString(sema.code.nullTerminatedString(param_name)),
             } },
         });
     }
src/Air.zig
@@ -456,6 +456,8 @@ pub const Inst = struct {
         /// Same as `dbg_var_ptr` except the local is a const, not a var, and the
         /// operand is the local's value.
         dbg_var_val,
+        /// Same as `dbg_var_val` except the local is an inline function argument.
+        dbg_arg_inline,
         /// ?T => bool
         /// Result type is always bool.
         /// Uses the `un_op` field.
@@ -1022,10 +1024,7 @@ pub const Inst = struct {
             ty: Ref,
             /// Index into `extra` of a null-terminated string representing the parameter name.
             /// This is `.none` if debug info is stripped.
-            name: enum(u32) {
-                none = std.math.maxInt(u32),
-                _,
-            },
+            name: NullTerminatedString,
         },
         ty_op: struct {
             ty: Ref,
@@ -1440,6 +1439,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool)
         .dbg_stmt,
         .dbg_var_ptr,
         .dbg_var_val,
+        .dbg_arg_inline,
         .store,
         .store_safe,
         .fence,
@@ -1562,14 +1562,16 @@ pub fn value(air: Air, inst: Inst.Ref, pt: Zcu.PerThread) !?Value {
     return air.typeOfIndex(index, &pt.zcu.intern_pool).onePossibleValue(pt);
 }
 
-pub fn nullTerminatedString(air: Air, index: usize) [:0]const u8 {
-    const bytes = std.mem.sliceAsBytes(air.extra[index..]);
-    var end: usize = 0;
-    while (bytes[end] != 0) {
-        end += 1;
+pub const NullTerminatedString = enum(u32) {
+    none = std.math.maxInt(u32),
+    _,
+
+    pub fn toSlice(nts: NullTerminatedString, air: Air) [:0]const u8 {
+        if (nts == .none) return "";
+        const bytes = std.mem.sliceAsBytes(air.extra[@intFromEnum(nts)..]);
+        return bytes[0..std.mem.indexOfScalar(u8, bytes, 0).? :0];
     }
-    return bytes[0..end :0];
-}
+};
 
 /// Returns whether the given instruction must always be lowered, for instance
 /// because it can cause side effects. If an instruction does not need to be
@@ -1596,6 +1598,7 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool {
         .dbg_inline_block,
         .dbg_var_ptr,
         .dbg_var_val,
+        .dbg_arg_inline,
         .ret,
         .ret_safe,
         .ret_load,
src/Liveness.zig
@@ -464,6 +464,7 @@ pub fn categorizeOperand(
 
         .dbg_var_ptr,
         .dbg_var_val,
+        .dbg_arg_inline,
         => {
             const o = air_datas[@intFromEnum(inst)].pl_op.operand;
             if (o == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none);
@@ -1097,6 +1098,7 @@ fn analyzeInst(
 
         .dbg_var_ptr,
         .dbg_var_val,
+        .dbg_arg_inline,
         => {
             const operand = inst_datas[@intFromEnum(inst)].pl_op.operand;
             return analyzeOperands(a, pass, data, inst, .{ operand, .none, .none });
src/print_air.zig
@@ -283,6 +283,7 @@ const Writer = struct {
 
             .dbg_var_ptr,
             .dbg_var_val,
+            .dbg_arg_inline,
             => try w.writeDbgVar(s, inst),
 
             .struct_field_ptr => try w.writeStructField(s, inst),
@@ -358,10 +359,7 @@ const Writer = struct {
         try w.writeType(s, arg.ty.toType());
         switch (arg.name) {
             .none => {},
-            _ => {
-                const name = w.air.nullTerminatedString(@intFromEnum(arg.name));
-                try s.print(", \"{}\"", .{std.zig.fmtEscapes(name)});
-            },
+            _ => try s.print(", \"{}\"", .{std.zig.fmtEscapes(arg.name.toSlice(w.air))}),
         }
     }
 
@@ -686,8 +684,8 @@ const Writer = struct {
     fn writeDbgVar(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
         const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
         try w.writeOperand(s, inst, 0, pl_op.operand);
-        const name = w.air.nullTerminatedString(pl_op.payload);
-        try s.print(", \"{}\"", .{std.zig.fmtEscapes(name)});
+        const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload);
+        try s.print(", \"{}\"", .{std.zig.fmtEscapes(name.toSlice(w.air))});
     }
 
     fn writeCall(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
src/Sema.zig
@@ -376,7 +376,7 @@ pub const Block = struct {
 
     c_import_buf: ?*std.ArrayList(u8) = null,
 
-    /// If not `null`, this boolean is set when a `dbg_var_ptr` or `dbg_var_val`
+    /// If not `null`, this boolean is set when a `dbg_var_ptr`, `dbg_var_val`, or `dbg_arg_inline`.
     /// instruction is emitted. It signals that the innermost lexically
     /// enclosing `block`/`block_inline` should be translated into a real AIR
     /// `block` in order for codegen to match lexical scoping for debug vars.
@@ -6567,7 +6567,7 @@ fn addDbgVar(
     const operand_ty = sema.typeOf(operand);
     const val_ty = switch (air_tag) {
         .dbg_var_ptr => operand_ty.childType(mod),
-        .dbg_var_val => operand_ty,
+        .dbg_var_val, .dbg_arg_inline => operand_ty,
         else => unreachable,
     };
     if (try sema.typeRequiresComptime(val_ty)) return;
@@ -6586,25 +6586,26 @@ fn addDbgVar(
     if (block.need_debug_scope) |ptr| ptr.* = true;
 
     // Add the name to the AIR.
-    const name_extra_index = try sema.appendAirString(name);
+    const name_nts = try sema.appendAirString(name);
 
     _ = try block.addInst(.{
         .tag = air_tag,
         .data = .{ .pl_op = .{
-            .payload = name_extra_index,
+            .payload = @intFromEnum(name_nts),
             .operand = operand,
         } },
     });
 }
 
-pub fn appendAirString(sema: *Sema, str: []const u8) Allocator.Error!u32 {
-    const str_extra_index: u32 = @intCast(sema.air_extra.items.len);
+pub fn appendAirString(sema: *Sema, str: []const u8) Allocator.Error!Air.NullTerminatedString {
+    if (str.len == 0) return .none;
+    const nts: Air.NullTerminatedString = @enumFromInt(sema.air_extra.items.len);
     const elements_used = str.len / 4 + 1;
     const elements = try sema.air_extra.addManyAsSlice(sema.gpa, elements_used);
     const buffer = mem.sliceAsBytes(elements);
     @memcpy(buffer[0..str.len], str);
     buffer[str.len] = 0;
-    return str_extra_index;
+    return nts;
 }
 
 fn zirDeclRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -7748,14 +7749,14 @@ fn analyzeCall(
                         const param_name = sema.code.nullTerminatedString(extra.data.name);
                         const inst = sema.inst_map.get(param).?;
 
-                        try sema.addDbgVar(&child_block, inst, .dbg_var_val, param_name);
+                        try sema.addDbgVar(&child_block, inst, .dbg_arg_inline, param_name);
                     },
                     .param_anytype, .param_anytype_comptime => {
                         const inst_data = sema.code.instructions.items(.data)[@intFromEnum(param)].str_tok;
                         const param_name = inst_data.get(sema.code);
                         const inst = sema.inst_map.get(param).?;
 
-                        try sema.addDbgVar(&child_block, inst, .dbg_var_val, param_name);
+                        try sema.addDbgVar(&child_block, inst, .dbg_arg_inline, param_name);
                     },
                     else => continue,
                 };
@@ -8266,7 +8267,7 @@ fn instantiateGenericCall(
                     .name = if (child_block.ownerModule().strip)
                         .none
                     else
-                        @enumFromInt(try sema.appendAirString(fn_zir.nullTerminatedString(param_name))),
+                        try sema.appendAirString(fn_zir.nullTerminatedString(param_name)),
                 } },
             }));
             try child_block.params.append(sema.arena, .{