Commit c6160fa3a5

Andrew Kelley <andrew@ziglang.org>
2022-03-07 23:43:20
LLVM: add compile unit to debug info
This commit also adds a bunch of bindings for debug info.
1 parent 95fc41b
Changed files (4)
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;
+};
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();
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();
 
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,