From 03438118369605df178f4bfdb5b1a8ad6a349be7 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 15 Mar 2022 16:42:26 +0200 Subject: [PATCH] Sema: emit dbg_func around inline calls --- src/Air.zig | 5 +++- src/Liveness.zig | 1 + src/Sema.zig | 32 +++++++++++++++++++++++++ src/arch/aarch64/CodeGen.zig | 9 ++++++++ src/arch/arm/CodeGen.zig | 9 ++++++++ src/arch/riscv64/CodeGen.zig | 9 ++++++++ src/arch/wasm/CodeGen.zig | 1 + src/arch/x86_64/CodeGen.zig | 9 ++++++++ src/codegen/c.zig | 9 ++++++++ src/codegen/llvm.zig | 45 +++++++++++++++++++++++++++++++++++- src/print_air.zig | 7 ++++++ 11 files changed, 134 insertions(+), 2 deletions(-) diff --git a/src/Air.zig b/src/Air.zig index 7ccb6ebc96..2097be073a 100644 --- a/src/Air.zig +++ b/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, diff --git a/src/Liveness.zig b/src/Liveness.zig index 849b2c52c3..54acc62962 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -314,6 +314,7 @@ fn analyzeInst( .const_ty, .breakpoint, .dbg_stmt, + .dbg_func, .unreach, .fence, .ret_addr, diff --git a/src/Sema.zig b/src/Sema.zig index c8a1f6e343..81bdc821d8 100644 --- a/src/Sema.zig +++ b/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()); diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 9534e31a01..2dca8e2dfe 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/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); diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 5dda14a07d..10a6348d4f 100644 --- a/src/arch/arm/CodeGen.zig +++ b/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); diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index e25848cd85..1604f04526 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/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); diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 74af207a0a..849fc597c8 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/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, diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 34facf4773..cecff3b53b 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/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); diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 05ceb30379..c726f659a1 100644 --- a/src/codegen/c.zig +++ b/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); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index c4b381f6d6..e8c527c112 100644 --- a/src/codegen/llvm.zig +++ b/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; diff --git a/src/print_air.zig b/src/print_air.zig index d70fdf5c24..3d1d17107c 100644 --- a/src/print_air.zig +++ b/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);