Commit 0343811836

Veikka Tuominen <git@vexu.eu>
2022-03-15 15:42:26
Sema: emit dbg_func around inline calls
1 parent 0f112b9
src/arch/aarch64/CodeGen.zig
@@ -598,6 +598,7 @@ 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),
@@ -2715,6 +2716,14 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
     return self.finishAirBookkeeping();
 }
 
+fn airDbgFunc(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
+    _ = function;
+    return self.finishAir(inst, .dead, .{ .none, .none, .none });
+}
+
 fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
     const pl_op = self.air.instructions.items(.data)[inst].pl_op;
     const name = self.air.nullTerminatedString(pl_op.payload);
src/arch/arm/CodeGen.zig
@@ -595,6 +595,7 @@ 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),
@@ -2835,6 +2836,14 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
     return self.finishAirBookkeeping();
 }
 
+fn airDbgFunc(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
+    _ = function;
+    return self.finishAir(inst, .dead, .{ .none, .none, .none });
+}
+
 fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
     const pl_op = self.air.instructions.items(.data)[inst].pl_op;
     const name = self.air.nullTerminatedString(pl_op.payload);
src/arch/riscv64/CodeGen.zig
@@ -562,6 +562,7 @@ 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),
@@ -1640,6 +1641,14 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
     return self.finishAirBookkeeping();
 }
 
+fn airDbgFunc(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
+    _ = function;
+    return self.finishAir(inst, .dead, .{ .none, .none, .none });
+}
+
 fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
     const pl_op = self.air.instructions.items(.data)[inst].pl_op;
     const name = self.air.nullTerminatedString(pl_op.payload);
src/arch/wasm/CodeGen.zig
@@ -1227,6 +1227,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
 
         // TODO
         .dbg_stmt,
+        .dbg_func,
         .dbg_var_ptr,
         .dbg_var_val,
         => WValue.none,
src/arch/x86_64/CodeGen.zig
@@ -679,6 +679,7 @@ 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),
@@ -3670,6 +3671,14 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
     return self.finishAirBookkeeping();
 }
 
+fn airDbgFunc(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
+    _ = function;
+    return self.finishAir(inst, .dead, .{ .none, .none, .none });
+}
+
 fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void {
     const pl_op = self.air.instructions.items(.data)[inst].pl_op;
     const name = self.air.nullTerminatedString(pl_op.payload);
src/codegen/c.zig
@@ -1687,6 +1687,7 @@ 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),
@@ -2660,6 +2661,14 @@ fn airDbgStmt(f: *Function, inst: Air.Inst.Index) !CValue {
     return CValue.none;
 }
 
+fn airDbgFunc(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;
+    try writer.print("/* dbg func:{s} */\n", .{function.owner_decl.name});
+    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);
src/codegen/llvm.zig
@@ -1754,11 +1754,14 @@ pub const DeclGen = struct {
     /// Note that this can be called before the function's semantic analysis has
     /// completed, so if any attributes rely on that, they must be done in updateFunc, not here.
     fn resolveLlvmFunction(dg: *DeclGen, decl: *Module.Decl) !*const llvm.Value {
+        return dg.resolveLlvmFunctionExtra(decl, decl.ty);
+    }
+
+    fn resolveLlvmFunctionExtra(dg: *DeclGen, decl: *Module.Decl, zig_fn_type: Type) !*const llvm.Value {
         const gop = try dg.object.decl_map.getOrPut(dg.gpa, decl);
         if (gop.found_existing) return gop.value_ptr.*;
 
         assert(decl.has_tv);
-        const zig_fn_type = decl.ty;
         const fn_info = zig_fn_type.fnInfo();
         const target = dg.module.getTarget();
         const sret = firstParamSRet(fn_info, target);
@@ -3460,6 +3463,7 @@ pub const FuncGen = struct {
                 .const_ty => unreachable,
                 .unreach  => self.airUnreach(inst),
                 .dbg_stmt => self.airDbgStmt(inst),
+                .dbg_func => try self.airDbgFunc(inst),
                 .dbg_var_ptr => try self.airDbgVarPtr(inst),
                 .dbg_var_val => try self.airDbgVarVal(inst),
                 // zig fmt: on
@@ -4181,6 +4185,45 @@ pub const FuncGen = struct {
         return null;
     }
 
+    fn airDbgFunc(self: *FuncGen, inst: Air.Inst.Index) !?*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 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 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
+        else
+            0;
+        const subprogram = dib.createFunction(
+            di_file.toScope(),
+            decl.name,
+            llvm_func.getValueName(),
+            di_file,
+            line_number,
+            try self.dg.lowerDebugType(fn_ty),
+            is_internal_linkage,
+            true, // is definition
+            line_number + function.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);
+
+        self.di_scope = subprogram.toScope();
+        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;
src/Air.zig
@@ -7,7 +7,6 @@ const std = @import("std");
 const builtin = @import("builtin");
 const Value = @import("value.zig").Value;
 const Type = @import("type.zig").Type;
-const Module = @import("Module.zig");
 const assert = std.debug.assert;
 const Air = @This();
 
@@ -327,6 +326,9 @@ 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.
+        /// Uses `ty_pl` with the payload being the index of a Value.Function in air.values.
+        dbg_func,
         /// 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.
@@ -971,6 +973,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
 
         .breakpoint,
         .dbg_stmt,
+        .dbg_func,
         .dbg_var_ptr,
         .dbg_var_val,
         .store,
src/Liveness.zig
@@ -314,6 +314,7 @@ fn analyzeInst(
         .const_ty,
         .breakpoint,
         .dbg_stmt,
+        .dbg_func,
         .unreach,
         .fence,
         .ret_addr,
src/print_air.zig
@@ -242,6 +242,7 @@ 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),
             .aggregate_init => try w.writeAggregateInit(s, inst),
             .union_init => try w.writeUnionInit(s, inst),
             .br => try w.writeBr(s, inst),
@@ -552,6 +553,12 @@ 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 {
+        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});
+    }
+
     fn writeDbgVar(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
         const pl_op = w.air.instructions.items(.data)[inst].pl_op;
         try w.writeOperand(s, inst, 0, pl_op.operand);
src/Sema.zig
@@ -4639,6 +4639,10 @@ fn analyzeCall(
         // comptime state.
         var should_memoize = true;
 
+        var new_fn_info = module_fn.owner_decl.ty.fnInfo();
+        new_fn_info.param_types = try sema.arena.alloc(Type, new_fn_info.param_types.len);
+        new_fn_info.comptime_params = (try sema.arena.alloc(bool, new_fn_info.param_types.len)).ptr;
+
         // This will have return instructions analyzed as break instructions to
         // the block_inst above. Here we are performing "comptime/inline semantic analysis"
         // for a function body, which means we must map the parameter ZIR instructions to
@@ -4658,6 +4662,7 @@ fn analyzeCall(
                 const param_body = sema.code.extra[extra.end..][0..extra.data.body_len];
                 const param_ty_inst = try sema.resolveBody(&child_block, param_body, inst);
                 const param_ty = try sema.analyzeAsType(&child_block, param_src, param_ty_inst);
+                new_fn_info.param_types[arg_i] = param_ty;
                 const arg_src = call_src; // TODO: better source location
                 const casted_arg = try sema.coerce(&child_block, param_ty, uncasted_args[arg_i], arg_src);
                 try sema.inst_map.putNoClobber(gpa, inst, casted_arg);
@@ -4685,6 +4690,7 @@ fn analyzeCall(
             .param_anytype, .param_anytype_comptime => {
                 // No coercion needed.
                 const uncasted_arg = uncasted_args[arg_i];
+                new_fn_info.param_types[arg_i] = sema.typeOf(uncasted_arg);
                 try sema.inst_map.putNoClobber(gpa, inst, uncasted_arg);
 
                 if (is_comptime_call) {
@@ -4735,6 +4741,7 @@ fn analyzeCall(
             }
             break :blk bare_return_type;
         };
+        new_fn_info.return_type = fn_ret_ty;
         const parent_fn_ret_ty = sema.fn_ret_ty;
         sema.fn_ret_ty = fn_ret_ty;
         defer sema.fn_ret_ty = parent_fn_ret_ty;
@@ -4757,6 +4764,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);
+
             const result = result: {
                 sema.analyzeBody(&child_block, fn_info.body) catch |err| switch (err) {
                     error.ComptimeReturn => break :result inlining.comptime_result,
@@ -4771,6 +4781,8 @@ 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 (should_memoize and is_comptime_call) {
                 const result_val = try sema.resolveConstMaybeUndefVal(block, call_src, result);
 
@@ -5175,6 +5187,26 @@ fn instantiateGenericCall(
     return func_inst;
 }
 
+fn emitDbgFunc(
+    sema: *Sema,
+    block: *Block,
+    old_func: *Module.Fn,
+    new_func: *Module.Fn,
+    new_func_ty: Type,
+) CompileError!void {
+    // No change of file; no dbg_func 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,
+        .data = .{ .ty_pl = .{
+            .ty = try sema.addType(new_func_ty),
+            .payload = @intCast(u32, sema.air_values.items.len - 1),
+        } },
+    });
+}
+
 fn zirIntType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     _ = block;
     const tracy = trace(@src());