Commit d83a26f068

Veikka Tuominen <git@vexu.eu>
2022-03-15 17:27:09
stage2 llvm: keep track of inlined functions
1 parent 0343811
src/arch/aarch64/CodeGen.zig
@@ -598,7 +598,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .fence           => try self.airFence(),
             .cond_br         => try self.airCondBr(inst),
             .dbg_stmt        => try self.airDbgStmt(inst),
-            .dbg_func        => try self.airDbgFunc(inst),
             .fptrunc         => try self.airFptrunc(inst),
             .fpext           => try self.airFpext(inst),
             .intcast         => try self.airIntCast(inst),
@@ -650,6 +649,10 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .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),
@@ -2716,7 +2719,7 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
     return self.finishAirBookkeeping();
 }
 
-fn airDbgFunc(self: *Self, inst: Air.Inst.Index) !void {
+fn airDbgInline(self: *Self, inst: Air.Inst.Index) !void {
     const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
     const function = self.air.values[ty_pl.payload].castTag(.function).?.data;
     // TODO emit debug info for function change
src/arch/arm/CodeGen.zig
@@ -595,7 +595,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .fence           => try self.airFence(),
             .cond_br         => try self.airCondBr(inst),
             .dbg_stmt        => try self.airDbgStmt(inst),
-            .dbg_func        => try self.airDbgFunc(inst),
             .fptrunc         => try self.airFptrunc(inst),
             .fpext           => try self.airFpext(inst),
             .intcast         => try self.airIntCast(inst),
@@ -647,6 +646,10 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .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),
@@ -2836,7 +2839,7 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
     return self.finishAirBookkeeping();
 }
 
-fn airDbgFunc(self: *Self, inst: Air.Inst.Index) !void {
+fn airDbgInline(self: *Self, inst: Air.Inst.Index) !void {
     const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
     const function = self.air.values[ty_pl.payload].castTag(.function).?.data;
     // TODO emit debug info for function change
src/arch/riscv64/CodeGen.zig
@@ -562,7 +562,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .fence           => try self.airFence(),
             .cond_br         => try self.airCondBr(inst),
             .dbg_stmt        => try self.airDbgStmt(inst),
-            .dbg_func        => try self.airDbgFunc(inst),
             .fptrunc         => try self.airFptrunc(inst),
             .fpext           => try self.airFpext(inst),
             .intcast         => try self.airIntCast(inst),
@@ -614,6 +613,10 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .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),
@@ -1641,7 +1644,7 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
     return self.finishAirBookkeeping();
 }
 
-fn airDbgFunc(self: *Self, inst: Air.Inst.Index) !void {
+fn airDbgInline(self: *Self, inst: Air.Inst.Index) !void {
     const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
     const function = self.air.values[ty_pl.payload].castTag(.function).?.data;
     // TODO emit debug info for function change
src/arch/wasm/CodeGen.zig
@@ -1227,7 +1227,8 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
 
         // TODO
         .dbg_stmt,
-        .dbg_func,
+        .dbg_inline_begin,
+        .dbg_inline_end,
         .dbg_var_ptr,
         .dbg_var_val,
         => WValue.none,
src/arch/x86_64/CodeGen.zig
@@ -679,7 +679,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .fence           => try self.airFence(),
             .cond_br         => try self.airCondBr(inst),
             .dbg_stmt        => try self.airDbgStmt(inst),
-            .dbg_func        => try self.airDbgFunc(inst),
             .fptrunc         => try self.airFptrunc(inst),
             .fpext           => try self.airFpext(inst),
             .intcast         => try self.airIntCast(inst),
@@ -731,6 +730,10 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .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),
@@ -3671,7 +3674,7 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
     return self.finishAirBookkeeping();
 }
 
-fn airDbgFunc(self: *Self, inst: Air.Inst.Index) !void {
+fn airDbgInline(self: *Self, inst: Air.Inst.Index) !void {
     const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
     const function = self.air.values[ty_pl.payload].castTag(.function).?.data;
     // TODO emit debug info for function change
src/codegen/llvm/bindings.zig
@@ -839,8 +839,8 @@ pub const Builder = opaque {
     pub const buildExactSDiv = LLVMBuildExactSDiv;
     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_uint, column: c_uint, scope: *DIScope) void;
+    pub const setCurrentDebugLocation = ZigLLVMSetCurrentDebugLocation2;
+    extern fn ZigLLVMSetCurrentDebugLocation2(builder: *const Builder, line: c_uint, column: c_uint, scope: *DIScope, inlined_at: ?*DILocation) void;
 
     pub const clearCurrentDebugLocation = ZigLLVMClearCurrentDebugLocation;
     extern fn ZigLLVMClearCurrentDebugLocation(builder: *const Builder) void;
@@ -1479,8 +1479,8 @@ pub const DISubprogram = opaque {
     extern fn ZigLLVMSubprogramReplaceLinkageName(subprogram: *DISubprogram, linkage_name: *MDString) void;
 };
 
-pub const getDebugLoc = ZigLLVMGetDebugLoc;
-extern fn ZigLLVMGetDebugLoc(line: c_uint, col: c_uint, scope: *DIScope) *DILocation;
+pub const getDebugLoc = ZigLLVMGetDebugLoc2;
+extern fn ZigLLVMGetDebugLoc2(line: c_uint, col: c_uint, scope: *DIScope, inlined_at: ?*DILocation) *DILocation;
 
 pub const DIBuilder = opaque {
     pub const dispose = ZigLLVMDisposeDIBuilder;
src/codegen/c.zig
@@ -1687,7 +1687,6 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
             .block            => try airBlock(f, inst),
             .bitcast          => try airBitcast(f, inst),
             .dbg_stmt         => try airDbgStmt(f, inst),
-            .dbg_func         => try airDbgFunc(f, inst),
             .intcast          => try airIntCast(f, inst),
             .trunc            => try airTrunc(f, inst),
             .bool_to_int      => try airBoolToInt(f, inst),
@@ -1727,6 +1726,10 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
             .dbg_var_val,
             => try airDbgVar(f, inst),
 
+            .dbg_inline_begin,
+            .dbg_inline_end,
+            => try airDbgInline(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),
@@ -2661,7 +2664,7 @@ fn airDbgStmt(f: *Function, inst: Air.Inst.Index) !CValue {
     return CValue.none;
 }
 
-fn airDbgFunc(f: *Function, inst: Air.Inst.Index) !CValue {
+fn airDbgInline(f: *Function, inst: Air.Inst.Index) !CValue {
     const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
     const writer = f.object.writer();
     const function = f.air.values[ty_pl.payload].castTag(.function).?.data;
src/codegen/llvm.zig
@@ -620,7 +620,8 @@ pub const Object = struct {
 
             llvm_func.fnSetSubprogram(subprogram);
 
-            di_scope = subprogram.toScope();
+            const lexical_block = dib.createLexicalBlock(subprogram.toScope(), di_file.?, line_number, 1);
+            di_scope = lexical_block.toScope();
         }
 
         var fg: FuncGen = .{
@@ -639,6 +640,7 @@ pub const Object = struct {
             .single_threaded = module.comp.bin_file.options.single_threaded,
             .di_scope = di_scope,
             .di_file = di_file,
+            .base_line = dg.decl.src_line,
             .prev_dbg_line = 0,
             .prev_dbg_column = 0,
         };
@@ -3210,9 +3212,13 @@ pub const FuncGen = struct {
     builder: *const llvm.Builder,
     di_scope: ?*llvm.DIScope,
     di_file: ?*llvm.DIFile,
+    base_line: u32,
     prev_dbg_line: c_uint,
     prev_dbg_column: c_uint,
 
+    /// Stack of locations where a call was inlined.
+    dbg_inlined: std.ArrayListUnmanaged(DbgState) = .{},
+
     /// 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.
     func_inst_table: std.AutoHashMapUnmanaged(Air.Inst.Ref, *const llvm.Value),
@@ -3240,11 +3246,13 @@ pub const FuncGen = struct {
 
     single_threaded: bool,
 
+    const DbgState = struct { loc: *llvm.DILocation, scope: *llvm.DIScope, base_line: u32 };
     const BreakBasicBlocks = std.ArrayListUnmanaged(*const llvm.BasicBlock);
     const BreakValues = std.ArrayListUnmanaged(*const llvm.Value);
 
     fn deinit(self: *FuncGen) void {
         self.builder.dispose();
+        self.dbg_inlined.deinit(self.gpa);
         self.func_inst_table.deinit(self.gpa);
         self.blocks.deinit(self.gpa);
     }
@@ -3463,7 +3471,8 @@ pub const FuncGen = struct {
                 .const_ty => unreachable,
                 .unreach  => self.airUnreach(inst),
                 .dbg_stmt => self.airDbgStmt(inst),
-                .dbg_func => try self.airDbgFunc(inst),
+                .dbg_inline_begin => try self.airDbgInline(inst, true),
+                .dbg_inline_end => try self.airDbgInline(inst, false),
                 .dbg_var_ptr => try self.airDbgVarPtr(inst),
                 .dbg_var_val => try self.airDbgVarVal(inst),
                 // zig fmt: on
@@ -4179,24 +4188,44 @@ 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.prev_dbg_line = @intCast(c_uint, self.dg.decl.src_line + dbg_stmt.line + 1);
+        self.prev_dbg_line = @intCast(c_uint, self.base_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);
+        const inlined_at = if (self.dbg_inlined.items.len > 0)
+            self.dbg_inlined.items[self.dbg_inlined.items.len - 1].loc
+        else
+            null;
+        self.builder.setCurrentDebugLocation(self.prev_dbg_line, self.prev_dbg_column, di_scope, inlined_at);
         return null;
     }
 
-    fn airDbgFunc(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
+    fn airDbgInline(self: *FuncGen, inst: Air.Inst.Index, start: bool) !?*const llvm.Value {
         const dib = self.dg.object.di_builder orelse return null;
         const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
-    
-        const function = self.air.values[ty_pl.payload].castTag(.function).?.data;
-        const decl = function.owner_decl;
+
+        const func = self.air.values[ty_pl.payload].castTag(.function).?.data;
+        const decl = func.owner_decl;
+        const di_file = try self.dg.object.getDIFile(self.gpa, decl.src_namespace.file_scope);
+        self.di_file = di_file;
+        const line_number = decl.src_line + 1;
+        const cur_debug_location = self.builder.getCurrentDebugLocation2();
+        if (start) {
+            try self.dbg_inlined.append(self.gpa, .{
+                .loc = @ptrCast(*llvm.DILocation, cur_debug_location),
+                .scope = self.di_scope.?,
+                .base_line = self.base_line,
+            });
+        } else {
+            const old = self.dbg_inlined.pop();
+            self.di_scope = old.scope;
+            self.base_line = old.base_line;
+            return null;
+        }
+
         const fn_ty = try self.air.getRefType(ty_pl.ty).copy(self.dg.object.type_map_arena.allocator());
-        const llvm_func = try self.dg.resolveLlvmFunctionExtra(decl, fn_ty);
+        const fqn = try decl.getFullyQualifiedName(self.gpa);
+        defer self.gpa.free(fqn);
         const fn_info = fn_ty.fnInfo();
-        const di_file = try self.dg.object.getDIFile(self.gpa, decl.src_namespace.file_scope);
 
-        const line_number = decl.src_line + 1;
         const is_internal_linkage = !self.dg.module.decl_exports.contains(decl);
         const noret_bit: c_uint = if (fn_info.return_type.isNoReturn())
             llvm.DIFlags.NoReturn
@@ -4205,22 +4234,23 @@ pub const FuncGen = struct {
         const subprogram = dib.createFunction(
             di_file.toScope(),
             decl.name,
-            llvm_func.getValueName(),
+            fqn,
             di_file,
             line_number,
-            try self.dg.lowerDebugType(fn_ty),
+            try self.dg.object.lowerDebugType(fn_ty, .full),
             is_internal_linkage,
             true, // is definition
-            line_number + function.lbrace_line, // scope line
+            line_number + func.lbrace_line, // scope line
             llvm.DIFlags.StaticMember | noret_bit,
             self.dg.module.comp.bin_file.options.optimize_mode != .Debug,
             null, // decl_subprogram
         );
-        try self.dg.object.di_map.put(self.gpa, decl, subprogram.toNode());
 
-        llvm_func.fnSetSubprogram(subprogram);
+        try self.dg.object.di_map.put(self.gpa, decl, subprogram.toNode());
 
-        self.di_scope = subprogram.toScope();
+        const lexical_block = dib.createLexicalBlock(subprogram.toScope(), di_file, line_number, 1);
+        self.di_scope = lexical_block.toScope();
+        self.base_line = decl.src_line;
         return null;
     }
 
@@ -4240,7 +4270,11 @@ pub const FuncGen = struct {
             true, // always preserve
             0, // flags
         );
-        const debug_loc = llvm.getDebugLoc(self.prev_dbg_line, self.prev_dbg_column, self.di_scope.?);
+        const inlined_at = if (self.dbg_inlined.items.len > 0)
+            self.dbg_inlined.items[self.dbg_inlined.items.len - 1].loc
+        else
+            null;
+        const debug_loc = llvm.getDebugLoc(self.prev_dbg_line, self.prev_dbg_column, self.di_scope.?, inlined_at);
         const insert_block = self.builder.getInsertBlock();
         _ = dib.insertDeclareAtEnd(operand, di_local_var, debug_loc, insert_block);
         return null;
@@ -4262,7 +4296,11 @@ pub const FuncGen = struct {
             true, // always preserve
             0, // flags
         );
-        const debug_loc = llvm.getDebugLoc(self.prev_dbg_line, self.prev_dbg_column, self.di_scope.?);
+        const inlined_at = if (self.dbg_inlined.items.len > 0)
+            self.dbg_inlined.items[self.dbg_inlined.items.len - 1].loc
+        else
+            null;
+        const debug_loc = llvm.getDebugLoc(self.prev_dbg_line, self.prev_dbg_column, self.di_scope.?, inlined_at);
         const insert_block = self.builder.getInsertBlock();
         if (isByRef(operand_ty)) {
             _ = dib.insertDeclareAtEnd(operand, di_local_var, debug_loc, insert_block);
@@ -5524,7 +5562,7 @@ pub const FuncGen = struct {
                 self.arg_index, // includes +1 because 0 is return type
             );
 
-            const debug_loc = llvm.getDebugLoc(lbrace_line, lbrace_col, self.di_scope.?);
+            const debug_loc = llvm.getDebugLoc(lbrace_line, lbrace_col, self.di_scope.?, null);
             const insert_block = self.builder.getInsertBlock();
             if (isByRef(inst_ty)) {
                 _ = dib.insertDeclareAtEnd(arg_val, di_local_var, debug_loc, insert_block);
src/Air.zig
@@ -326,9 +326,12 @@ pub const Inst = struct {
         /// Result type is always void.
         /// Uses the `dbg_stmt` field.
         dbg_stmt,
-        /// Marks change of source function. Emitted around an inline call.
+        /// Marks the start of an inline call.
         /// Uses `ty_pl` with the payload being the index of a Value.Function in air.values.
-        dbg_func,
+        dbg_inline_begin,
+        /// Marks the end of an inline call.
+        /// Uses `ty_pl` with the payload being the index of a Value.Function in air.values.
+        dbg_inline_end,
         /// 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.
@@ -973,7 +976,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
 
         .breakpoint,
         .dbg_stmt,
-        .dbg_func,
+        .dbg_inline_begin,
+        .dbg_inline_end,
         .dbg_var_ptr,
         .dbg_var_val,
         .store,
src/Liveness.zig
@@ -314,7 +314,8 @@ fn analyzeInst(
         .const_ty,
         .breakpoint,
         .dbg_stmt,
-        .dbg_func,
+        .dbg_inline_begin,
+        .dbg_inline_end,
         .unreach,
         .fence,
         .ret_addr,
src/print_air.zig
@@ -242,7 +242,8 @@ const Writer = struct {
             .constant => try w.writeConstant(s, inst),
             .assembly => try w.writeAssembly(s, inst),
             .dbg_stmt => try w.writeDbgStmt(s, inst),
-            .dbg_func => try w.writeDbgFunc(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),
@@ -553,7 +554,7 @@ const Writer = struct {
         try s.print("{d}:{d}", .{ dbg_stmt.line + 1, dbg_stmt.column + 1 });
     }
 
-    fn writeDbgFunc(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
+    fn writeDbgInline(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
         const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
         const function = w.air.values[ty_pl.payload].castTag(.function).?.data;
         try s.print("{s}", .{function.owner_decl.name});
src/Sema.zig
@@ -4765,7 +4765,9 @@ fn analyzeCall(
             }
 
             const new_func_resolved_ty = try Type.Tag.function.create(sema.arena, new_fn_info);
-            if (!is_comptime_call) try sema.emitDbgFunc(block, parent_func.?, module_fn, new_func_resolved_ty);
+            if (!is_comptime_call) {
+                try sema.emitDbgInline(block, parent_func.?, module_fn, new_func_resolved_ty, .dbg_inline_begin);
+            }
 
             const result = result: {
                 sema.analyzeBody(&child_block, fn_info.body) catch |err| switch (err) {
@@ -4781,7 +4783,9 @@ fn analyzeCall(
                 break :result try sema.analyzeBlockBody(block, call_src, &child_block, merges);
             };
 
-            if (!is_comptime_call) try sema.emitDbgFunc(block, module_fn, parent_func.?, parent_func.?.owner_decl.ty);
+            if (!is_comptime_call) {
+                try sema.emitDbgInline(block, module_fn, parent_func.?, parent_func.?.owner_decl.ty, .dbg_inline_end);
+            }
 
             if (should_memoize and is_comptime_call) {
                 const result_val = try sema.resolveConstMaybeUndefVal(block, call_src, result);
@@ -5187,19 +5191,20 @@ fn instantiateGenericCall(
     return func_inst;
 }
 
-fn emitDbgFunc(
+fn emitDbgInline(
     sema: *Sema,
     block: *Block,
     old_func: *Module.Fn,
     new_func: *Module.Fn,
     new_func_ty: Type,
+    tag: Air.Inst.Tag,
 ) CompileError!void {
-    // No change of file; no dbg_func needed.
+    // No change of file; no dbg_inline needed.
     if (old_func == new_func) return;
 
     try sema.air_values.append(sema.gpa, try Value.Tag.function.create(sema.arena, new_func));
     _ = try block.addInst(.{
-        .tag = .dbg_func,
+        .tag = tag,
         .data = .{ .ty_pl = .{
             .ty = try sema.addType(new_func_ty),
             .payload = @intCast(u32, sema.air_values.items.len - 1),
src/zig_llvm.cpp
@@ -799,6 +799,15 @@ void ZigLLVMSetCurrentDebugLocation(LLVMBuilderRef builder,
     unwrap(builder)->SetCurrentDebugLocation(debug_loc);
 }
 
+void ZigLLVMSetCurrentDebugLocation2(LLVMBuilderRef builder, unsigned int line,
+        unsigned int column, ZigLLVMDIScope *scope, ZigLLVMDILocation *inlined_at)
+{
+    DIScope* di_scope = reinterpret_cast<DIScope*>(scope);
+    DebugLoc debug_loc = DILocation::get(di_scope->getContext(), line, column, di_scope, 
+        reinterpret_cast<DILocation *>(inlined_at), false);
+    unwrap(builder)->SetCurrentDebugLocation(debug_loc);
+}
+
 void ZigLLVMClearCurrentDebugLocation(LLVMBuilderRef builder) {
     unwrap(builder)->SetCurrentDebugLocation(DebugLoc());
 }
@@ -1025,6 +1034,14 @@ ZigLLVMDILocation *ZigLLVMGetDebugLoc(unsigned line, unsigned col, ZigLLVMDIScop
     return reinterpret_cast<ZigLLVMDILocation*>(debug_loc.get());
 }
 
+ZigLLVMDILocation *ZigLLVMGetDebugLoc2(unsigned line, unsigned col, ZigLLVMDIScope *scope,
+        ZigLLVMDILocation *inlined_at) {
+    DIScope* di_scope = reinterpret_cast<DIScope*>(scope);
+    DebugLoc debug_loc = DILocation::get(di_scope->getContext(), line, col, di_scope,
+        reinterpret_cast<DILocation *>(inlined_at), false);
+    return reinterpret_cast<ZigLLVMDILocation*>(debug_loc.get());
+}
+
 void ZigLLVMSetFastMath(LLVMBuilderRef builder_wrapped, bool on_state) {
     if (on_state) {
         FastMathFlags fmf;
src/zig_llvm.h
@@ -232,6 +232,8 @@ ZIG_EXTERN_C void ZigLLVMSetModuleCodeModel(LLVMModuleRef module, LLVMCodeModel
 
 ZIG_EXTERN_C void ZigLLVMSetCurrentDebugLocation(LLVMBuilderRef builder,
         unsigned int line, unsigned int column, struct ZigLLVMDIScope *scope);
+ZIG_EXTERN_C void ZigLLVMSetCurrentDebugLocation2(LLVMBuilderRef builder, unsigned int line,
+        unsigned int column, ZigLLVMDIScope *scope, ZigLLVMDILocation *inlined_at);
 ZIG_EXTERN_C void ZigLLVMClearCurrentDebugLocation(LLVMBuilderRef builder);
 
 ZIG_EXTERN_C struct ZigLLVMDIScope *ZigLLVMLexicalBlockToScope(struct ZigLLVMDILexicalBlock *lexical_block);
@@ -290,6 +292,8 @@ ZIG_EXTERN_C void ZigLLVMDIBuilderFinalize(struct ZigLLVMDIBuilder *dibuilder);
 
 ZIG_EXTERN_C struct ZigLLVMDILocation *ZigLLVMGetDebugLoc(unsigned line, unsigned col,
         struct ZigLLVMDIScope *scope);
+ZIG_EXTERN_C struct ZigLLVMDILocation *ZigLLVMGetDebugLoc2(unsigned line, unsigned col,
+        ZigLLVMDIScope *scope, ZigLLVMDILocation *inlined_at);
 
 ZIG_EXTERN_C LLVMValueRef ZigLLVMInsertDeclareAtEnd(struct ZigLLVMDIBuilder *dib,
         LLVMValueRef storage, struct ZigLLVMDILocalVariable *var_info,