From c6160fa3a5c2f55871c2ab2377531c8a4e34a76a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 7 Mar 2022 15:43:20 -0700 Subject: [PATCH] LLVM: add compile unit to debug info This commit also adds a bunch of bindings for debug info. --- src/Compilation.zig | 2 +- src/codegen/llvm.zig | 66 +++++++- src/codegen/llvm/bindings.zig | 300 +++++++++++++++++++++++++++++++++- src/link.zig | 2 +- 4 files changed, 362 insertions(+), 8 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index b2ac9b249c..fa2e4ca68b 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -898,7 +898,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { // We put the `Compilation` itself in the arena. Freeing the arena will free the module. // It's initialized later after we prepare the initialization options. const comp = try arena.create(Compilation); - const root_name = try arena.dupe(u8, options.root_name); + const root_name = try arena.dupeZ(u8, options.root_name); const ofmt = options.object_format orelse options.target.getObjectFormat(); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index c837d9a00d..c6ada88aa4 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -5,12 +5,14 @@ const Allocator = std.mem.Allocator; const log = std.log.scoped(.codegen); const math = std.math; const native_endian = builtin.cpu.arch.endian(); +const DW = std.dwarf; const llvm = @import("llvm/bindings.zig"); const link = @import("../link.zig"); const Compilation = @import("../Compilation.zig"); const build_options = @import("build_options"); const Module = @import("../Module.zig"); +const Package = @import("../Package.zig"); const TypedValue = @import("../TypedValue.zig"); const Air = @import("../Air.zig"); const Liveness = @import("../Liveness.zig"); @@ -159,6 +161,7 @@ pub fn targetTriple(allocator: Allocator, target: std.Target) ![:0]u8 { pub const Object = struct { llvm_module: *const llvm.Module, + dibuilder: ?*llvm.DIBuilder, context: *const llvm.Context, target_machine: *const llvm.TargetMachine, target_data: *const llvm.TargetData, @@ -180,8 +183,9 @@ pub const Object = struct { /// The backing memory for `type_map`. Periodically garbage collected after flush(). /// The code for doing the periodical GC is not yet implemented. type_map_arena: std.heap.ArenaAllocator, - /// The LLVM global table which holds the names corresponding to Zig errors. Note that the values - /// are not added until flushModule, when all errors in the compilation are known. + /// The LLVM global table which holds the names corresponding to Zig errors. + /// Note that the values are not added until flushModule, when all errors in + /// the compilation are known. error_name_table: ?*const llvm.Value, pub const TypeMap = std.HashMapUnmanaged( @@ -204,9 +208,7 @@ pub const Object = struct { initializeLLVMTarget(options.target.cpu.arch); - const root_nameZ = try gpa.dupeZ(u8, options.root_name); - defer gpa.free(root_nameZ); - const llvm_module = llvm.Module.createWithName(root_nameZ.ptr, context); + const llvm_module = llvm.Module.createWithName(options.root_name.ptr, context); errdefer llvm_module.dispose(); const llvm_target_triple = try targetTriple(gpa, options.target); @@ -221,6 +223,58 @@ pub const Object = struct { return error.InvalidLlvmTriple; } + llvm_module.setTarget(llvm_target_triple.ptr); + var opt_dbuilder: ?*llvm.DIBuilder = null; + errdefer if (opt_dbuilder) |dibuilder| dibuilder.dispose(); + + if (!options.strip) { + switch (options.object_format) { + .coff => llvm_module.addModuleCodeViewFlag(), + else => llvm_module.addModuleDebugInfoFlag(), + } + const dibuilder = llvm_module.createDIBuilder(true); + opt_dbuilder = dibuilder; + + // Don't use the version string here; LLVM misparses it when it + // includes the git revision. + const producer = try std.fmt.allocPrintZ(gpa, "zig {d}.{d}.{d}", .{ + build_options.semver.major, + build_options.semver.minor, + build_options.semver.patch, + }); + defer gpa.free(producer); + + // For macOS stack traces, we want to avoid having to parse the compilation unit debug + // info. As long as each debug info file has a path independent of the compilation unit + // directory (DW_AT_comp_dir), then we never have to look at the compilation unit debug + // info. If we provide an absolute path to LLVM here for the compilation unit debug + // info, LLVM will emit DWARF info that depends on DW_AT_comp_dir. To avoid this, we + // pass "." for the compilation unit directory. This forces each debug file to have a + // directory rather than be relative to DW_AT_comp_dir. According to DWARF 5, debug + // files will no longer reference DW_AT_comp_dir, for the purpose of being able to + // support the common practice of stripping all but the line number sections from an + // executable. + const compile_unit_dir = d: { + if (options.target.isDarwin()) break :d "."; + const mod = options.module orelse break :d "."; + break :d mod.root_pkg.root_src_directory.path orelse "."; + }; + const compile_unit_dir_z = try gpa.dupeZ(u8, compile_unit_dir); + defer gpa.free(compile_unit_dir_z); + + _ = dibuilder.createCompileUnit( + DW.LANG.C99, + dibuilder.createFile(options.root_name, compile_unit_dir_z), + producer, + options.optimize_mode != .Debug, + "", // flags + 0, // runtime version + "", // split name + 0, // dwo id + true, // emit debug info + ); + } + const opt_level: llvm.CodeGenOptLevel = if (options.optimize_mode == .Debug) .None else @@ -266,6 +320,7 @@ pub const Object = struct { return Object{ .llvm_module = llvm_module, + .dibuilder = opt_dbuilder, .context = context, .target_machine = target_machine, .target_data = target_data, @@ -277,6 +332,7 @@ pub const Object = struct { } pub fn deinit(self: *Object, gpa: Allocator) void { + if (self.dibuilder) |dib| dib.dispose(); self.target_data.dispose(); self.target_machine.dispose(); self.llvm_module.dispose(); diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index fd095ae511..35fd95feef 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -184,6 +184,9 @@ pub const Value = opaque { pub const setFunctionCallConv = LLVMSetFunctionCallConv; extern fn LLVMSetFunctionCallConv(Fn: *const Value, CC: CallConv) void; + pub const fnSetSubprogram = ZigLLVMFnSetSubprogram; + extern fn ZigLLVMFnSetSubprogram(f: *const Value, subprogram: *DISubprogram) void; + pub const setValueName = LLVMSetValueName; extern fn LLVMSetValueName(Val: *const Value, Name: [*:0]const u8) void; @@ -354,6 +357,18 @@ pub const Module = opaque { Name: [*:0]const u8, NameLen: usize, ) ?*const Value; + + pub const setTarget = LLVMSetTarget; + extern fn LLVMSetTarget(M: *const Module, Triple: [*:0]const u8) void; + + pub const addModuleDebugInfoFlag = ZigLLVMAddModuleDebugInfoFlag; + extern fn ZigLLVMAddModuleDebugInfoFlag(module: *const Module) void; + + pub const addModuleCodeViewFlag = ZigLLVMAddModuleCodeViewFlag; + extern fn ZigLLVMAddModuleCodeViewFlag(module: *const Module) void; + + pub const createDIBuilder = ZigLLVMCreateDIBuilder; + extern fn ZigLLVMCreateDIBuilder(module: *const Module, allow_unresolved: bool) *DIBuilder; }; pub const lookupIntrinsicID = LLVMLookupIntrinsicID; @@ -1203,7 +1218,7 @@ pub const WriteImportLibrary = ZigLLVMWriteImportLibrary; extern fn ZigLLVMWriteImportLibrary( def_path: [*:0]const u8, arch: ArchType, - output_lib_path: [*c]const u8, + output_lib_path: [*:0]const u8, kill_at: bool, ) bool; @@ -1400,3 +1415,286 @@ pub const address_space = struct { pub const constant_buffer_15: c_uint = 23; }; }; + +pub const DIEnumerator = opaque {}; +pub const DILocalVariable = opaque {}; +pub const DIGlobalVariable = opaque {}; +pub const DILocation = opaque {}; + +pub const DIType = opaque { + pub const toScope = ZigLLVMTypeToScope; + extern fn ZigLLVMTypeToScope(ty: *DIType) *DIScope; +}; +pub const DIFile = opaque { + pub const toScope = ZigLLVMFileToScope; + extern fn ZigLLVMFileToScope(difile: *DIFile) *DIScope; +}; +pub const DILexicalBlock = opaque { + pub const toScope = ZigLLVMLexicalBlockToScope; + extern fn ZigLLVMLexicalBlockToScope(lexical_block: *DILexicalBlock) *DIScope; +}; +pub const DICompileUnit = opaque { + pub const toScope = ZigLLVMCompileUnitToScope; + extern fn ZigLLVMCompileUnitToScope(compile_unit: *DICompileUnit) *DIScope; +}; +pub const DISubprogram = opaque { + pub const toScope = ZigLLVMSubprogramToScope; + extern fn ZigLLVMSubprogramToScope(subprogram: *DISubprogram) *DIScope; +}; + +pub const getDebugLoc = ZigLLVMGetDebugLoc; +extern fn ZigLLVMGetDebugLoc(line: c_uint, col: c_uint, scope: *DIScope) *DILocation; + +pub const DIBuilder = opaque { + pub const dispose = ZigLLVMDisposeDIBuilder; + extern fn ZigLLVMDisposeDIBuilder(dib: *DIBuilder) void; + + pub const finalize = ZigLLVMDIBuilderFinalize; + extern fn ZigLLVMDIBuilderFinalize(dib: *DIBuilder) void; + + pub const createPointerType = ZigLLVMCreateDebugPointerType; + extern fn ZigLLVMCreateDebugPointerType( + dib: *DIBuilder, + pointee_type: *DIType, + size_in_bits: u64, + align_in_bits: u64, + name: [*:0]const u8, + ) *DIType; + + pub const createBasicType = ZigLLVMCreateDebugBasicType; + extern fn ZigLLVMCreateDebugBasicType( + dib: *DIBuilder, + name: [*:0]const u8, + size_in_bits: u64, + encoding: c_uint, + ) *DIType; + + pub const createArrayType = ZigLLVMCreateDebugArrayType; + extern fn ZigLLVMCreateDebugArrayType( + dib: *DIBuilder, + size_in_bits: u64, + align_in_bits: u64, + elem_type: *DIType, + elem_count: c_int, + ) *DIType; + + pub const createEnumerator = ZigLLVMCreateDebugEnumerator; + extern fn ZigLLVMCreateDebugEnumerator( + dib: *DIBuilder, + name: [*:0]const u8, + val: i64, + ) *DIEnumerator; + + pub const createEnumerationType = ZigLLVMCreateDebugEnumerationType; + extern fn ZigLLVMCreateDebugEnumerationType( + dib: *DIBuilder, + scope: *DIScope, + name: [*:0]const u8, + file: *DIFile, + line_number: c_uint, + size_in_bits: u64, + align_in_bits: u64, + enumerator_array: [*]const *DIEnumerator, + enumerator_array_len: c_int, + underlying_type: *DIType, + unique_id: [*:0]const u8, + ) *DIType; + + pub const createStructType = ZigLLVMCreateDebugStructType; + extern fn ZigLLVMCreateDebugStructType( + dib: *DIBuilder, + scope: *DIScope, + name: [*:0]const u8, + file: *DIFile, + line_number: c_uint, + size_in_bits: u64, + align_in_bits: u64, + flags: c_uint, + derived_from: *DIType, + types_array: [*]const *DIType, + types_array_len: c_int, + run_time_lang: c_uint, + vtable_holder: *DIType, + unique_id: [*:0]const u8, + ) *DIType; + + pub const createUnionType = ZigLLVMCreateDebugUnionType; + extern fn ZigLLVMCreateDebugUnionType( + dib: *DIBuilder, + scope: *DIScope, + name: [*:0]const u8, + file: *DIFile, + line_number: c_uint, + size_in_bits: u64, + align_in_bits: u64, + flags: c_uint, + types_array: [*]const *DIType, + types_array_len: c_int, + run_time_lang: c_uint, + unique_id: [*:0]const u8, + ) *DIType; + + pub const createMemberType = ZigLLVMCreateDebugMemberType; + extern fn ZigLLVMCreateDebugMemberType( + dib: *DIBuilder, + scope: *DIScope, + name: [*:0]const u8, + file: *DIFile, + line: c_uint, + size_in_bits: u64, + align_in_bits: u64, + offset_in_bits: u64, + flags: c_uint, + ty: *DIType, + ) *DIType; + + pub const createReplaceableCompositeType = ZigLLVMCreateReplaceableCompositeType; + extern fn ZigLLVMCreateReplaceableCompositeType( + dib: *DIBuilder, + tag: c_uint, + name: [*:0]const u8, + scope: *DIScope, + file: *DIFile, + line: c_uint, + ) *DIType; + + pub const createForwardDeclType = ZigLLVMCreateDebugForwardDeclType; + extern fn ZigLLVMCreateDebugForwardDeclType( + dib: *DIBuilder, + tag: c_uint, + name: [*:0]const u8, + scope: *DIScope, + file: *DIFile, + line: c_uint, + ) *DIType; + + pub const replaceTemporary = ZigLLVMReplaceTemporary; + extern fn ZigLLVMReplaceTemporary(dib: *DIBuilder, ty: *DIType, replacement: *DIType) void; + + pub const replaceDebugArrays = ZigLLVMReplaceDebugArrays; + extern fn ZigLLVMReplaceDebugArrays( + dib: *DIBuilder, + ty: *DIType, + types_array: [*]const *DIType, + types_array_len: c_int, + ) void; + + pub const createSubroutineType = ZigLLVMCreateSubroutineType; + extern fn ZigLLVMCreateSubroutineType( + dib: *DIBuilder, + types_array: [*]const *DIType, + types_array_len: c_int, + flags: c_uint, + ) *DIType; + + pub const createAutoVariable = ZigLLVMCreateAutoVariable; + extern fn ZigLLVMCreateAutoVariable( + dib: *DIBuilder, + scope: *DIScope, + name: [*:0]const u8, + file: *DIFile, + line_no: c_uint, + ty: *DIType, + always_preserve: bool, + flags: c_uint, + ) *DILocalVariable; + + pub const createGlobalVariable = ZigLLVMCreateGlobalVariable; + extern fn ZigLLVMCreateGlobalVariable( + dib: *DIBuilder, + scope: *DIScope, + name: [*:0]const u8, + linkage_name: [*:0]const u8, + file: *DIFile, + line_no: c_uint, + di_type: *DIType, + is_local_to_unit: bool, + ) *DIGlobalVariable; + + pub const createParameterVariable = ZigLLVMCreateParameterVariable; + extern fn ZigLLVMCreateParameterVariable( + dib: *DIBuilder, + scope: *DIScope, + name: [*:0]const u8, + file: *DIFile, + line_no: c_uint, + ty: *DIType, + always_preserve: bool, + flags: c_uint, + arg_no: c_uint, + ) *DILocalVariable; + + pub const createLexicalBlock = ZigLLVMCreateLexicalBlock; + extern fn ZigLLVMCreateLexicalBlock( + dib: *DIBuilder, + scope: *DIScope, + file: *DIFile, + line: c_uint, + col: c_uint, + ) *DILexicalBlock; + + pub const createCompileUnit = ZigLLVMCreateCompileUnit; + extern fn ZigLLVMCreateCompileUnit( + dib: *DIBuilder, + lang: c_uint, + difile: *DIFile, + producer: [*:0]const u8, + is_optimized: bool, + flags: [*:0]const u8, + runtime_version: c_uint, + split_name: [*:0]const u8, + dwo_id: u64, + emit_debug_info: bool, + ) *DICompileUnit; + + pub const createFile = ZigLLVMCreateFile; + extern fn ZigLLVMCreateFile( + dib: *DIBuilder, + filename: [*:0]const u8, + directory: [*:0]const u8, + ) *DIFile; + + pub const createFunction = ZigLLVMCreateFunction; + extern fn ZigLLVMCreateFunction( + dib: *DIBuilder, + scope: *DIScope, + name: [*:0]const u8, + linkage_name: [*:0]const u8, + file: *DIFile, + lineno: c_uint, + fn_di_type: *DIType, + is_local_to_unit: bool, + is_definition: bool, + scope_line: c_uint, + flags: c_uint, + is_optimized: bool, + decl_subprogram: *DISubprogram, + ) *DISubprogram; + + pub const createVectorType = ZigLLVMDIBuilderCreateVectorType; + extern fn ZigLLVMDIBuilderCreateVectorType( + dib: *DIBuilder, + SizeInBits: u64, + AlignInBits: u32, + Ty: *DIType, + elem_count: u32, + ) *DIType; + + pub const insertDeclareAtEnd = ZigLLVMInsertDeclareAtEnd; + extern fn ZigLLVMInsertDeclareAtEnd( + dib: *DIBuilder, + storage: *const Value, + var_info: *DILocalVariable, + debug_loc: *DILocation, + basic_block_ref: *const BasicBlock, + ) *const Value; + + pub const insertDeclare = ZigLLVMInsertDeclare; + extern fn ZigLLVMInsertDeclare( + dib: *DIBuilder, + storage: *const Value, + var_info: *DILocalVariable, + debug_loc: *DILocation, + insert_before_instr: *const Value, + ) *const Value; +}; diff --git a/src/link.zig b/src/link.zig index 577a78d893..34e2bb8ed8 100644 --- a/src/link.zig +++ b/src/link.zig @@ -72,7 +72,7 @@ pub const Options = struct { object_format: std.Target.ObjectFormat, optimize_mode: std.builtin.Mode, machine_code_model: std.builtin.CodeModel, - root_name: []const u8, + root_name: [:0]const u8, /// Not every Compilation compiles .zig code! For example you could do `zig build-exe foo.o`. module: ?*Module, dynamic_linker: ?[]const u8,