Commit aa688567f5

Jacob Young <jacobly0@users.noreply.github.com>
2024-03-02 19:49:20
Air: replace `.dbg_inline_*` with `.dbg_inline_block`
This prevents the possibility of not emitting a `.dbg_inline_end` instruction and reduces the allocation requirements of the backends. Closes #19093
1 parent 671c2ac
src/arch/aarch64/CodeGen.zig
@@ -747,7 +747,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .frame_addr      => try self.airFrameAddress(inst),
             .fence           => try self.airFence(),
             .cond_br         => try self.airCondBr(inst),
-            .dbg_stmt        => try self.airDbgStmt(inst),
             .fptrunc         => try self.airFptrunc(inst),
             .fpext           => try self.airFpext(inst),
             .intcast         => try self.airIntCast(inst),
@@ -805,14 +804,12 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .@"try"          => try self.airTry(inst),
             .try_ptr         => try self.airTryPtr(inst),
 
+            .dbg_stmt         => try self.airDbgStmt(inst),
+            .dbg_inline_block => try self.airDbgInlineBlock(inst),
             .dbg_var_ptr,
             .dbg_var_val,
             => try self.airDbgVar(inst),
 
-            .dbg_inline_begin,
-            .dbg_inline_end,
-            => try self.airDbgInline(inst),
-
             .call              => try self.airCall(inst, .auto),
             .call_always_tail  => try self.airCall(inst, .always_tail),
             .call_never_tail   => try self.airCall(inst, .never_tail),
@@ -4621,13 +4618,14 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
     return self.finishAirBookkeeping();
 }
 
-fn airDbgInline(self: *Self, inst: Air.Inst.Index) !void {
-    const ty_fn = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_fn;
+fn airDbgInlineBlock(self: *Self, inst: Air.Inst.Index) !void {
     const mod = self.bin_file.comp.module.?;
-    const func = mod.funcInfo(ty_fn.func);
+    const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
+    const extra = self.air.extraData(Air.DbgInlineBlock, ty_pl.payload);
+    const func = mod.funcInfo(extra.data.func);
     // TODO emit debug info for function change
     _ = func;
-    return self.finishAir(inst, .dead, .{ .none, .none, .none });
+    try self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]));
 }
 
 fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
@@ -5042,6 +5040,12 @@ fn jump(self: *Self, inst: Mir.Inst.Index) !void {
 }
 
 fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
+    const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
+    const extra = self.air.extraData(Air.Block, ty_pl.payload);
+    try self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]));
+}
+
+fn lowerBlock(self: *Self, inst: Air.Inst.Index, body: []const Air.Inst.Index) !void {
     try self.blocks.putNoClobber(self.gpa, inst, .{
         // A block is a setup to be able to jump to the end.
         .relocs = .{},
@@ -5054,9 +5058,6 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
     });
     defer self.blocks.getPtr(inst).?.relocs.deinit(self.gpa);
 
-    const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
-    const extra = self.air.extraData(Air.Block, ty_pl.payload);
-    const body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]);
     // TODO emit debug info lexical block
     try self.genBody(body);
 
src/arch/arm/CodeGen.zig
@@ -733,7 +733,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .frame_addr      => try self.airFrameAddress(inst),
             .fence           => try self.airFence(),
             .cond_br         => try self.airCondBr(inst),
-            .dbg_stmt        => try self.airDbgStmt(inst),
             .fptrunc         => try self.airFptrunc(inst),
             .fpext           => try self.airFpext(inst),
             .intcast         => try self.airIntCast(inst),
@@ -791,14 +790,12 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .@"try"          => try self.airTry(inst),
             .try_ptr         => try self.airTryPtr(inst),
 
+            .dbg_stmt         => try self.airDbgStmt(inst),
+            .dbg_inline_block => try self.airDbgInlineBlock(inst),
             .dbg_var_ptr,
             .dbg_var_val,
             => try self.airDbgVar(inst),
 
-            .dbg_inline_begin,
-            .dbg_inline_end,
-            => try self.airDbgInline(inst),
-
             .call              => try self.airCall(inst, .auto),
             .call_always_tail  => try self.airCall(inst, .always_tail),
             .call_never_tail   => try self.airCall(inst, .never_tail),
@@ -4574,13 +4571,14 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
     return self.finishAirBookkeeping();
 }
 
-fn airDbgInline(self: *Self, inst: Air.Inst.Index) !void {
-    const ty_fn = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_fn;
+fn airDbgInlineBlock(self: *Self, inst: Air.Inst.Index) !void {
     const mod = self.bin_file.comp.module.?;
-    const func = mod.funcInfo(ty_fn.func);
+    const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
+    const extra = self.air.extraData(Air.DbgInlineBlock, ty_pl.payload);
+    const func = mod.funcInfo(extra.data.func);
     // TODO emit debug info for function change
     _ = func;
-    return self.finishAir(inst, .dead, .{ .none, .none, .none });
+    try self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]));
 }
 
 fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
@@ -4973,6 +4971,12 @@ fn jump(self: *Self, inst: Mir.Inst.Index) !void {
 }
 
 fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
+    const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
+    const extra = self.air.extraData(Air.Block, ty_pl.payload);
+    try self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]));
+}
+
+fn lowerBlock(self: *Self, inst: Air.Inst.Index, body: []const Air.Inst.Index) !void {
     try self.blocks.putNoClobber(self.gpa, inst, .{
         // A block is a setup to be able to jump to the end.
         .relocs = .{},
@@ -4985,9 +4989,6 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
     });
     defer self.blocks.getPtr(inst).?.relocs.deinit(self.gpa);
 
-    const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
-    const extra = self.air.extraData(Air.Block, ty_pl.payload);
-    const body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]);
     // TODO emit debug info lexical block
     try self.genBody(body);
 
src/arch/riscv64/CodeGen.zig
@@ -566,7 +566,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .frame_addr      => try self.airFrameAddress(inst),
             .fence           => try self.airFence(),
             .cond_br         => try self.airCondBr(inst),
-            .dbg_stmt        => try self.airDbgStmt(inst),
             .fptrunc         => try self.airFptrunc(inst),
             .fpext           => try self.airFpext(inst),
             .intcast         => try self.airIntCast(inst),
@@ -624,14 +623,12 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .@"try"          => @panic("TODO"),
             .try_ptr         => @panic("TODO"),
 
+            .dbg_stmt         => try self.airDbgStmt(inst),
+            .dbg_inline_block => try self.airDbgInlineBlock(inst),
             .dbg_var_ptr,
             .dbg_var_val,
             => try self.airDbgVar(inst),
 
-            .dbg_inline_begin,
-            .dbg_inline_end,
-            => try self.airDbgInline(inst),
-
             .call              => try self.airCall(inst, .auto),
             .call_always_tail  => try self.airCall(inst, .always_tail),
             .call_never_tail   => try self.airCall(inst, .never_tail),
@@ -1881,13 +1878,14 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
     return self.finishAirBookkeeping();
 }
 
-fn airDbgInline(self: *Self, inst: Air.Inst.Index) !void {
-    const ty_fn = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_fn;
+fn airDbgInlineBlock(self: *Self, inst: Air.Inst.Index) !void {
     const mod = self.bin_file.comp.module.?;
-    const func = mod.funcInfo(ty_fn.func);
+    const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
+    const extra = self.air.extraData(Air.DbgInlineBlock, ty_pl.payload);
+    const func = mod.funcInfo(extra.data.func);
     // TODO emit debug info for function change
     _ = func;
-    return self.finishAir(inst, .dead, .{ .none, .none, .none });
+    try self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]));
 }
 
 fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
@@ -2060,6 +2058,12 @@ fn jump(self: *Self, index: usize) !void {
 }
 
 fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
+    const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
+    const extra = self.air.extraData(Air.Block, ty_pl.payload);
+    try self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]));
+}
+
+fn lowerBlock(self: *Self, inst: Air.Inst.Index, body: []const Air.Inst.Index) !void {
     try self.blocks.putNoClobber(self.gpa, inst, .{
         // A block is a setup to be able to jump to the end.
         .relocs = .{},
@@ -2071,10 +2075,6 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
         .mcv = MCValue{ .none = {} },
     });
     defer self.blocks.getPtr(inst).?.relocs.deinit(self.gpa);
-
-    const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
-    const extra = self.air.extraData(Air.Block, ty_pl.payload);
-    const body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]);
     // TODO emit debug info lexical block
     try self.genBody(body);
 
src/arch/sparc64/CodeGen.zig
@@ -579,7 +579,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .frame_addr      => @panic("TODO try self.airFrameAddress(inst)"),
             .fence           => try self.airFence(inst),
             .cond_br         => try self.airCondBr(inst),
-            .dbg_stmt        => try self.airDbgStmt(inst),
             .fptrunc         => @panic("TODO try self.airFptrunc(inst)"),
             .fpext           => @panic("TODO try self.airFpext(inst)"),
             .intcast         => try self.airIntCast(inst),
@@ -637,14 +636,12 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .@"try"          => try self.airTry(inst),
             .try_ptr         => @panic("TODO try self.airTryPtr(inst)"),
 
+            .dbg_stmt         => try self.airDbgStmt(inst),
+            .dbg_inline_block => try self.airDbgInlineBlock(inst),
             .dbg_var_ptr,
             .dbg_var_val,
             => try self.airDbgVar(inst),
 
-            .dbg_inline_begin,
-            .dbg_inline_end,
-            => try self.airDbgInline(inst),
-
             .call              => try self.airCall(inst, .auto),
             .call_always_tail  => try self.airCall(inst, .always_tail),
             .call_never_tail   => try self.airCall(inst, .never_tail),
@@ -1127,6 +1124,12 @@ fn airBitReverse(self: *Self, inst: Air.Inst.Index) !void {
 }
 
 fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
+    const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
+    const extra = self.air.extraData(Air.Block, ty_pl.payload);
+    try self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]));
+}
+
+fn lowerBlock(self: *Self, inst: Air.Inst.Index, body: []const Air.Inst.Index) !void {
     try self.blocks.putNoClobber(self.gpa, inst, .{
         // A block is a setup to be able to jump to the end.
         .relocs = .{},
@@ -1139,9 +1142,6 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
     });
     defer self.blocks.getPtr(inst).?.relocs.deinit(self.gpa);
 
-    const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
-    const extra = self.air.extraData(Air.Block, ty_pl.payload);
-    const body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]);
     // TODO emit debug info lexical block
     try self.genBody(body);
 
@@ -1652,13 +1652,14 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) !void {
     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
 }
 
-fn airDbgInline(self: *Self, inst: Air.Inst.Index) !void {
-    const ty_fn = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_fn;
+fn airDbgInlineBlock(self: *Self, inst: Air.Inst.Index) !void {
     const mod = self.bin_file.comp.module.?;
-    const func = mod.funcInfo(ty_fn.func);
+    const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
+    const extra = self.air.extraData(Air.DbgInlineBlock, ty_pl.payload);
+    const func = mod.funcInfo(extra.data.func);
     // TODO emit debug info for function change
     _ = func;
-    return self.finishAir(inst, .dead, .{ .none, .none, .none });
+    try self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]));
 }
 
 fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
src/arch/wasm/CodeGen.zig
@@ -1910,16 +1910,11 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
         .@"try" => func.airTry(inst),
         .try_ptr => func.airTryPtr(inst),
 
-        // TODO
-        .dbg_inline_begin,
-        .dbg_inline_end,
-        => func.finishAir(inst, .none, &.{}),
-
+        .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_stmt => func.airDbgStmt(inst),
-
         .call => func.airCall(inst, .auto),
         .call_always_tail => func.airCall(inst, .always_tail),
         .call_never_tail => func.airCall(inst, .never_tail),
@@ -3476,12 +3471,14 @@ fn intStorageAsI32(storage: InternPool.Key.Int.Storage, mod: *Module) i32 {
 }
 
 fn airBlock(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
-    const mod = func.bin_file.base.comp.module.?;
     const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
-    const block_ty = ty_pl.ty.toType();
-    const wasm_block_ty = genBlockType(block_ty, mod);
     const extra = func.air.extraData(Air.Block, ty_pl.payload);
-    const body: []const Air.Inst.Index = @ptrCast(func.air.extra[extra.end..][0..extra.data.body_len]);
+    try func.lowerBlock(inst, ty_pl.ty.toType(), @ptrCast(func.air.extra[extra.end..][0..extra.data.body_len]));
+}
+
+fn lowerBlock(func: *CodeGen, inst: Air.Inst.Index, block_ty: Type, body: []const Air.Inst.Index) InnerError!void {
+    const mod = func.bin_file.base.comp.module.?;
+    const wasm_block_ty = genBlockType(block_ty, mod);
 
     // if wasm_block_ty is non-empty, we create a register to store the temporary value
     const block_result: WValue = if (wasm_block_ty != wasm.block_empty) blk: {
@@ -6472,7 +6469,27 @@ fn airCtz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
     func.finishAir(inst, result, &.{ty_op.operand});
 }
 
-fn airDbgVar(func: *CodeGen, inst: Air.Inst.Index, is_ptr: bool) !void {
+fn airDbgStmt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
+    if (func.debug_output != .dwarf) return func.finishAir(inst, .none, &.{});
+
+    const dbg_stmt = func.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt;
+    try func.addInst(.{ .tag = .dbg_line, .data = .{
+        .payload = try func.addExtra(Mir.DbgLineColumn{
+            .line = dbg_stmt.line,
+            .column = dbg_stmt.column,
+        }),
+    } });
+    func.finishAir(inst, .none, &.{});
+}
+
+fn airDbgInlineBlock(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
+    const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
+    const extra = func.air.extraData(Air.DbgInlineBlock, ty_pl.payload);
+    // TODO
+    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 {
     if (func.debug_output != .dwarf) return func.finishAir(inst, .none, &.{});
 
     const mod = func.bin_file.base.comp.module.?;
@@ -6497,19 +6514,6 @@ fn airDbgVar(func: *CodeGen, inst: Air.Inst.Index, is_ptr: bool) !void {
     func.finishAir(inst, .none, &.{});
 }
 
-fn airDbgStmt(func: *CodeGen, inst: Air.Inst.Index) !void {
-    if (func.debug_output != .dwarf) return func.finishAir(inst, .none, &.{});
-
-    const dbg_stmt = func.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt;
-    try func.addInst(.{ .tag = .dbg_line, .data = .{
-        .payload = try func.addExtra(Mir.DbgLineColumn{
-            .line = dbg_stmt.line,
-            .column = dbg_stmt.column,
-        }),
-    } });
-    func.finishAir(inst, .none, &.{});
-}
-
 fn airTry(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
     const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
     const err_union = try func.resolveInst(pl_op.operand);
src/arch/x86_64/CodeGen.zig
@@ -58,6 +58,7 @@ bin_file: *link.File,
 debug_output: DebugInfoOutput,
 target: *const std.Target,
 owner: Owner,
+inline_func: InternPool.Index,
 mod: *Package.Module,
 err_msg: ?*ErrorMsg,
 args: []MCValue,
@@ -820,6 +821,7 @@ pub fn generate(
         .bin_file = bin_file,
         .debug_output = debug_output,
         .owner = .{ .func_index = func_index },
+        .inline_func = func_index,
         .err_msg = null,
         .args = undefined, // populated after `resolveCallingConventionValues`
         .va_info = undefined, // populated after `resolveCallingConventionValues`
@@ -987,6 +989,7 @@ pub fn generateLazy(
         .bin_file = bin_file,
         .debug_output = debug_output,
         .owner = .{ .lazy_sym = lazy_sym },
+        .inline_func = undefined,
         .err_msg = null,
         .args = undefined,
         .va_info = undefined,
@@ -2042,7 +2045,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .frame_addr      => try self.airFrameAddress(inst),
             .fence           => try self.airFence(inst),
             .cond_br         => try self.airCondBr(inst),
-            .dbg_stmt        => try self.airDbgStmt(inst),
             .fptrunc         => try self.airFptrunc(inst),
             .fpext           => try self.airFpext(inst),
             .intcast         => try self.airIntCast(inst),
@@ -2098,14 +2100,12 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .@"try"          => try self.airTry(inst),
             .try_ptr         => try self.airTryPtr(inst),
 
+            .dbg_stmt         => try self.airDbgStmt(inst),
+            .dbg_inline_block => try self.airDbgInlineBlock(inst),
             .dbg_var_ptr,
             .dbg_var_val,
             => try self.airDbgVar(inst),
 
-            .dbg_inline_begin,
-            .dbg_inline_end,
-            => try self.airDbgInline(inst),
-
             .call              => try self.airCall(inst, .auto),
             .call_always_tail  => try self.airCall(inst, .always_tail),
             .call_never_tail   => try self.airCall(inst, .never_tail),
@@ -12962,14 +12962,23 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
     self.finishAirBookkeeping();
 }
 
-fn airDbgInline(self: *Self, inst: Air.Inst.Index) !void {
-    const ty_fn = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_fn;
+fn airDbgInlineBlock(self: *Self, inst: Air.Inst.Index) !void {
+    const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
+    const extra = self.air.extraData(Air.DbgInlineBlock, ty_pl.payload);
+    const old_inline_func = self.inline_func;
+    defer self.inline_func = old_inline_func;
+    self.inline_func = extra.data.func;
     _ = try self.addInst(.{
         .tag = .pseudo,
         .ops = .pseudo_dbg_inline_func,
-        .data = .{ .func = ty_fn.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,
+        .data = .{ .func = old_inline_func },
     });
-    self.finishAirBookkeeping();
 }
 
 fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
@@ -13407,6 +13416,12 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void {
 }
 
 fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
+    const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
+    const extra = self.air.extraData(Air.Block, ty_pl.payload);
+    try self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]));
+}
+
+fn lowerBlock(self: *Self, inst: Air.Inst.Index, body: []const Air.Inst.Index) !void {
     // A block is a setup to be able to jump to the end.
     const inst_tracking_i = self.inst_tracking.count();
     self.inst_tracking.putAssumeCapacityNoClobber(inst, InstTracking.init(.unreach));
@@ -13415,9 +13430,6 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
     try self.blocks.putNoClobber(self.gpa, inst, .{ .state = self.initRetroactiveState() });
     const liveness = self.liveness.getBlock(inst);
 
-    const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
-    const extra = self.air.extraData(Air.Block, ty_pl.payload);
-    const body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]);
     // TODO emit debug info lexical block
     try self.genBody(body);
 
src/codegen/c.zig
@@ -2554,9 +2554,9 @@ pub fn genTypeDecl(
                         .suffix,
                         .{},
                     );
-                    try writer.writeAll("; // ");
+                    try writer.writeAll("; /* ");
                     try mod.declPtr(owner_decl).renderFullyQualifiedName(mod, writer);
-                    try writer.writeByte('\n');
+                    try writer.writeAll(" */\n");
                 },
 
                 .anon_struct,
@@ -3215,7 +3215,6 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
             .assembly         => try airAsm(f, inst),
             .block            => try airBlock(f, inst),
             .bitcast          => try airBitcast(f, inst),
-            .dbg_stmt         => try airDbgStmt(f, inst),
             .intcast          => try airIntCast(f, inst),
             .trunc            => try airTrunc(f, inst),
             .int_from_bool      => try airIntFromBool(f, inst),
@@ -3259,13 +3258,9 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
             .@"try"  => try airTry(f, inst),
             .try_ptr => try airTryPtr(f, inst),
 
-            .dbg_var_ptr,
-            .dbg_var_val,
-            => try airDbgVar(f, inst),
-
-            .dbg_inline_begin,
-            .dbg_inline_end,
-            => try airDbgInline(f, inst),
+            .dbg_stmt => try airDbgStmt(f, inst),
+            .dbg_inline_block => try airDbgInlineBlock(f, inst),
+            .dbg_var_ptr, .dbg_var_val => try airDbgVar(f, inst),
 
             .call              => try airCall(f, inst, .auto),
             .call_always_tail  => .none,
@@ -4497,15 +4492,16 @@ fn airDbgStmt(f: *Function, inst: Air.Inst.Index) !CValue {
     return .none;
 }
 
-fn airDbgInline(f: *Function, inst: Air.Inst.Index) !CValue {
-    const ty_fn = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_fn;
+fn airDbgInlineBlock(f: *Function, inst: Air.Inst.Index) !CValue {
     const mod = f.object.dg.module;
+    const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
+    const extra = f.air.extraData(Air.DbgInlineBlock, ty_pl.payload);
+    const owner_decl = mod.funcOwnerDeclPtr(extra.data.func);
     const writer = f.object.writer();
-    const owner_decl = mod.funcOwnerDeclPtr(ty_fn.func);
-    try writer.print("/* dbg func:{s} */\n", .{
-        mod.intern_pool.stringToSlice(owner_decl.name),
-    });
-    return .none;
+    try writer.writeAll("/* ");
+    try owner_decl.renderFullyQualifiedName(mod, writer);
+    try writer.writeAll(" */ ");
+    return lowerBlock(f, inst, @ptrCast(f.air.extra[extra.end..][0..extra.data.body_len]));
 }
 
 fn airDbgVar(f: *Function, inst: Air.Inst.Index) !CValue {
@@ -4522,10 +4518,13 @@ fn airDbgVar(f: *Function, inst: Air.Inst.Index) !CValue {
 }
 
 fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue {
-    const mod = f.object.dg.module;
     const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
     const extra = f.air.extraData(Air.Block, ty_pl.payload);
-    const body: []const Air.Inst.Index = @ptrCast(f.air.extra[extra.end..][0..extra.data.body_len]);
+    return lowerBlock(f, inst, @ptrCast(f.air.extra[extra.end..][0..extra.data.body_len]));
+}
+
+fn lowerBlock(f: *Function, inst: Air.Inst.Index, body: []const Air.Inst.Index) !CValue {
+    const mod = f.object.dg.module;
     const liveness_block = f.liveness.getBlock(inst);
 
     const block_id: usize = f.next_block_index;
src/codegen/llvm.zig
@@ -4762,11 +4762,7 @@ pub const FuncGen = struct {
     file: Builder.Metadata,
     scope: Builder.Metadata,
 
-    inlined: std.ArrayListUnmanaged(struct {
-        base_line: u32,
-        location: Builder.DebugLocation,
-        scope: Builder.Metadata,
-    }) = .{},
+    inlined: Builder.DebugLocation = .no_location,
 
     base_line: u32,
     prev_dbg_line: c_uint,
@@ -4811,7 +4807,6 @@ pub const FuncGen = struct {
 
     fn deinit(self: *FuncGen) void {
         self.wip.deinit();
-        self.inlined.deinit(self.gpa);
         self.func_inst_table.deinit(self.gpa);
         self.blocks.deinit(self.gpa);
     }
@@ -5107,8 +5102,7 @@ pub const FuncGen = struct {
 
                 .unreach  => try self.airUnreach(inst),
                 .dbg_stmt => try self.airDbgStmt(inst),
-                .dbg_inline_begin => try self.airDbgInlineBegin(inst),
-                .dbg_inline_end => try self.airDbgInlineEnd(inst),
+                .dbg_inline_block => try self.airDbgInlineBlock(inst),
                 .dbg_var_ptr => try self.airDbgVarPtr(inst),
                 .dbg_var_val => try self.airDbgVarVal(inst),
 
@@ -5126,17 +5120,81 @@ pub const FuncGen = struct {
         }
     }
 
-    fn genBodyDebugScope(self: *FuncGen, body: []const Air.Inst.Index) Error!void {
+    fn genBodyDebugScope(self: *FuncGen, maybe_inline_func: ?InternPool.Index, body: []const Air.Inst.Index) Error!void {
         if (self.wip.strip) return self.genBody(body);
+
+        const old_file = self.file;
+        const old_inlined = self.inlined;
+        const old_base_line = self.base_line;
         const old_scope = self.scope;
+        defer if (maybe_inline_func) |_| {
+            self.wip.debug_location = self.inlined;
+            self.file = old_file;
+            self.inlined = old_inlined;
+            self.base_line = old_base_line;
+        };
+        defer self.scope = old_scope;
+
+        if (maybe_inline_func) |inline_func| {
+            const o = self.dg.object;
+            const zcu = o.module;
+
+            const func = zcu.funcInfo(inline_func);
+            const decl_index = func.owner_decl;
+            const decl = zcu.declPtr(decl_index);
+            const namespace = zcu.namespacePtr(decl.src_namespace);
+            const owner_mod = namespace.file_scope.mod;
+
+            self.file = try o.getDebugFile(namespace.file_scope);
+
+            const line_number = decl.src_line + 1;
+            self.inlined = self.wip.debug_location;
+
+            const fqn = try decl.fullyQualifiedName(zcu);
+
+            const is_internal_linkage = !zcu.decl_exports.contains(decl_index);
+            const fn_ty = try zcu.funcType(.{
+                .param_types = &.{},
+                .return_type = .void_type,
+            });
+
+            self.scope = try o.builder.debugSubprogram(
+                self.file,
+                try o.builder.metadataString(zcu.intern_pool.stringToSlice(decl.name)),
+                try o.builder.metadataString(zcu.intern_pool.stringToSlice(fqn)),
+                line_number,
+                line_number + func.lbrace_line,
+                try o.lowerDebugType(fn_ty),
+                .{
+                    .di_flags = .{ .StaticMember = true },
+                    .sp_flags = .{
+                        .Optimized = owner_mod.optimize_mode != .Debug,
+                        .Definition = true,
+                        .LocalToUnit = is_internal_linkage,
+                    },
+                },
+                o.debug_compile_unit,
+            );
+
+            self.base_line = decl.src_line;
+            const inlined_at_location = try self.wip.debug_location.toMetadata(&o.builder);
+            self.wip.debug_location = .{
+                .location = .{
+                    .line = line_number,
+                    .column = 0,
+                    .scope = self.scope,
+                    .inlined_at = inlined_at_location,
+                },
+            };
+        }
+
         self.scope = try self.dg.object.builder.debugLexicalBlock(
-            old_scope,
+            self.scope,
             self.file,
             self.prev_dbg_line,
             self.prev_dbg_column,
         );
         try self.genBody(body);
-        self.scope = old_scope;
     }
 
     pub const CallAttr = enum {
@@ -5820,15 +5878,23 @@ pub const FuncGen = struct {
     }
 
     fn airBlock(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value {
-        const o = self.dg.object;
-        const mod = o.module;
         const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
         const extra = self.air.extraData(Air.Block, ty_pl.payload);
-        const body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]);
+        return self.lowerBlock(inst, null, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]));
+    }
+
+    fn lowerBlock(
+        self: *FuncGen,
+        inst: Air.Inst.Index,
+        maybe_inline_func: ?InternPool.Index,
+        body: []const Air.Inst.Index,
+    ) !Builder.Value {
+        const o = self.dg.object;
+        const mod = o.module;
         const inst_ty = self.typeOfIndex(inst);
 
         if (inst_ty.isNoReturn(mod)) {
-            try self.genBodyDebugScope(body);
+            try self.genBodyDebugScope(maybe_inline_func, body);
             return .none;
         }
 
@@ -5844,7 +5910,7 @@ pub const FuncGen = struct {
         });
         defer assert(self.blocks.remove(inst));
 
-        try self.genBodyDebugScope(body);
+        try self.genBodyDebugScope(maybe_inline_func, body);
 
         self.wip.cursor = .{ .block = parent_bb };
 
@@ -5903,10 +5969,10 @@ pub const FuncGen = struct {
         _ = try self.wip.brCond(cond, then_block, else_block);
 
         self.wip.cursor = .{ .block = then_block };
-        try self.genBodyDebugScope(then_body);
+        try self.genBodyDebugScope(null, then_body);
 
         self.wip.cursor = .{ .block = else_block };
-        try self.genBodyDebugScope(else_body);
+        try self.genBodyDebugScope(null, else_body);
 
         // No need to reset the insert cursor since this instruction is noreturn.
         return .none;
@@ -5987,7 +6053,7 @@ pub const FuncGen = struct {
             _ = try fg.wip.brCond(is_err, return_block, continue_block);
 
             fg.wip.cursor = .{ .block = return_block };
-            try fg.genBodyDebugScope(body);
+            try fg.genBodyDebugScope(null, body);
 
             fg.wip.cursor = .{ .block = continue_block };
         }
@@ -6060,13 +6126,13 @@ pub const FuncGen = struct {
             }
 
             self.wip.cursor = .{ .block = case_block };
-            try self.genBodyDebugScope(case_body);
+            try self.genBodyDebugScope(null, case_body);
         }
 
         self.wip.cursor = .{ .block = else_block };
         const else_body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra_index..][0..switch_br.data.else_body_len]);
         if (else_body.len != 0) {
-            try self.genBodyDebugScope(else_body);
+            try self.genBodyDebugScope(null, else_body);
         } else {
             _ = try self.wip.@"unreachable"();
         }
@@ -6085,7 +6151,7 @@ pub const FuncGen = struct {
         _ = try self.wip.br(loop_block);
 
         self.wip.cursor = .{ .block = loop_block };
-        try self.genBodyDebugScope(body);
+        try self.genBodyDebugScope(null, body);
 
         // TODO instead of this logic, change AIR to have the property that
         // every block is guaranteed to end with a noreturn instruction.
@@ -6592,96 +6658,22 @@ pub const FuncGen = struct {
         self.prev_dbg_line = @intCast(self.base_line + dbg_stmt.line + 1);
         self.prev_dbg_column = @intCast(dbg_stmt.column + 1);
 
-        const inlined_at_location = if (self.inlined.getLastOrNull()) |inlined|
-            try inlined.location.toMetadata(self.wip.builder)
-        else
-            .none;
-
         self.wip.debug_location = .{
             .location = .{
                 .line = self.prev_dbg_line,
                 .column = self.prev_dbg_column,
                 .scope = self.scope,
-                .inlined_at = inlined_at_location,
+                .inlined_at = try self.inlined.toMetadata(self.wip.builder),
             },
         };
 
         return .none;
     }
 
-    fn airDbgInlineBegin(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value {
-        const o = self.dg.object;
-        const zcu = o.module;
-
-        const ty_fn = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_fn;
-        const func = zcu.funcInfo(ty_fn.func);
-        const decl_index = func.owner_decl;
-        const decl = zcu.declPtr(decl_index);
-        const namespace = zcu.namespacePtr(decl.src_namespace);
-        const owner_mod = namespace.file_scope.mod;
-
-        self.file = try o.getDebugFile(namespace.file_scope);
-
-        const line_number = decl.src_line + 1;
-        try self.inlined.append(self.gpa, .{
-            .location = self.wip.debug_location,
-            .scope = self.scope,
-            .base_line = self.base_line,
-        });
-
-        const fqn = try decl.fullyQualifiedName(zcu);
-
-        const is_internal_linkage = !zcu.decl_exports.contains(decl_index);
-        const fn_ty = try zcu.funcType(.{
-            .param_types = &.{},
-            .return_type = .void_type,
-        });
-
-        self.scope = try o.builder.debugSubprogram(
-            self.file,
-            try o.builder.metadataString(zcu.intern_pool.stringToSlice(decl.name)),
-            try o.builder.metadataString(zcu.intern_pool.stringToSlice(fqn)),
-            line_number,
-            line_number + func.lbrace_line,
-            try o.lowerDebugType(fn_ty),
-            .{
-                .di_flags = .{ .StaticMember = true },
-                .sp_flags = .{
-                    .Optimized = owner_mod.optimize_mode != .Debug,
-                    .Definition = true,
-                    .LocalToUnit = is_internal_linkage,
-                },
-            },
-            o.debug_compile_unit,
-        );
-
-        self.base_line = decl.src_line;
-        const inlined_at_location = try self.wip.debug_location.toMetadata(&o.builder);
-        self.wip.debug_location = .{
-            .location = .{
-                .line = line_number,
-                .column = 0,
-                .scope = self.scope,
-                .inlined_at = inlined_at_location,
-            },
-        };
-        return .none;
-    }
-
-    fn airDbgInlineEnd(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value {
-        const o = self.dg.object;
-
-        const ty_fn = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_fn;
-
-        const mod = o.module;
-        const decl = mod.funcOwnerDeclPtr(ty_fn.func);
-        self.file = try o.getDebugFile(mod.namespacePtr(decl.src_namespace).file_scope);
-
-        const old = self.inlined.pop();
-        self.scope = old.scope;
-        self.base_line = old.base_line;
-        self.wip.debug_location = old.location;
-        return .none;
+    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);
+        return self.lowerBlock(inst, extra.data.func, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]));
     }
 
     fn airDbgVarPtr(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value {
src/codegen/spirv.zig
@@ -208,6 +208,7 @@ pub const Object = struct {
                 false => .{ .unstructured = .{} },
             },
             .current_block_label = undefined,
+            .base_line = decl.src_line,
         };
         defer decl_gen.deinit();
 
@@ -321,9 +322,8 @@ const DeclGen = struct {
     /// The code (prologue and body) for the function we are currently generating code for.
     func: SpvModule.Fn = .{},
 
-    /// Stack of the base offsets of the current decl, which is what `dbg_stmt` is relative to.
-    /// This is a stack to keep track of inline functions.
-    base_line_stack: std.ArrayListUnmanaged(u32) = .{},
+    /// The base offset of the current decl, which is what `dbg_stmt` is relative to.
+    base_line: u32,
 
     /// If `gen` returned `Error.CodegenFail`, this contains an explanatory message.
     /// Memory is owned by `module.gpa`.
@@ -401,7 +401,6 @@ const DeclGen = struct {
         self.wip_pointers.deinit(self.gpa);
         self.control_flow.deinit(self.gpa);
         self.func.deinit(self.gpa);
-        self.base_line_stack.deinit(self.gpa);
     }
 
     /// Return the target which we are currently compiling for.
@@ -1959,8 +1958,6 @@ const DeclGen = struct {
 
         const decl_id = self.spv.declPtr(spv_decl_index).result_id;
 
-        try self.base_line_stack.append(self.gpa, decl.src_line);
-
         if (decl.val.getFunction(mod)) |_| {
             assert(decl.ty.zigTypeTag(mod) == .Fn);
             const fn_info = mod.typeToFunc(decl.ty).?;
@@ -2317,8 +2314,7 @@ const DeclGen = struct {
             .unreach, .trap => return self.airUnreach(),
 
             .dbg_stmt                  => return self.airDbgStmt(inst),
-            .dbg_inline_begin          => return self.airDbgInlineBegin(inst),
-            .dbg_inline_end            => return self.airDbgInlineEnd(inst),
+            .dbg_inline_block          => try self.airDbgInlineBlock(inst),
             .dbg_var_ptr, .dbg_var_val => return self.airDbgVar(inst),
 
             .unwrap_errunion_err => try self.airErrUnionErr(inst),
@@ -4311,6 +4307,12 @@ const DeclGen = struct {
     }
 
     fn airBlock(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
+        const inst_datas = self.air.instructions.items(.data);
+        const extra = self.air.extraData(Air.Block, inst_datas[@intFromEnum(inst)].ty_pl.payload);
+        return self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]));
+    }
+
+    fn lowerBlock(self: *DeclGen, inst: Air.Inst.Index, body: []const Air.Inst.Index) !?IdRef {
         // In AIR, a block doesn't really define an entry point like a block, but
         // more like a scope that breaks can jump out of and "return" a value from.
         // This cannot be directly modelled in SPIR-V, so in a block instruction,
@@ -4320,10 +4322,6 @@ const DeclGen = struct {
 
         const mod = self.module;
         const ty = self.typeOfIndex(inst);
-        const inst_datas = self.air.instructions.items(.data);
-        const extra = self.air.extraData(Air.Block, inst_datas[@intFromEnum(inst)].ty_pl.payload);
-        const body: []const Air.Inst.Index =
-            @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]);
         const have_block_result = ty.isFnOrHasRuntimeBitsIgnoreComptime(mod);
 
         const cf = switch (self.control_flow) {
@@ -5157,25 +5155,22 @@ const DeclGen = struct {
         const decl = mod.declPtr(self.decl_index);
         const path = decl.getFileScope(mod).sub_file_path;
         const src_fname_id = try self.spv.resolveSourceFileName(path);
-        const base_line = self.base_line_stack.getLast();
         try self.func.body.emit(self.spv.gpa, .OpLine, .{
             .file = src_fname_id,
-            .line = base_line + dbg_stmt.line + 1,
+            .line = self.base_line + dbg_stmt.line + 1,
             .column = dbg_stmt.column + 1,
         });
     }
 
-    fn airDbgInlineBegin(self: *DeclGen, inst: Air.Inst.Index) !void {
+    fn airDbgInlineBlock(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
         const mod = self.module;
-        const fn_ty = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_fn;
-        const decl_index = mod.funcInfo(fn_ty.func).owner_decl;
-        const decl = mod.declPtr(decl_index);
-        try self.base_line_stack.append(self.gpa, decl.src_line);
-    }
-
-    fn airDbgInlineEnd(self: *DeclGen, inst: Air.Inst.Index) !void {
-        _ = inst;
-        _ = self.base_line_stack.pop();
+        const inst_datas = self.air.instructions.items(.data);
+        const extra = self.air.extraData(Air.DbgInlineBlock, inst_datas[@intFromEnum(inst)].ty_pl.payload);
+        const decl = mod.funcOwnerDeclPtr(extra.data.func);
+        const old_base_line = self.base_line;
+        defer self.base_line = old_base_line;
+        self.base_line = decl.src_line;
+        return self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]));
     }
 
     fn airDbgVar(self: *DeclGen, inst: Air.Inst.Index) !void {
src/Liveness/Verify.zig
@@ -29,7 +29,7 @@ const LiveMap = std.AutoHashMapUnmanaged(Air.Inst.Index, void);
 
 fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
     const ip = self.intern_pool;
-    const tag = self.air.instructions.items(.tag);
+    const tags = self.air.instructions.items(.tag);
     const data = self.air.instructions.items(.data);
     for (body) |inst| {
         if (self.liveness.isUnused(inst) and !self.air.mustLower(inst, ip)) {
@@ -37,7 +37,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
             continue;
         }
 
-        switch (tag[@intFromEnum(inst)]) {
+        switch (tags[@intFromEnum(inst)]) {
             // no operands
             .arg,
             .alloc,
@@ -46,8 +46,6 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
             .ret_ptr,
             .breakpoint,
             .dbg_stmt,
-            .dbg_inline_begin,
-            .dbg_inline_end,
             .fence,
             .ret_addr,
             .frame_addr,
@@ -431,11 +429,20 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
                 }
                 try self.verifyInst(inst);
             },
-            .block => {
+            .block, .dbg_inline_block => |tag| {
                 const ty_pl = data[@intFromEnum(inst)].ty_pl;
                 const block_ty = ty_pl.ty.toType();
-                const extra = self.air.extraData(Air.Block, ty_pl.payload);
-                const block_body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]);
+                const block_body: []const Air.Inst.Index = @ptrCast(switch (tag) {
+                    inline .block, .dbg_inline_block => |comptime_tag| body: {
+                        const extra = self.air.extraData(switch (comptime_tag) {
+                            .block => Air.Block,
+                            .dbg_inline_block => Air.DbgInlineBlock,
+                            else => unreachable,
+                        }, ty_pl.payload);
+                        break :body self.air.extra[extra.end..][0..extra.data.body_len];
+                    },
+                    else => unreachable,
+                });
                 const block_liveness = self.liveness.getBlock(inst);
 
                 var orig_live = try self.live.clone(self.gpa);
src/Air.zig
@@ -443,12 +443,9 @@ pub const Inst = struct {
         /// Result type is always void.
         /// Uses the `dbg_stmt` field.
         dbg_stmt,
-        /// Marks the start of an inline call.
-        /// Uses the `ty_fn` field.
-        dbg_inline_begin,
-        /// Marks the end of an inline call.
-        /// Uses the `ty_fn` field.
-        dbg_inline_end,
+        /// A block that represents an inlined function call.
+        /// Uses the `ty_pl` field. Payload is `DbgInlineBlock`.
+        dbg_inline_block,
         /// Marks the beginning of a local variable. The operand is a pointer pointing
         /// to the storage for the variable. The local may be a const or a var.
         /// Result type is always void.
@@ -1051,10 +1048,6 @@ pub const Inst = struct {
             // Index into a different array.
             payload: u32,
         },
-        ty_fn: struct {
-            ty: Ref,
-            func: InternPool.Index,
-        },
         br: struct {
             block_inst: Index,
             operand: Ref,
@@ -1117,6 +1110,12 @@ pub const Block = struct {
     body_len: u32,
 };
 
+/// Trailing is a list of instruction indexes for every `body_len`.
+pub const DbgInlineBlock = struct {
+    func: InternPool.Index,
+    body_len: u32,
+};
+
 /// Trailing is a list of `Inst.Ref` for every `args_len`.
 pub const Call = struct {
     args_len: u32,
@@ -1371,6 +1370,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool)
 
         .assembly,
         .block,
+        .dbg_inline_block,
         .struct_field_ptr,
         .struct_field_val,
         .slice_elem_ptr,
@@ -1448,8 +1448,6 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool)
 
         .breakpoint,
         .dbg_stmt,
-        .dbg_inline_begin,
-        .dbg_inline_end,
         .dbg_var_ptr,
         .dbg_var_val,
         .store,
@@ -1606,8 +1604,7 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool {
         .@"try",
         .try_ptr,
         .dbg_stmt,
-        .dbg_inline_begin,
-        .dbg_inline_end,
+        .dbg_inline_block,
         .dbg_var_ptr,
         .dbg_var_val,
         .ret,
src/Liveness.zig
@@ -327,8 +327,6 @@ pub fn categorizeOperand(
         .trap,
         .breakpoint,
         .dbg_stmt,
-        .dbg_inline_begin,
-        .dbg_inline_end,
         .unreach,
         .ret_addr,
         .frame_addr,
@@ -604,9 +602,19 @@ pub fn categorizeOperand(
         .assembly => {
             return .complex;
         },
-        .block => {
-            const extra = air.extraData(Air.Block, air_datas[@intFromEnum(inst)].ty_pl.payload);
-            const body: []const Air.Inst.Index = @ptrCast(air.extra[extra.end..][0..extra.data.body_len]);
+        .block, .dbg_inline_block => |tag| {
+            const ty_pl = air_datas[@intFromEnum(inst)].ty_pl;
+            const body: []const Air.Inst.Index = @ptrCast(switch (tag) {
+                inline .block, .dbg_inline_block => |comptime_tag| body: {
+                    const extra = air.extraData(switch (comptime_tag) {
+                        .block => Air.Block,
+                        .dbg_inline_block => Air.DbgInlineBlock,
+                        else => unreachable,
+                    }, ty_pl.payload);
+                    break :body air.extra[extra.end..][0..extra.data.body_len];
+                },
+                else => unreachable,
+            });
 
             if (body.len == 1 and air_tags[@intFromEnum(body[0])] == .cond_br) {
                 // Peephole optimization for "panic-like" conditionals, which have
@@ -963,8 +971,6 @@ fn analyzeInst(
         .ret_ptr,
         .breakpoint,
         .dbg_stmt,
-        .dbg_inline_begin,
-        .dbg_inline_end,
         .fence,
         .ret_addr,
         .frame_addr,
@@ -1235,7 +1241,15 @@ fn analyzeInst(
             return big.finish();
         },
 
-        .block => return analyzeInstBlock(a, pass, data, inst),
+        inline .block, .dbg_inline_block => |comptime_tag| {
+            const ty_pl = inst_datas[@intFromEnum(inst)].ty_pl;
+            const extra = a.air.extraData(switch (comptime_tag) {
+                .block => Air.Block,
+                .dbg_inline_block => Air.DbgInlineBlock,
+                else => unreachable,
+            }, ty_pl.payload);
+            return analyzeInstBlock(a, pass, data, inst, ty_pl.ty, @ptrCast(a.air.extra[extra.end..][0..extra.data.body_len]));
+        },
         .loop => return analyzeInstLoop(a, pass, data, inst),
 
         .@"try" => return analyzeInstCondBr(a, pass, data, inst, .@"try"),
@@ -1369,12 +1383,9 @@ fn analyzeInstBlock(
     comptime pass: LivenessPass,
     data: *LivenessPassData(pass),
     inst: Air.Inst.Index,
+    ty: Air.Inst.Ref,
+    body: []const Air.Inst.Index,
 ) !void {
-    const inst_datas = a.air.instructions.items(.data);
-    const ty_pl = inst_datas[@intFromEnum(inst)].ty_pl;
-    const extra = a.air.extraData(Air.Block, ty_pl.payload);
-    const body: []const Air.Inst.Index = @ptrCast(a.air.extra[extra.end..][0..extra.data.body_len]);
-
     const gpa = a.gpa;
 
     // We actually want to do `analyzeOperands` *first*, since our result logically doesn't
@@ -1403,7 +1414,7 @@ fn analyzeInstBlock(
 
             // If the block is noreturn, block deaths not only aren't useful, they're impossible to
             // find: there could be more stuff alive after the block than before it!
-            if (!a.intern_pool.isNoReturn(ty_pl.ty.toType().ip_index)) {
+            if (!a.intern_pool.isNoReturn(ty.toType().toIntern())) {
                 // The block kills the difference in the live sets
                 const block_scope = data.block_scopes.get(inst).?;
                 const num_deaths = data.live_set.count() - block_scope.live_set.count();
src/print_air.zig
@@ -260,7 +260,7 @@ const Writer = struct {
             .c_va_copy,
             => try w.writeTyOp(s, inst),
 
-            .block => try w.writeBlock(s, inst),
+            .block, .dbg_inline_block => try w.writeBlock(s, tag, inst),
 
             .loop => try w.writeLoop(s, inst),
 
@@ -292,7 +292,6 @@ const Writer = struct {
             .assembly => try w.writeAssembly(s, inst),
             .dbg_stmt => try w.writeDbgStmt(s, inst),
 
-            .dbg_inline_begin, .dbg_inline_end => try w.writeDbgInline(s, inst),
             .aggregate_init => try w.writeAggregateInit(s, inst),
             .union_init => try w.writeUnionInit(s, inst),
             .br => try w.writeBr(s, inst),
@@ -367,17 +366,34 @@ const Writer = struct {
         try w.writeOperand(s, inst, 0, ty_op.operand);
     }
 
-    fn writeBlock(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
+    fn writeBlock(w: *Writer, s: anytype, tag: Air.Inst.Tag, inst: Air.Inst.Index) @TypeOf(s).Error!void {
         const ty_pl = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
-        const extra = w.air.extraData(Air.Block, ty_pl.payload);
-        const body: []const Air.Inst.Index = @ptrCast(w.air.extra[extra.end..][0..extra.data.body_len]);
+        try w.writeType(s, ty_pl.ty.toType());
+        const body: []const Air.Inst.Index = @ptrCast(switch (tag) {
+            inline .block, .dbg_inline_block => |comptime_tag| body: {
+                const extra = w.air.extraData(switch (comptime_tag) {
+                    .block => Air.Block,
+                    .dbg_inline_block => Air.DbgInlineBlock,
+                    else => unreachable,
+                }, ty_pl.payload);
+                switch (comptime_tag) {
+                    .block => {},
+                    .dbg_inline_block => {
+                        try s.writeAll(", ");
+                        try w.writeInstRef(s, Air.internedToRef(extra.data.func), false);
+                    },
+                    else => unreachable,
+                }
+                break :body w.air.extra[extra.end..][0..extra.data.body_len];
+            },
+            else => unreachable,
+        });
+        if (w.skip_body) return s.writeAll(", ...");
         const liveness_block = if (w.liveness) |liveness|
             liveness.getBlock(inst)
         else
             Liveness.BlockSlices{ .deaths = &.{} };
 
-        try w.writeType(s, ty_pl.ty.toType());
-        if (w.skip_body) return s.writeAll(", ...");
         try s.writeAll(", {\n");
         const old_indent = w.indent;
         w.indent += 2;
@@ -661,13 +677,6 @@ const Writer = struct {
         try s.print("{d}:{d}", .{ dbg_stmt.line + 1, dbg_stmt.column + 1 });
     }
 
-    fn writeDbgInline(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
-        const ty_fn = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_fn;
-        const func_index = ty_fn.func;
-        const owner_decl = w.module.funcOwnerDeclPtr(func_index);
-        try s.print("{}", .{owner_decl.name.fmt(&w.module.intern_pool)});
-    }
-
     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);
src/Sema.zig
@@ -5939,6 +5939,7 @@ fn resolveBlockBody(
     if (child_block.is_comptime) {
         return sema.resolveInlineBody(child_block, body, body_inst);
     } else {
+        assert(sema.air_instructions.items(.tag)[@intFromEnum(merges.block_inst)] == .block);
         var need_debug_scope = false;
         child_block.need_debug_scope = &need_debug_scope;
         if (sema.analyzeBodyInner(child_block, body)) |_| {
@@ -6002,18 +6003,44 @@ fn resolveAnalyzedBlock(
     assert(child_block.instructions.items.len != 0);
     assert(sema.typeOf(child_block.instructions.items[child_block.instructions.items.len - 1].toRef()).isNoReturn(mod));
 
+    const block_tag = sema.air_instructions.items(.tag)[@intFromEnum(merges.block_inst)];
+    switch (block_tag) {
+        .block => {},
+        .dbg_inline_block => assert(need_debug_scope),
+        else => unreachable,
+    }
     if (merges.results.items.len == 0) {
-        // No need for a block instruction. We can put the new instructions
-        // directly into the parent block.
-        if (need_debug_scope) {
-            // The code following this block is unreachable, as the block has no
-            // merges, so we don't necessarily need to emit this as an AIR block.
-            // However, we need a block *somewhere* to make the scoping correct,
-            // so forward this request to the parent block.
-            if (parent_block.need_debug_scope) |ptr| ptr.* = true;
-        }
-        try parent_block.instructions.appendSlice(gpa, child_block.instructions.items);
-        return child_block.instructions.items[child_block.instructions.items.len - 1].toRef();
+        switch (block_tag) {
+            .block => {
+                // No need for a block instruction. We can put the new instructions
+                // directly into the parent block.
+                if (need_debug_scope) {
+                    // The code following this block is unreachable, as the block has no
+                    // merges, so we don't necessarily need to emit this as an AIR block.
+                    // However, we need a block *somewhere* to make the scoping correct,
+                    // so forward this request to the parent block.
+                    if (parent_block.need_debug_scope) |ptr| ptr.* = true;
+                }
+                try parent_block.instructions.appendSlice(gpa, child_block.instructions.items);
+                return child_block.instructions.items[child_block.instructions.items.len - 1].toRef();
+            },
+            .dbg_inline_block => {
+                // Create a block containing all instruction from the body.
+                try parent_block.instructions.append(gpa, merges.block_inst);
+                try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.DbgInlineBlock).Struct.fields.len +
+                    child_block.instructions.items.len);
+                sema.air_instructions.items(.data)[@intFromEnum(merges.block_inst)] = .{ .ty_pl = .{
+                    .ty = .noreturn_type,
+                    .payload = sema.addExtraAssumeCapacity(Air.DbgInlineBlock{
+                        .func = child_block.inlining.?.func,
+                        .body_len = @intCast(child_block.instructions.items.len),
+                    }),
+                } };
+                sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items));
+                return merges.block_inst.toRef();
+            },
+            else => unreachable,
+        }
     }
     if (merges.results.items.len == 1) {
         // If the `break` is trailing, we may be able to elide the AIR block here
@@ -6036,14 +6063,30 @@ fn resolveAnalyzedBlock(
         if (try sema.resolveValue(merges.results.items[0])) |result_val| {
             // Create a block containing all instruction from the body.
             try parent_block.instructions.append(gpa, merges.block_inst);
-            try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +
-                child_block.instructions.items.len);
-            sema.air_instructions.items(.data)[@intFromEnum(merges.block_inst)] = .{ .ty_pl = .{
-                .ty = .void_type,
-                .payload = sema.addExtraAssumeCapacity(Air.Block{
-                    .body_len = @intCast(child_block.instructions.items.len),
-                }),
-            } };
+            switch (block_tag) {
+                .block => {
+                    try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +
+                        child_block.instructions.items.len);
+                    sema.air_instructions.items(.data)[@intFromEnum(merges.block_inst)] = .{ .ty_pl = .{
+                        .ty = .void_type,
+                        .payload = sema.addExtraAssumeCapacity(Air.Block{
+                            .body_len = @intCast(child_block.instructions.items.len),
+                        }),
+                    } };
+                },
+                .dbg_inline_block => {
+                    try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.DbgInlineBlock).Struct.fields.len +
+                        child_block.instructions.items.len);
+                    sema.air_instructions.items(.data)[@intFromEnum(merges.block_inst)] = .{ .ty_pl = .{
+                        .ty = .void_type,
+                        .payload = sema.addExtraAssumeCapacity(Air.DbgInlineBlock{
+                            .func = child_block.inlining.?.func,
+                            .body_len = @intCast(child_block.instructions.items.len),
+                        }),
+                    } };
+                },
+                else => unreachable,
+            }
             sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items));
             // Rewrite the break to just give value {}; the value is
             // comptime-known and will be returned directly.
@@ -6079,14 +6122,30 @@ fn resolveAnalyzedBlock(
         return sema.failWithOwnedErrorMsg(child_block, msg);
     }
     const ty_inst = Air.internedToRef(resolved_ty.toIntern());
-    try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +
-        child_block.instructions.items.len);
-    sema.air_instructions.items(.data)[@intFromEnum(merges.block_inst)] = .{ .ty_pl = .{
-        .ty = ty_inst,
-        .payload = sema.addExtraAssumeCapacity(Air.Block{
-            .body_len = @intCast(child_block.instructions.items.len),
-        }),
-    } };
+    switch (block_tag) {
+        .block => {
+            try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +
+                child_block.instructions.items.len);
+            sema.air_instructions.items(.data)[@intFromEnum(merges.block_inst)] = .{ .ty_pl = .{
+                .ty = ty_inst,
+                .payload = sema.addExtraAssumeCapacity(Air.Block{
+                    .body_len = @intCast(child_block.instructions.items.len),
+                }),
+            } };
+        },
+        .dbg_inline_block => {
+            try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.DbgInlineBlock).Struct.fields.len +
+                child_block.instructions.items.len);
+            sema.air_instructions.items(.data)[@intFromEnum(merges.block_inst)] = .{ .ty_pl = .{
+                .ty = ty_inst,
+                .payload = sema.addExtraAssumeCapacity(Air.DbgInlineBlock{
+                    .func = child_block.inlining.?.func,
+                    .body_len = @intCast(child_block.instructions.items.len),
+                }),
+            } };
+        },
+        else => unreachable,
+    }
     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items));
     // Now that the block has its type resolved, we need to go back into all the break
     // instructions, and insert type coercion on the operands.
@@ -7379,7 +7438,6 @@ fn analyzeCall(
             .needed_comptime_reason = "function being called at comptime must be comptime-known",
             .block_comptime_reason = comptime_reason,
         });
-        const prev_fn_index = sema.func_index;
         const module_fn_index = switch (mod.intern_pool.indexToKey(func_val.toIntern())) {
             .extern_func => return sema.fail(block, call_src, "{s} call of extern function", .{
                 @as([]const u8, if (is_comptime_call) "comptime" else "inline"),
@@ -7415,9 +7473,10 @@ fn analyzeCall(
         // set to in the `Block`.
         // This block instruction will be used to capture the return value from the
         // inlined function.
+        const need_debug_scope = !is_comptime_call and !block.is_typeof and !block.ownerModule().strip;
         const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
         try sema.air_instructions.append(gpa, .{
-            .tag = .block,
+            .tag = if (need_debug_scope) .dbg_inline_block else .block,
             .data = undefined,
         });
         // This one is shared among sub-blocks within the same callee, but not
@@ -7584,10 +7643,7 @@ fn analyzeCall(
             }
 
             new_fn_info.return_type = sema.fn_ret_ty.toIntern();
-            const new_func_resolved_ty = try mod.funcType(new_fn_info);
             if (!is_comptime_call and !block.is_typeof) {
-                try emitDbgInline(block, prev_fn_index, module_fn_index, new_func_resolved_ty, .dbg_inline_begin);
-
                 const zir_tags = sema.code.instructions.items(.tag);
                 for (fn_info.param_body) |param| switch (zir_tags[@intFromEnum(param)]) {
                     .param, .param_comptime => {
@@ -7626,21 +7682,9 @@ fn analyzeCall(
                     error.ComptimeReturn => break :result inlining.comptime_result,
                     else => |e| return e,
                 };
-                break :result try sema.resolveAnalyzedBlock(block, call_src, &child_block, merges, false);
+                break :result try sema.resolveAnalyzedBlock(block, call_src, &child_block, merges, need_debug_scope);
             };
 
-            if (!is_comptime_call and !block.is_typeof and
-                sema.typeOf(result).zigTypeTag(mod) != .NoReturn)
-            {
-                try emitDbgInline(
-                    block,
-                    module_fn_index,
-                    prev_fn_index,
-                    mod.funcOwnerDeclPtr(sema.func_index).ty,
-                    .dbg_inline_end,
-                );
-            }
-
             if (should_memoize and is_comptime_call) {
                 const result_val = try sema.resolveConstValue(block, .unneeded, result, undefined);
                 const result_interned = try result_val.intern2(sema.fn_ret_ty, mod);
@@ -8207,27 +8251,6 @@ fn resolveTupleLazyValues(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type)
     }
 }
 
-fn emitDbgInline(
-    block: *Block,
-    old_func: InternPool.Index,
-    new_func: InternPool.Index,
-    new_func_ty: Type,
-    tag: Air.Inst.Tag,
-) CompileError!void {
-    if (block.ownerModule().strip) return;
-
-    // Recursive inline call; no dbg_inline needed.
-    if (old_func == new_func) return;
-
-    _ = try block.addInst(.{
-        .tag = tag,
-        .data = .{ .ty_fn = .{
-            .ty = Air.internedToRef(new_func_ty.toIntern()),
-            .func = new_func,
-        } },
-    });
-}
-
 fn zirIntType(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
     const int_type = sema.code.instructions.items(.data)[@intFromEnum(inst)].int_type;
test/behavior/switch.zig
@@ -913,3 +913,20 @@ test "switch prong captures range" {
     S.a(&arr, 5);
     try expect(arr[5] == 5);
 }
+
+test "prong with inline call to unreachable" {
+    const U = union(enum) {
+        void: void,
+        bool: bool,
+
+        inline fn unreach() noreturn {
+            unreachable;
+        }
+    };
+    var u: U = undefined;
+    u = .{ .bool = true };
+    switch (u) {
+        .void => U.unreach(),
+        .bool => |ok| try expect(ok),
+    }
+}