Commit 659603c621

Andrew Kelley <andrew@ziglang.org>
2020-08-03 06:28:06
codegen: emit .debug_line ops for IR instructions
1 parent 42d331b
Changed files (7)
lib/std/zig.zig
@@ -43,6 +43,15 @@ pub fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usi
     return .{ .line = line, .column = column };
 }
 
+pub fn lineDelta(source: []const u8, start: usize, end: usize) usize {
+    var line: usize = 0;
+    for (source[start..end]) |byte| switch (byte) {
+        '\n' => line += 1,
+        else => continue,
+    };
+    return line;
+}
+
 /// Returns the standard file system basename of a binary generated by the Zig compiler.
 pub fn binNameAlloc(
     allocator: *std.mem.Allocator,
src-self-hosted/astgen.zig
@@ -120,6 +120,8 @@ pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block
 
     var scope = parent_scope;
     for (block_node.statements()) |statement| {
+        const src = scope.tree().token_locs[statement.firstToken()].start;
+        _ = try addZIRNoOp(mod, scope, src, .dbg_stmt);
         switch (statement.tag) {
             .VarDecl => {
                 const var_decl_node = statement.castTag(.VarDecl).?;
@@ -146,7 +148,6 @@ pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block
             else => {
                 const possibly_unused_result = try expr(mod, scope, .none, statement);
                 if (!possibly_unused_result.tag.isNoReturn()) {
-                    const src = scope.tree().token_locs[statement.firstToken()].start;
                     _ = try addZIRUnOp(mod, scope, src, .ensure_result_used, possibly_unused_result);
                 }
             },
src-self-hosted/codegen.zig
@@ -12,6 +12,11 @@ const ErrorMsg = Module.ErrorMsg;
 const Target = std.Target;
 const Allocator = mem.Allocator;
 const trace = @import("tracy.zig").trace;
+const DW = std.dwarf;
+const leb128 = std.debug.leb;
+
+// TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented.
+// zig fmt: off
 
 /// The codegen-related data that is stored in `ir.Inst.Block` instructions.
 pub const BlockData = struct {
@@ -52,57 +57,57 @@ pub fn generateSymbol(
     switch (typed_value.ty.zigTypeTag()) {
         .Fn => {
             switch (bin_file.base.options.target.cpu.arch) {
-                //.arm => return Function(.arm).generateSymbol(bin_file, src, typed_value, code),
-                //.armeb => return Function(.armeb).generateSymbol(bin_file, src, typed_value, code),
-                //.aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code),
-                //.aarch64_be => return Function(.aarch64_be).generateSymbol(bin_file, src, typed_value, code),
-                //.aarch64_32 => return Function(.aarch64_32).generateSymbol(bin_file, src, typed_value, code),
-                //.arc => return Function(.arc).generateSymbol(bin_file, src, typed_value, code),
-                //.avr => return Function(.avr).generateSymbol(bin_file, src, typed_value, code),
-                //.bpfel => return Function(.bpfel).generateSymbol(bin_file, src, typed_value, code),
-                //.bpfeb => return Function(.bpfeb).generateSymbol(bin_file, src, typed_value, code),
-                //.hexagon => return Function(.hexagon).generateSymbol(bin_file, src, typed_value, code),
-                //.mips => return Function(.mips).generateSymbol(bin_file, src, typed_value, code),
-                //.mipsel => return Function(.mipsel).generateSymbol(bin_file, src, typed_value, code),
-                //.mips64 => return Function(.mips64).generateSymbol(bin_file, src, typed_value, code),
-                //.mips64el => return Function(.mips64el).generateSymbol(bin_file, src, typed_value, code),
-                //.msp430 => return Function(.msp430).generateSymbol(bin_file, src, typed_value, code),
-                //.powerpc => return Function(.powerpc).generateSymbol(bin_file, src, typed_value, code),
-                //.powerpc64 => return Function(.powerpc64).generateSymbol(bin_file, src, typed_value, code),
-                //.powerpc64le => return Function(.powerpc64le).generateSymbol(bin_file, src, typed_value, code),
-                //.r600 => return Function(.r600).generateSymbol(bin_file, src, typed_value, code),
-                //.amdgcn => return Function(.amdgcn).generateSymbol(bin_file, src, typed_value, code),
-                //.riscv32 => return Function(.riscv32).generateSymbol(bin_file, src, typed_value, code),
-                //.riscv64 => return Function(.riscv64).generateSymbol(bin_file, src, typed_value, code),
-                //.sparc => return Function(.sparc).generateSymbol(bin_file, src, typed_value, code),
-                //.sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code),
-                //.sparcel => return Function(.sparcel).generateSymbol(bin_file, src, typed_value, code),
-                //.s390x => return Function(.s390x).generateSymbol(bin_file, src, typed_value, code),
-                //.tce => return Function(.tce).generateSymbol(bin_file, src, typed_value, code),
-                //.tcele => return Function(.tcele).generateSymbol(bin_file, src, typed_value, code),
-                //.thumb => return Function(.thumb).generateSymbol(bin_file, src, typed_value, code),
-                //.thumbeb => return Function(.thumbeb).generateSymbol(bin_file, src, typed_value, code),
-                //.i386 => return Function(.i386).generateSymbol(bin_file, src, typed_value, code),
-                .x86_64 => return Function(.x86_64).generateSymbol(bin_file, src, typed_value, code),
-                //.xcore => return Function(.xcore).generateSymbol(bin_file, src, typed_value, code),
-                //.nvptx => return Function(.nvptx).generateSymbol(bin_file, src, typed_value, code),
-                //.nvptx64 => return Function(.nvptx64).generateSymbol(bin_file, src, typed_value, code),
-                //.le32 => return Function(.le32).generateSymbol(bin_file, src, typed_value, code),
-                //.le64 => return Function(.le64).generateSymbol(bin_file, src, typed_value, code),
-                //.amdil => return Function(.amdil).generateSymbol(bin_file, src, typed_value, code),
-                //.amdil64 => return Function(.amdil64).generateSymbol(bin_file, src, typed_value, code),
-                //.hsail => return Function(.hsail).generateSymbol(bin_file, src, typed_value, code),
-                //.hsail64 => return Function(.hsail64).generateSymbol(bin_file, src, typed_value, code),
-                //.spir => return Function(.spir).generateSymbol(bin_file, src, typed_value, code),
-                //.spir64 => return Function(.spir64).generateSymbol(bin_file, src, typed_value, code),
-                //.kalimba => return Function(.kalimba).generateSymbol(bin_file, src, typed_value, code),
-                //.shave => return Function(.shave).generateSymbol(bin_file, src, typed_value, code),
-                //.lanai => return Function(.lanai).generateSymbol(bin_file, src, typed_value, code),
-                //.wasm32 => return Function(.wasm32).generateSymbol(bin_file, src, typed_value, code),
-                //.wasm64 => return Function(.wasm64).generateSymbol(bin_file, src, typed_value, code),
-                //.renderscript32 => return Function(.renderscript32).generateSymbol(bin_file, src, typed_value, code),
-                //.renderscript64 => return Function(.renderscript64).generateSymbol(bin_file, src, typed_value, code),
-                //.ve => return Function(.ve).generateSymbol(bin_file, src, typed_value, code),
+                //.arm => return Function(.arm).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.armeb => return Function(.armeb).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.aarch64_be => return Function(.aarch64_be).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.aarch64_32 => return Function(.aarch64_32).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.arc => return Function(.arc).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.avr => return Function(.avr).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.bpfel => return Function(.bpfel).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.bpfeb => return Function(.bpfeb).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.hexagon => return Function(.hexagon).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.mips => return Function(.mips).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.mipsel => return Function(.mipsel).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.mips64 => return Function(.mips64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.mips64el => return Function(.mips64el).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.msp430 => return Function(.msp430).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.powerpc => return Function(.powerpc).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.powerpc64 => return Function(.powerpc64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.powerpc64le => return Function(.powerpc64le).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.r600 => return Function(.r600).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.amdgcn => return Function(.amdgcn).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.riscv32 => return Function(.riscv32).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.riscv64 => return Function(.riscv64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.sparc => return Function(.sparc).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.sparcel => return Function(.sparcel).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.s390x => return Function(.s390x).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.tce => return Function(.tce).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.tcele => return Function(.tcele).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.thumb => return Function(.thumb).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.thumbeb => return Function(.thumbeb).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.i386 => return Function(.i386).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                .x86_64 => return Function(.x86_64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.xcore => return Function(.xcore).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.nvptx => return Function(.nvptx).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.nvptx64 => return Function(.nvptx64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.le32 => return Function(.le32).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.le64 => return Function(.le64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.amdil => return Function(.amdil).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.amdil64 => return Function(.amdil64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.hsail => return Function(.hsail).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.hsail64 => return Function(.hsail64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.spir => return Function(.spir).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.spir64 => return Function(.spir64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.kalimba => return Function(.kalimba).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.shave => return Function(.shave).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.lanai => return Function(.lanai).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.wasm32 => return Function(.wasm32).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.wasm64 => return Function(.wasm64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.renderscript32 => return Function(.renderscript32).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.renderscript64 => return Function(.renderscript64).generateSymbol(bin_file, src, typed_value, code, dbg_line),
+                //.ve => return Function(.ve).generateSymbol(bin_file, src, typed_value, code, dbg_line),
                 else => @panic("Backend architectures that don't have good support yet are commented out, to improve compilation performance. If you are interested in one of these other backends feel free to uncomment them. Eventually these will be completed, but stage1 is slow and a memory hog."),
             }
         },
@@ -207,6 +212,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
         target: *const std.Target,
         mod_fn: *const Module.Fn,
         code: *std.ArrayList(u8),
+        dbg_line: *std.ArrayList(u8),
         err_msg: ?*ErrorMsg,
         args: []MCValue,
         ret_mcv: MCValue,
@@ -215,6 +221,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
         src: usize,
         stack_align: u32,
 
+        /// Byte offset within the source file.
+        prev_di_src: usize,
+        /// Relative to the beginning of `code`.
+        prev_di_pc: usize,
+        /// Used to find newlines and count line deltas.
+        source: []const u8,
+        /// Byte offset within the source file of the ending curly.
+        rbrace_src: usize,
+
         /// The value is an offset into the `Function` `code` from the beginning.
         /// To perform the reloc, write 32-bit signed little-endian integer
         /// which is a relative jump, based on the address following the reloc.
@@ -366,6 +381,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
             src: usize,
             typed_value: TypedValue,
             code: *std.ArrayList(u8),
+            dbg_line: *std.ArrayList(u8),
         ) GenerateSymbolError!Result {
             const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;
 
@@ -380,12 +396,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
             const branch = try branch_stack.addOne();
             branch.* = .{};
 
+            const scope_file = module_fn.owner_decl.scope.cast(Module.Scope.File).?;
+            const tree = scope_file.contents.tree;
+            const fn_proto = tree.root_node.decls()[module_fn.owner_decl.src_index].castTag(.FnProto).?;
+            const block = fn_proto.body().?.castTag(.Block).?;
+            const lbrace_src = tree.token_locs[block.lbrace].start;
+            const rbrace_src = tree.token_locs[block.rbrace].start;
+
             var function = Self{
                 .gpa = bin_file.allocator,
                 .target = &bin_file.base.options.target,
                 .bin_file = bin_file,
                 .mod_fn = module_fn,
                 .code = code,
+                .dbg_line = dbg_line,
                 .err_msg = null,
                 .args = undefined, // populated after `resolveCallingConventionValues`
                 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`
@@ -394,6 +418,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
                 .branch_stack = &branch_stack,
                 .src = src,
                 .stack_align = undefined,
+                .prev_di_pc = 0,
+                .prev_di_src = lbrace_src,
+                .rbrace_src = rbrace_src,
+                .source = tree.source,
             };
             defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
 
@@ -432,21 +460,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
                         // TODO During semantic analysis, check if there are no function calls. If there
                         // are none, here we can omit the part where we subtract and then add rsp.
                         self.code.appendSliceAssumeCapacity(&[_]u8{
-                            // push rbp
-                            0x55,
-                            // mov rbp, rsp
-                            0x48,
-                            0x89,
-                            0xe5,
-                            // sub rsp, imm32 (with reloc)
-                            0x48,
-                            0x81,
-                            0xec,
+                            0x55, // push rbp
+                            0x48, 0x89, 0xe5, // mov rbp, rsp
+                            0x48, 0x81, 0xec, // sub rsp, imm32 (with reloc)
                         });
                         const reloc_index = self.code.items.len;
                         self.code.items.len += 4;
 
+                        try self.dbgSetPrologueEnd();
                         try self.genBody(self.mod_fn.analysis.success);
+                        try self.dbgSetEpilogueBegin();
 
                         const stack_end = self.branch_stack.items[0].max_end_stack;
                         if (stack_end > math.maxInt(i32))
@@ -486,13 +509,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
                             0xc3, // ret
                         });
                     } else {
+                        try self.dbgSetPrologueEnd();
                         try self.genBody(self.mod_fn.analysis.success);
+                        try self.dbgSetEpilogueBegin();
                     }
                 },
                 else => {
+                    try self.dbgSetPrologueEnd();
                     try self.genBody(self.mod_fn.analysis.success);
+                    try self.dbgSetEpilogueBegin();
                 },
             }
+            // Drop them off at the rbrace.
+            try self.dbgAdvancePCAndLine(self.rbrace_src);
         }
 
         fn genBody(self: *Self, body: ir.Body) InnerError!void {
@@ -509,6 +538,38 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
             }
         }
 
+        fn dbgSetPrologueEnd(self: *Self) InnerError!void {
+            try self.dbg_line.append(DW.LNS_set_prologue_end);
+            try self.dbgAdvancePCAndLine(self.prev_di_src);
+        }
+
+        fn dbgSetEpilogueBegin(self: *Self) InnerError!void {
+            try self.dbg_line.append(DW.LNS_set_epilogue_begin);
+            try self.dbgAdvancePCAndLine(self.prev_di_src);
+        }
+
+        fn dbgAdvancePCAndLine(self: *Self, src: usize) InnerError!void {
+            // TODO Look into improving the performance here by adding a token-index-to-line
+            // lookup table, and changing ir.Inst from storing byte offset to token. Currently
+            // this involves scanning over the source code for newlines
+            // (but only from the previous byte offset to the new one).
+            const delta_line = std.zig.lineDelta(self.source, self.prev_di_src, src);
+            const delta_pc = self.code.items.len - self.prev_di_pc;
+            self.prev_di_src = src;
+            self.prev_di_pc = self.code.items.len;
+            // TODO Look into using the DWARF special opcodes to compress this data. It lets you emit
+            // single-byte opcodes that add different numbers to both the PC and the line number
+            // at the same time.
+            try self.dbg_line.ensureCapacity(self.dbg_line.items.len + 11);
+            self.dbg_line.appendAssumeCapacity(DW.LNS_advance_pc);
+            leb128.writeULEB128(self.dbg_line.writer(), delta_pc) catch unreachable;
+            if (delta_line != 0) {
+                self.dbg_line.appendAssumeCapacity(DW.LNS_advance_line);
+                leb128.writeULEB128(self.dbg_line.writer(), delta_line) catch unreachable;
+            }
+            self.dbg_line.appendAssumeCapacity(DW.LNS_copy);
+        }
+
         fn processDeath(self: *Self, inst: *ir.Inst) void {
             const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
             const entry = branch.inst_table.getEntry(inst) orelse return;
@@ -544,6 +605,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
                 .cmp_neq => return self.genCmp(inst.castTag(.cmp_neq).?, .neq),
                 .condbr => return self.genCondBr(inst.castTag(.condbr).?),
                 .constant => unreachable, // excluded from function bodies
+                .dbg_stmt => return self.genDbgStmt(inst.castTag(.dbg_stmt).?),
                 .floatcast => return self.genFloatCast(inst.castTag(.floatcast).?),
                 .intcast => return self.genIntCast(inst.castTag(.intcast).?),
                 .isnonnull => return self.genIsNonNull(inst.castTag(.isnonnull).?),
@@ -1107,6 +1169,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
             }
         }
 
+        fn genDbgStmt(self: *Self, inst: *ir.Inst.NoOp) !MCValue {
+            try self.dbgAdvancePCAndLine(inst.base.src);
+            return MCValue.none;
+        }
+
         fn genCondBr(self: *Self, inst: *ir.Inst.CondBr) !MCValue {
             switch (arch) {
                 .x86_64 => {
src-self-hosted/ir.zig
@@ -65,6 +65,7 @@ pub const Inst = struct {
         cmp_neq,
         condbr,
         constant,
+        dbg_stmt,
         isnonnull,
         isnull,
         /// Read a value from a pointer.
@@ -88,6 +89,7 @@ pub const Inst = struct {
                 .unreach,
                 .arg,
                 .breakpoint,
+                .dbg_stmt,
                 => NoOp,
 
                 .ref,
src-self-hosted/link.zig
@@ -2024,9 +2024,9 @@ pub const File = struct {
                         // line number of the open curly from the beginning of the file.
                         const fn_proto = file_ast_decls[decl.src_index].castTag(.FnProto).?;
                         const block = fn_proto.body().?.castTag(.Block).?;
-                        const loc = tree.tokenLocation(0, block.lbrace);
+                        const line_delta = std.zig.lineDelta(tree.source, 0, tree.token_locs[block.lbrace].start);
                         // No need to add one; this is a delta from DWARF's starting line number (1).
-                        break :blk @intCast(u28, loc.line);
+                        break :blk @intCast(u28, line_delta);
                     } else {
                         const prev_src_fn = src_file.fns.entries.items[src_fn_index - 1].key;
                         const mod_fn = @fieldParentPtr(Module.Fn, "link", prev_src_fn);
@@ -2035,9 +2035,12 @@ pub const File = struct {
                         const prev_block = prev_fn_proto.body().?.castTag(.Block).?;
                         const this_block = this_fn_proto.body().?.castTag(.Block).?;
                         // Find the difference between prev decl end curly and this decl begin curly.
-                        const loc = tree.tokenLocation(tree.token_locs[prev_block.rbrace].start, this_block.lbrace);
+                        const line_delta = std.zig.lineDelta(tree.source,
+                            tree.token_locs[prev_block.rbrace].start,
+                            tree.token_locs[this_block.lbrace].start,
+                        );
                         // No need to add one; this is a delta from the previous line number.
-                        break :blk @intCast(u28, loc.line);
+                        break :blk @intCast(u28, line_delta);
                     }
                 };
 
src-self-hosted/zir.zig
@@ -107,6 +107,8 @@ pub const Inst = struct {
         condbr,
         /// Special case, has no textual representation.
         @"const",
+        /// Declares the beginning of a statement. Used for debug info.
+        dbg_stmt,
         /// Represents a pointer to a global decl by name.
         declref,
         /// Represents a pointer to a global decl by string name.
@@ -211,6 +213,7 @@ pub const Inst = struct {
             return switch (tag) {
                 .arg,
                 .breakpoint,
+                .dbg_stmt,
                 .returnvoid,
                 .alloc_inferred,
                 .ret_ptr,
@@ -324,6 +327,7 @@ pub const Inst = struct {
                 .coerce_result_block_ptr,
                 .coerce_to_ptr_elem,
                 .@"const",
+                .dbg_stmt,
                 .declref,
                 .declref_str,
                 .declval,
@@ -1843,6 +1847,7 @@ const EmitZIR = struct {
                 .breakpoint => try self.emitNoOp(inst.src, .breakpoint),
                 .unreach => try self.emitNoOp(inst.src, .@"unreachable"),
                 .retvoid => try self.emitNoOp(inst.src, .returnvoid),
+                .dbg_stmt => try self.emitNoOp(inst.src, .dbg_stmt),
 
                 .not => try self.emitUnOp(inst.src, new_body, inst.castTag(.not).?, .boolnot),
                 .ret => try self.emitUnOp(inst.src, new_body, inst.castTag(.ret).?, .@"return"),
src-self-hosted/zir_sema.zig
@@ -41,6 +41,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
         .coerce_to_ptr_elem => return analyzeInstCoerceToPtrElem(mod, scope, old_inst.castTag(.coerce_to_ptr_elem).?),
         .compileerror => return analyzeInstCompileError(mod, scope, old_inst.castTag(.compileerror).?),
         .@"const" => return analyzeInstConst(mod, scope, old_inst.castTag(.@"const").?),
+        .dbg_stmt => return analyzeInstDbgStmt(mod, scope, old_inst.castTag(.dbg_stmt).?),
         .declref => return analyzeInstDeclRef(mod, scope, old_inst.castTag(.declref).?),
         .declref_str => return analyzeInstDeclRefStr(mod, scope, old_inst.castTag(.declref_str).?),
         .declval => return analyzeInstDeclVal(mod, scope, old_inst.castTag(.declval).?),
@@ -487,6 +488,11 @@ fn analyzeInstBreakVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.BreakVoid)
     return analyzeBreak(mod, scope, inst.base.src, block, void_inst);
 }
 
+fn analyzeInstDbgStmt(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
+    const b = try mod.requireRuntimeBlock(scope, inst.base.src);
+    return mod.addNoOp(b, inst.base.src, Type.initTag(.void), .dbg_stmt);
+}
+
 fn analyzeInstDeclRefStr(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclRefStr) InnerError!*Inst {
     const decl_name = try resolveConstString(mod, scope, inst.positionals.name);
     return mod.analyzeDeclRefByName(scope, inst.base.src, decl_name);