Commit 53c668d3a9

Jakub Konka <kubkon@jakubkonka.com>
2022-01-24 10:33:42
stage2: add naive impl of pointer type in ELF
Augment relocation tracking mechanism to de-duplicate potential creation of base as well as composite types while unrolling composite types in the linker - there is still potential for further space optimisation by moving all type information into a separate section `.debug_types` and providing references to entries within that section whenever required (e.g., `ref4` form). Currently, we duplicate type definitions on a per-decl basis. Anyhow, with this patch, an example function signature of the following type: ```zig fn byPtrPtr(ptr_ptr_x: **u32, ptr_x: *u32) void { ptr_ptr_x.* = ptr_x; } ``` will generate the following `.debug_info` for formal parameters: ``` <1><1aa>: Abbrev Number: 3 (DW_TAG_subprogram) <1ab> DW_AT_low_pc : 0x8000197 <1b3> DW_AT_high_pc : 0x2c <1b7> DW_AT_name : byPtrPtr <2><1c0>: Abbrev Number: 7 (DW_TAG_formal_parameter) <1c1> DW_AT_location : 1 byte block: 55 (DW_OP_reg5 (rdi)) <1c3> DW_AT_type : <0x1df> <1c7> DW_AT_name : ptr_ptr_x <2><1d1>: Abbrev Number: 7 (DW_TAG_formal_parameter) <1d2> DW_AT_location : 1 byte block: 54 (DW_OP_reg4 (rsi)) <1d4> DW_AT_type : <0x1e4> <1d8> DW_AT_name : ptr_x <2><1de>: Abbrev Number: 0 <1><1df>: Abbrev Number: 5 (DW_TAG_pointer_type) <1e0> DW_AT_type : <0x1e4> <1><1e4>: Abbrev Number: 5 (DW_TAG_pointer_type) <1e5> DW_AT_type : <0x1e9> <1><1e9>: Abbrev Number: 4 (DW_TAG_base_type) <1ea> DW_AT_encoding : 7 (unsigned) <1eb> DW_AT_byte_size : 4 <1ec> DW_AT_name : u32 ```
1 parent 05c5bb9
Changed files (4)
src/link/MachO/DebugSymbols.zig
@@ -1112,8 +1112,7 @@ pub fn commitDeclDebugInfo(
 
     {
         // Now that we have the offset assigned we can finally perform type relocations.
-        var it = dbg_info_type_relocs.valueIterator();
-        while (it.next()) |value| {
+        for (dbg_info_type_relocs.values()) |value| {
             for (value.relocs.items) |off| {
                 mem.writeIntLittle(
                     u32,
src/link/Elf.zig
@@ -783,8 +783,9 @@ pub const abbrev_compile_unit = 1;
 pub const abbrev_subprogram = 2;
 pub const abbrev_subprogram_retvoid = 3;
 pub const abbrev_base_type = 4;
-pub const abbrev_pad1 = 5;
-pub const abbrev_parameter = 6;
+pub const abbrev_ptr_type = 5;
+pub const abbrev_pad1 = 6;
+pub const abbrev_parameter = 7;
 
 pub fn flush(self: *Elf, comp: *Compilation) !void {
     if (self.base.options.emit == null) {
@@ -871,9 +872,21 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {
             DW.AT.byte_size,
             DW.FORM.data1,
             DW.AT.name,
-            DW.FORM.string, 0, 0, // table sentinel
-            abbrev_pad1, DW.TAG.unspecified_type, DW.CHILDREN.no, // header
-            0,                0, // table sentinel
+            DW.FORM.string,
+            0,
+            0, // table sentinel
+            abbrev_ptr_type,
+            DW.TAG.pointer_type,
+            DW.CHILDREN.no, // header
+            DW.AT.type,
+            DW.FORM.ref4,
+            0,
+            0, // table sentinel
+            abbrev_pad1,
+            DW.TAG.unspecified_type,
+            DW.CHILDREN.no, // header
+            0,
+            0, // table sentinel
             abbrev_parameter,
             DW.TAG.formal_parameter, DW.CHILDREN.no, // header
             DW.AT.location,          DW.FORM.exprloc,
@@ -2309,8 +2322,7 @@ pub fn freeDecl(self: *Elf, decl: *Module.Decl) void {
 }
 
 fn deinitRelocs(gpa: Allocator, table: *File.DbgInfoTypeRelocsTable) void {
-    var it = table.valueIterator();
-    while (it.next()) |value| {
+    for (table.values()) |*value| {
         value.relocs.deinit(gpa);
     }
     table.deinit(gpa);
@@ -2387,10 +2399,12 @@ fn finishUpdateDecl(
     // the buffer, so we have to do it before computing the offset, and we can't perform the actual
     // relocations yet.
     {
-        var it = dbg_info_type_relocs.iterator();
-        while (it.next()) |entry| {
-            entry.value_ptr.off = @intCast(u32, dbg_info_buffer.items.len);
-            try self.addDbgInfoType(entry.key_ptr.*, dbg_info_buffer);
+        var it: usize = 0;
+        while (it < dbg_info_type_relocs.count()) : (it += 1) {
+            const ty = dbg_info_type_relocs.keys()[it];
+            const value_ptr = dbg_info_type_relocs.getPtr(ty).?;
+            value_ptr.off = @intCast(u32, dbg_info_buffer.items.len);
+            try self.addDbgInfoType(ty, dbg_info_buffer, dbg_info_type_relocs);
         }
     }
 
@@ -2401,8 +2415,7 @@ fn finishUpdateDecl(
 
     {
         // Now that we have the offset assigned we can finally perform type relocations.
-        var it = dbg_info_type_relocs.valueIterator();
-        while (it.next()) |value| {
+        for (dbg_info_type_relocs.values()) |value| {
             for (value.relocs.items) |off| {
                 mem.writeInt(
                     u32,
@@ -2706,7 +2719,14 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
 }
 
 /// Asserts the type has codegen bits.
-fn addDbgInfoType(self: *Elf, ty: Type, dbg_info_buffer: *std.ArrayList(u8)) !void {
+fn addDbgInfoType(
+    self: *Elf,
+    ty: Type,
+    dbg_info_buffer: *std.ArrayList(u8),
+    dbg_info_type_relocs: *File.DbgInfoTypeRelocsTable,
+) error{OutOfMemory}!void {
+    var reloc: ?struct { ty: Type, reloc: u32 } = null;
+
     switch (ty.zigTypeTag()) {
         .Void => unreachable,
         .NoReturn => unreachable,
@@ -2747,11 +2767,34 @@ fn addDbgInfoType(self: *Elf, ty: Type, dbg_info_buffer: *std.ArrayList(u8)) !vo
                 try dbg_info_buffer.append(abbrev_pad1);
             }
         },
+        .Pointer => blk: {
+            if (ty.isSlice()) {
+                log.debug("TODO implement .debug_info for type '{}'", .{ty});
+                try dbg_info_buffer.append(abbrev_pad1);
+                break :blk;
+            }
+            try dbg_info_buffer.ensureUnusedCapacity(5);
+            dbg_info_buffer.appendAssumeCapacity(abbrev_ptr_type);
+            const index = dbg_info_buffer.items.len;
+            try dbg_info_buffer.resize(index + 4); // DW.AT.type, DW.FORM.ref4
+            reloc = .{ .ty = ty.childType(), .reloc = @intCast(u32, index) };
+        },
         else => {
             log.debug("TODO implement .debug_info for type '{}'", .{ty});
             try dbg_info_buffer.append(abbrev_pad1);
         },
     }
+
+    if (reloc) |rel| {
+        const gop = try dbg_info_type_relocs.getOrPut(self.base.allocator, rel.ty);
+        if (!gop.found_existing) {
+            gop.value_ptr.* = .{
+                .off = undefined,
+                .relocs = .{},
+            };
+        }
+        try gop.value_ptr.relocs.append(self.base.allocator, rel.reloc);
+    }
 }
 
 fn updateDeclDebugInfoAllocation(self: *Elf, text_block: *TextBlock, len: u32) !void {
src/link/MachO.zig
@@ -3578,8 +3578,7 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv
         if (debug_buffers) |dbg| {
             dbg.dbg_line_buffer.deinit();
             dbg.dbg_info_buffer.deinit();
-            var it = dbg.dbg_info_type_relocs.valueIterator();
-            while (it.next()) |value| {
+            for (dbg.dbg_info_type_relocs.values()) |*value| {
                 value.relocs.deinit(self.base.allocator);
             }
             dbg.dbg_info_type_relocs.deinit(self.base.allocator);
@@ -3659,8 +3658,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
         if (debug_buffers) |dbg| {
             dbg.dbg_line_buffer.deinit();
             dbg.dbg_info_buffer.deinit();
-            var it = dbg.dbg_info_type_relocs.valueIterator();
-            while (it.next()) |value| {
+            for (dbg.dbg_info_type_relocs.values()) |*value| {
                 value.relocs.deinit(self.base.allocator);
             }
             dbg.dbg_info_type_relocs.deinit(self.base.allocator);
src/link.zig
@@ -235,7 +235,12 @@ pub const File = struct {
     };
 
     /// For DWARF .debug_info.
-    pub const DbgInfoTypeRelocsTable = std.HashMapUnmanaged(Type, DbgInfoTypeReloc, Type.HashContext64, std.hash_map.default_max_load_percentage);
+    pub const DbgInfoTypeRelocsTable = std.ArrayHashMapUnmanaged(
+        Type,
+        DbgInfoTypeReloc,
+        Type.HashContext32,
+        true,
+    );
 
     /// For DWARF .debug_info.
     pub const DbgInfoTypeReloc = struct {