| ... | ... | @@ -188,6 +188,7 @@ pub const Object = struct { |
| 188 | 188 | /// The backing memory for `type_map`. Periodically garbage collected after flush(). |
| 189 | 189 | /// The code for doing the periodical GC is not yet implemented. |
| 190 | 190 | type_map_arena: std.heap.ArenaAllocator, |
| 191 | di_type_map: DITypeMap, |
| 191 | 192 | /// The LLVM global table which holds the names corresponding to Zig errors. |
| 192 | 193 | /// Note that the values are not added until flushModule, when all errors in |
| 193 | 194 | /// the compilation are known. |
| ... | ... | @@ -200,6 +201,13 @@ pub const Object = struct { |
| 200 | 201 | std.hash_map.default_max_load_percentage, |
| 201 | 202 | ); |
| 202 | 203 | |
| 204 | pub const DITypeMap = std.HashMapUnmanaged( |
| 205 | Type, |
| 206 | *llvm.DIType, |
| 207 | Type.HashContext64, |
| 208 | std.hash_map.default_max_load_percentage, |
| 209 | ); |
| 210 | |
| 203 | 211 | pub fn create(gpa: Allocator, options: link.Options) !*Object { |
| 204 | 212 | const obj = try gpa.create(Object); |
| 205 | 213 | errdefer gpa.destroy(obj); |
| ... | ... | @@ -336,6 +344,7 @@ pub const Object = struct { |
| 336 | 344 | .decl_map = .{}, |
| 337 | 345 | .type_map = .{}, |
| 338 | 346 | .type_map_arena = std.heap.ArenaAllocator.init(gpa), |
| 347 | .di_type_map = .{}, |
| 339 | 348 | .error_name_table = null, |
| 340 | 349 | }; |
| 341 | 350 | } |
| ... | ... | @@ -344,6 +353,7 @@ pub const Object = struct { |
| 344 | 353 | if (self.di_builder) |dib| { |
| 345 | 354 | dib.dispose(); |
| 346 | 355 | self.di_map.deinit(gpa); |
| 356 | self.di_type_map.deinit(gpa); |
| 347 | 357 | } |
| 348 | 358 | self.target_data.dispose(); |
| 349 | 359 | self.target_machine.dispose(); |
| ... | ... | @@ -558,19 +568,7 @@ pub const Object = struct { |
| 558 | 568 | var di_scope: ?*llvm.DIScope = null; |
| 559 | 569 | |
| 560 | 570 | if (dg.object.di_builder) |dib| { |
| 561 | | di_file = s: { |
| 562 | | const file = decl.src_namespace.file_scope; |
| 563 | | const gop = try dg.object.di_map.getOrPut(gpa, file); |
| 564 | | if (!gop.found_existing) { |
| 565 | | const dir_path = file.pkg.root_src_directory.path orelse "."; |
| 566 | | const sub_file_path_z = try gpa.dupeZ(u8, file.sub_file_path); |
| 567 | | defer gpa.free(sub_file_path_z); |
| 568 | | const dir_path_z = try gpa.dupeZ(u8, dir_path); |
| 569 | | defer gpa.free(dir_path_z); |
| 570 | | gop.value_ptr.* = dib.createFile(sub_file_path_z, dir_path_z).toScope(); |
| 571 | | } |
| 572 | | break :s @ptrCast(*llvm.DIFile, gop.value_ptr.*); |
| 573 | | }; |
| 571 | di_file = try dg.object.getDIFile(gpa, decl.src_namespace.file_scope); |
| 574 | 572 | |
| 575 | 573 | const line_number = decl.src_line + 1; |
| 576 | 574 | const is_internal_linkage = decl.val.tag() != .extern_fn and |
| ... | ... | @@ -718,6 +716,22 @@ pub const Object = struct { |
| 718 | 716 | const llvm_value = self.decl_map.get(decl) orelse return; |
| 719 | 717 | llvm_value.deleteGlobal(); |
| 720 | 718 | } |
| 719 | |
| 720 | fn getDIFile(o: *Object, gpa: Allocator, file: *const Module.File) !*llvm.DIFile { |
| 721 | const gop = try o.di_map.getOrPut(gpa, file); |
| 722 | errdefer assert(o.di_map.remove(file)); |
| 723 | if (gop.found_existing) { |
| 724 | return @ptrCast(*llvm.DIFile, gop.value_ptr.*); |
| 725 | } |
| 726 | const dir_path = file.pkg.root_src_directory.path orelse "."; |
| 727 | const sub_file_path_z = try gpa.dupeZ(u8, file.sub_file_path); |
| 728 | defer gpa.free(sub_file_path_z); |
| 729 | const dir_path_z = try gpa.dupeZ(u8, dir_path); |
| 730 | defer gpa.free(dir_path_z); |
| 731 | const di_file = o.di_builder.?.createFile(sub_file_path_z, dir_path_z); |
| 732 | gop.value_ptr.* = di_file.toScope(); |
| 733 | return di_file; |
| 734 | } |
| 721 | 735 | }; |
| 722 | 736 | |
| 723 | 737 | pub const DeclGen = struct { |
| ... | ... | @@ -1891,9 +1905,18 @@ pub const DeclGen = struct { |
| 1891 | 1905 | } |
| 1892 | 1906 | |
| 1893 | 1907 | fn lowerDebugType(dg: *DeclGen, ty: Type) Allocator.Error!*llvm.DIType { |
| 1894 | | const gpa = dg.gpa; |
| 1908 | const gop = try dg.object.di_type_map.getOrPut(dg.gpa, ty); |
| 1909 | errdefer assert(dg.object.di_type_map.remove(ty)); |
| 1910 | if (!gop.found_existing) { |
| 1911 | gop.value_ptr.* = try lowerDebugTypeRaw(dg, ty); |
| 1912 | } |
| 1913 | return gop.value_ptr.*; |
| 1914 | } |
| 1915 | |
| 1916 | fn lowerDebugTypeRaw(dg: *DeclGen, ty: Type) Allocator.Error!*llvm.DIType { |
| 1895 | 1917 | const target = dg.module.getTarget(); |
| 1896 | 1918 | const dib = dg.object.di_builder.?; |
| 1919 | const gpa = dg.gpa; |
| 1897 | 1920 | switch (ty.zigTypeTag()) { |
| 1898 | 1921 | .Void, .NoReturn => return dib.createBasicType("void", 0, DW.ATE.signed), |
| 1899 | 1922 | .Int => { |
| ... | ... | @@ -1907,12 +1930,54 @@ pub const DeclGen = struct { |
| 1907 | 1930 | return dib.createBasicType(name, info.bits, dwarf_encoding); |
| 1908 | 1931 | }, |
| 1909 | 1932 | .Enum => { |
| 1910 | | @panic("TODO debug info type for enums"); |
| 1911 | | //var buffer: Type.Payload.Bits = undefined; |
| 1912 | | //const int_ty = ty.intTagType(&buffer); |
| 1913 | | //const bit_count = int_ty.intInfo(target).bits; |
| 1914 | | //assert(bit_count != 0); |
| 1915 | | //return dg.context.intType(bit_count); |
| 1933 | const owner_decl = ty.getOwnerDecl(); |
| 1934 | |
| 1935 | if (!ty.hasRuntimeBits()) { |
| 1936 | return dg.makeEmptyNamespaceDIType(owner_decl); |
| 1937 | } |
| 1938 | |
| 1939 | const field_names = ty.enumFields().keys(); |
| 1940 | |
| 1941 | const enumerators = try gpa.alloc(*llvm.DIEnumerator, field_names.len); |
| 1942 | defer gpa.free(enumerators); |
| 1943 | |
| 1944 | var buf_field_index: Value.Payload.U32 = .{ |
| 1945 | .base = .{ .tag = .enum_field_index }, |
| 1946 | .data = undefined, |
| 1947 | }; |
| 1948 | const field_index_val = Value.initPayload(&buf_field_index.base); |
| 1949 | |
| 1950 | for (field_names) |field_name, i| { |
| 1951 | const field_name_z = try gpa.dupeZ(u8, field_name); |
| 1952 | defer gpa.free(field_name_z); |
| 1953 | |
| 1954 | buf_field_index.data = @intCast(u32, i); |
| 1955 | var buf_u64: Value.Payload.U64 = undefined; |
| 1956 | const field_int_val = field_index_val.enumToInt(ty, &buf_u64); |
| 1957 | // See https://github.com/ziglang/zig/issues/645 |
| 1958 | const field_int = field_int_val.toSignedInt(); |
| 1959 | enumerators[i] = dib.createEnumerator(field_name_z, field_int); |
| 1960 | } |
| 1961 | |
| 1962 | const di_file = try dg.object.getDIFile(gpa, owner_decl.src_namespace.file_scope); |
| 1963 | const di_scope = try dg.namespaceToDebugScope(owner_decl.src_namespace); |
| 1964 | |
| 1965 | const name = try ty.nameAlloc(gpa); // TODO this is a leak |
| 1966 | var buffer: Type.Payload.Bits = undefined; |
| 1967 | const int_ty = ty.intTagType(&buffer); |
| 1968 | |
| 1969 | return dib.createEnumerationType( |
| 1970 | di_scope, |
| 1971 | name, |
| 1972 | di_file, |
| 1973 | owner_decl.src_node + 1, |
| 1974 | ty.abiSize(target) * 8, |
| 1975 | ty.abiAlignment(target) * 8, |
| 1976 | enumerators.ptr, |
| 1977 | @intCast(c_int, enumerators.len), |
| 1978 | try lowerDebugType(dg, int_ty), |
| 1979 | "", |
| 1980 | ); |
| 1916 | 1981 | }, |
| 1917 | 1982 | .Float => { |
| 1918 | 1983 | const bits = ty.floatBits(target); |
| ... | ... | @@ -2302,6 +2367,33 @@ pub const DeclGen = struct { |
| 2302 | 2367 | } |
| 2303 | 2368 | } |
| 2304 | 2369 | |
| 2370 | fn namespaceToDebugScope(dg: *DeclGen, namespace: *const Module.Namespace) !*llvm.DIScope { |
| 2371 | const di_type = try dg.lowerDebugType(namespace.ty); |
| 2372 | return di_type.toScope(); |
| 2373 | } |
| 2374 | |
| 2375 | /// This is to be used instead of void for debug info types, to avoid tripping |
| 2376 | /// Assertion `!isa<DIType>(Scope) && "shouldn't make a namespace scope for a type"' |
| 2377 | /// when targeting CodeView (Windows). |
| 2378 | fn makeEmptyNamespaceDIType(dg: *DeclGen, decl: *const Module.Decl) !*llvm.DIType { |
| 2379 | const fields: [0]*llvm.DIType = .{}; |
| 2380 | return dg.object.di_builder.?.createStructType( |
| 2381 | try dg.namespaceToDebugScope(decl.src_namespace), |
| 2382 | decl.name, // TODO use fully qualified name |
| 2383 | try dg.object.getDIFile(dg.gpa, decl.src_namespace.file_scope), |
| 2384 | decl.src_line + 1, |
| 2385 | 0, // size in bits |
| 2386 | 0, // align in bits |
| 2387 | 0, // flags |
| 2388 | null, // derived from |
| 2389 | undefined, // TODO should be able to pass &fields, |
| 2390 | fields.len, |
| 2391 | 0, // run time lang |
| 2392 | null, // vtable holder |
| 2393 | "", // unique id |
| 2394 | ); |
| 2395 | } |
| 2396 | |
| 2305 | 2397 | const ParentPtr = struct { |
| 2306 | 2398 | ty: Type, |
| 2307 | 2399 | llvm_ptr: *const llvm.Value, |