| ... | @@ -188,6 +188,7 @@ pub const Object = struct { | ... | @@ -188,6 +188,7 @@ pub const Object = struct { |
| 188 | /// The backing memory for `type_map`. Periodically garbage collected after flush(). | 188 | /// The backing memory for `type_map`. Periodically garbage collected after flush(). |
| 189 | /// The code for doing the periodical GC is not yet implemented. | 189 | /// The code for doing the periodical GC is not yet implemented. |
| 190 | type_map_arena: std.heap.ArenaAllocator, | 190 | type_map_arena: std.heap.ArenaAllocator, |
| | 191 | di_type_map: DITypeMap, |
| 191 | /// The LLVM global table which holds the names corresponding to Zig errors. | 192 | /// The LLVM global table which holds the names corresponding to Zig errors. |
| 192 | /// Note that the values are not added until flushModule, when all errors in | 193 | /// Note that the values are not added until flushModule, when all errors in |
| 193 | /// the compilation are known. | 194 | /// the compilation are known. |
| ... | @@ -200,6 +201,13 @@ pub const Object = struct { | ... | @@ -200,6 +201,13 @@ pub const Object = struct { |
| 200 | std.hash_map.default_max_load_percentage, | 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 | pub fn create(gpa: Allocator, options: link.Options) !*Object { | 211 | pub fn create(gpa: Allocator, options: link.Options) !*Object { |
| 204 | const obj = try gpa.create(Object); | 212 | const obj = try gpa.create(Object); |
| 205 | errdefer gpa.destroy(obj); | 213 | errdefer gpa.destroy(obj); |
| ... | @@ -336,6 +344,7 @@ pub const Object = struct { | ... | @@ -336,6 +344,7 @@ pub const Object = struct { |
| 336 | .decl_map = .{}, | 344 | .decl_map = .{}, |
| 337 | .type_map = .{}, | 345 | .type_map = .{}, |
| 338 | .type_map_arena = std.heap.ArenaAllocator.init(gpa), | 346 | .type_map_arena = std.heap.ArenaAllocator.init(gpa), |
| | 347 | .di_type_map = .{}, |
| 339 | .error_name_table = null, | 348 | .error_name_table = null, |
| 340 | }; | 349 | }; |
| 341 | } | 350 | } |
| ... | @@ -344,6 +353,7 @@ pub const Object = struct { | ... | @@ -344,6 +353,7 @@ pub const Object = struct { |
| 344 | if (self.di_builder) |dib| { | 353 | if (self.di_builder) |dib| { |
| 345 | dib.dispose(); | 354 | dib.dispose(); |
| 346 | self.di_map.deinit(gpa); | 355 | self.di_map.deinit(gpa); |
| | 356 | self.di_type_map.deinit(gpa); |
| 347 | } | 357 | } |
| 348 | self.target_data.dispose(); | 358 | self.target_data.dispose(); |
| 349 | self.target_machine.dispose(); | 359 | self.target_machine.dispose(); |
| ... | @@ -558,19 +568,7 @@ pub const Object = struct { | ... | @@ -558,19 +568,7 @@ pub const Object = struct { |
| 558 | var di_scope: ?*llvm.DIScope = null; | 568 | var di_scope: ?*llvm.DIScope = null; |
| 559 | | 569 | |
| 560 | if (dg.object.di_builder) |dib| { | 570 | if (dg.object.di_builder) |dib| { |
| 561 | di_file = s: { | 571 | di_file = try dg.object.getDIFile(gpa, decl.src_namespace.file_scope); |
| 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 | }; | | |
| 574 | | 572 | |
| 575 | const line_number = decl.src_line + 1; | 573 | const line_number = decl.src_line + 1; |
| 576 | const is_internal_linkage = decl.val.tag() != .extern_fn and | 574 | const is_internal_linkage = decl.val.tag() != .extern_fn and |
| ... | @@ -718,6 +716,22 @@ pub const Object = struct { | ... | @@ -718,6 +716,22 @@ pub const Object = struct { |
| 718 | const llvm_value = self.decl_map.get(decl) orelse return; | 716 | const llvm_value = self.decl_map.get(decl) orelse return; |
| 719 | llvm_value.deleteGlobal(); | 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 | pub const DeclGen = struct { | 737 | pub const DeclGen = struct { |
| ... | @@ -1891,9 +1905,18 @@ pub const DeclGen = struct { | ... | @@ -1891,9 +1905,18 @@ pub const DeclGen = struct { |
| 1891 | } | 1905 | } |
| 1892 | | 1906 | |
| 1893 | fn lowerDebugType(dg: *DeclGen, ty: Type) Allocator.Error!*llvm.DIType { | 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 | const target = dg.module.getTarget(); | 1917 | const target = dg.module.getTarget(); |
| 1896 | const dib = dg.object.di_builder.?; | 1918 | const dib = dg.object.di_builder.?; |
| | 1919 | const gpa = dg.gpa; |
| 1897 | switch (ty.zigTypeTag()) { | 1920 | switch (ty.zigTypeTag()) { |
| 1898 | .Void, .NoReturn => return dib.createBasicType("void", 0, DW.ATE.signed), | 1921 | .Void, .NoReturn => return dib.createBasicType("void", 0, DW.ATE.signed), |
| 1899 | .Int => { | 1922 | .Int => { |
| ... | @@ -1907,12 +1930,54 @@ pub const DeclGen = struct { | ... | @@ -1907,12 +1930,54 @@ pub const DeclGen = struct { |
| 1907 | return dib.createBasicType(name, info.bits, dwarf_encoding); | 1930 | return dib.createBasicType(name, info.bits, dwarf_encoding); |
| 1908 | }, | 1931 | }, |
| 1909 | .Enum => { | 1932 | .Enum => { |
| 1910 | @panic("TODO debug info type for enums"); | 1933 | const owner_decl = ty.getOwnerDecl(); |
| 1911 | //var buffer: Type.Payload.Bits = undefined; | 1934 | |
| 1912 | //const int_ty = ty.intTagType(&buffer); | 1935 | if (!ty.hasRuntimeBits()) { |
| 1913 | //const bit_count = int_ty.intInfo(target).bits; | 1936 | return dg.makeEmptyNamespaceDIType(owner_decl); |
| 1914 | //assert(bit_count != 0); | 1937 | } |
| 1915 | //return dg.context.intType(bit_count); | 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 | .Float => { | 1982 | .Float => { |
| 1918 | const bits = ty.floatBits(target); | 1983 | const bits = ty.floatBits(target); |
| ... | @@ -2302,6 +2367,33 @@ pub const DeclGen = struct { | ... | @@ -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 | const ParentPtr = struct { | 2397 | const ParentPtr = struct { |
| 2306 | ty: Type, | 2398 | ty: Type, |
| 2307 | llvm_ptr: *const llvm.Value, | 2399 | llvm_ptr: *const llvm.Value, |