From 0bc96354909c0828b7fd36ce0be5705b7b21d63c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 12 Mar 2022 19:55:48 -0700 Subject: stage2: add debug info for locals in the LLVM backend Adds 2 new AIR instructions: * dbg_var_ptr * dbg_var_val Sema no longer emits dbg_stmt AIR instructions when strip=true. LLVM backend: fixed lowerPtrToVoid when calling ptrAlignment on the element type is problematic. LLVM backend: fixed alloca instructions improperly getting debug location annotated, causing chaotic debug info behavior. zig_llvm.cpp: fixed incorrect bindings for a function that should use unsigned integers for line and column. A bunch of C test cases regressed because the new dbg_var AIR instructions caused their operands to be alive, exposing latent bugs. Mostly it's just a problem that the C backend lowers mutable and const slices to the same C type, so we need to represent that in the C backend instead of printing two duplicate typedefs. --- src/codegen/c.zig | 14 ++++++++ src/codegen/llvm.zig | 75 +++++++++++++++++++++++++++++++++++++------ src/codegen/llvm/bindings.zig | 2 +- 3 files changed, 81 insertions(+), 10 deletions(-) (limited to 'src/codegen') diff --git a/src/codegen/c.zig b/src/codegen/c.zig index f1da1fdc0c..5ab84520a4 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1721,6 +1721,10 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .union_init => try airUnionInit(f, inst), .prefetch => try airPrefetch(f, inst), + .dbg_var_ptr, + .dbg_var_val, + => try airDbgVar(f, inst), + .call => try airCall(f, inst, .auto), .call_always_tail => try airCall(f, inst, .always_tail), .call_never_tail => try airCall(f, inst, .never_tail), @@ -2651,6 +2655,16 @@ fn airDbgStmt(f: *Function, inst: Air.Inst.Index) !CValue { return CValue.none; } +fn airDbgVar(f: *Function, inst: Air.Inst.Index) !CValue { + const pl_op = f.air.instructions.items(.data)[inst].pl_op; + const name = f.air.nullTerminatedString(pl_op.payload); + const operand = try f.resolveInst(pl_op.operand); + _ = operand; + const writer = f.object.writer(); + try writer.print("/* var:{s} */\n", .{name}); + return CValue.none; +} + fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue { const ty_pl = f.air.instructions.items(.data)[inst].ty_pl; const extra = f.air.extraData(Air.Block, ty_pl.payload); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index c942a975df..c1c7ac06f0 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -613,6 +613,8 @@ pub const Object = struct { .single_threaded = module.comp.bin_file.options.single_threaded, .di_scope = di_scope, .di_file = di_file, + .prev_dbg_line = 0, + .prev_dbg_column = 0, }; defer fg.deinit(); @@ -2885,8 +2887,7 @@ pub const DeclGen = struct { } fn lowerPtrToVoid(dg: *DeclGen, ptr_ty: Type) !*const llvm.Value { - const target = dg.module.getTarget(); - const alignment = ptr_ty.ptrAlignment(target); + const alignment = ptr_ty.ptrInfo().data.@"align"; // Even though we are pointing at something which has zero bits (e.g. `void`), // Pointers are defined to have bits. So we must return something here. // The value cannot be undefined, because we use the `nonnull` annotation @@ -2902,6 +2903,7 @@ pub const DeclGen = struct { // have an "undef_but_not_null" attribute. As an example, if this `alloc` AIR // instruction is followed by a `wrap_optional`, it will return this value // verbatim, and the result should test as non-null. + const target = dg.module.getTarget(); const int = switch (target.cpu.arch.ptrBitWidth()) { 32 => llvm_usize.constInt(0xaaaaaaaa, .False), 64 => llvm_usize.constInt(0xaaaaaaaa_aaaaaaaa, .False), @@ -3004,6 +3006,8 @@ pub const FuncGen = struct { builder: *const llvm.Builder, di_scope: ?*llvm.DIScope, di_file: ?*llvm.DIFile, + prev_dbg_line: c_uint, + prev_dbg_column: c_uint, /// This stores the LLVM values used in a function, such that they can be referred to /// in other instructions. This table is cleared before every function is generated. @@ -3255,6 +3259,8 @@ pub const FuncGen = struct { .const_ty => unreachable, .unreach => self.airUnreach(inst), .dbg_stmt => self.airDbgStmt(inst), + .dbg_var_ptr => try self.airDbgVarPtr(inst), + .dbg_var_val => try self.airDbgVarVal(inst), // zig fmt: on }; if (opt_value) |val| { @@ -3967,11 +3973,56 @@ pub const FuncGen = struct { fn airDbgStmt(self: *FuncGen, inst: Air.Inst.Index) ?*const llvm.Value { const di_scope = self.di_scope orelse return null; const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt; - self.builder.setCurrentDebugLocation( - @intCast(c_int, self.dg.decl.src_line + dbg_stmt.line + 1), - @intCast(c_int, dbg_stmt.column + 1), - di_scope, + self.prev_dbg_line = @intCast(c_uint, self.dg.decl.src_line + dbg_stmt.line + 1); + self.prev_dbg_column = @intCast(c_uint, dbg_stmt.column + 1); + self.builder.setCurrentDebugLocation(self.prev_dbg_line, self.prev_dbg_column, di_scope); + return null; + } + + fn airDbgVarPtr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + const dib = self.dg.object.di_builder orelse return null; + const pl_op = self.air.instructions.items(.data)[inst].pl_op; + const operand = try self.resolveInst(pl_op.operand); + const name = self.air.nullTerminatedString(pl_op.payload); + + const di_local_var = dib.createAutoVariable( + self.di_scope.?, + name.ptr, + self.di_file.?, + self.prev_dbg_line, + try self.dg.lowerDebugType(self.air.typeOf(pl_op.operand)), + true, // always preserve + 0, // flags + ); + const debug_loc = llvm.getDebugLoc(self.prev_dbg_line, self.prev_dbg_column, self.di_scope.?); + const insert_block = self.builder.getInsertBlock(); + _ = dib.insertDeclareAtEnd(operand, di_local_var, debug_loc, insert_block); + return null; + } + + fn airDbgVarVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + const dib = self.dg.object.di_builder orelse return null; + const pl_op = self.air.instructions.items(.data)[inst].pl_op; + const operand = try self.resolveInst(pl_op.operand); + const operand_ty = self.air.typeOf(pl_op.operand); + const name = self.air.nullTerminatedString(pl_op.payload); + + const di_local_var = dib.createAutoVariable( + self.di_scope.?, + name.ptr, + self.di_file.?, + self.prev_dbg_line, + try self.dg.lowerDebugType(operand_ty), + true, // always preserve + 0, // flags ); + const debug_loc = llvm.getDebugLoc(self.prev_dbg_line, self.prev_dbg_column, self.di_scope.?); + const insert_block = self.builder.getInsertBlock(); + if (isByRef(operand_ty)) { + _ = dib.insertDeclareAtEnd(operand, di_local_var, debug_loc, insert_block); + } else { + _ = dib.insertDbgValueIntrinsicAtEnd(operand, di_local_var, debug_loc, insert_block); + } return null; } @@ -5272,6 +5323,13 @@ pub const FuncGen = struct { /// put the alloca instruction at the top of the function! fn buildAlloca(self: *FuncGen, llvm_ty: *const llvm.Type) *const llvm.Value { const prev_block = self.builder.getInsertBlock(); + const prev_debug_location = self.builder.getCurrentDebugLocation2(); + defer { + self.builder.positionBuilderAtEnd(prev_block); + if (self.di_scope != null) { + self.builder.setCurrentDebugLocation2(prev_debug_location); + } + } const entry_block = self.llvm_func.getFirstBasicBlock().?; if (entry_block.getFirstInstruction()) |first_inst| { @@ -5279,10 +5337,9 @@ pub const FuncGen = struct { } else { self.builder.positionBuilderAtEnd(entry_block); } + self.builder.clearCurrentDebugLocation(); - const alloca = self.builder.buildAlloca(llvm_ty, ""); - self.builder.positionBuilderAtEnd(prev_block); - return alloca; + return self.builder.buildAlloca(llvm_ty, ""); } fn airStore(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index f590e46c23..6afd754d37 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -840,7 +840,7 @@ pub const Builder = opaque { extern fn LLVMBuildExactSDiv(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; pub const setCurrentDebugLocation = ZigLLVMSetCurrentDebugLocation; - extern fn ZigLLVMSetCurrentDebugLocation(builder: *const Builder, line: c_int, column: c_int, scope: *DIScope) void; + extern fn ZigLLVMSetCurrentDebugLocation(builder: *const Builder, line: c_uint, column: c_uint, scope: *DIScope) void; pub const clearCurrentDebugLocation = ZigLLVMClearCurrentDebugLocation; extern fn ZigLLVMClearCurrentDebugLocation(builder: *const Builder) void; -- cgit v1.2.3