From 53c668d3a9dad430749cbc642d7bf8cf45eefba1 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 24 Jan 2022 10:33:42 +0100 Subject: 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 ``` --- src/link.zig | 7 +++- src/link/Elf.zig | 71 +++++++++++++++++++++++++++++++++-------- src/link/MachO.zig | 6 ++-- src/link/MachO/DebugSymbols.zig | 3 +- 4 files changed, 66 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/link.zig b/src/link.zig index 916dea0429..817cb4c052 100644 --- a/src/link.zig +++ b/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 { diff --git a/src/link/Elf.zig b/src/link/Elf.zig index a7ce8d0783..2b88458bc9 100644 --- a/src/link/Elf.zig +++ b/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 { diff --git a/src/link/MachO.zig b/src/link/MachO.zig index d7385f1f33..cd1d197010 100644 --- a/src/link/MachO.zig +++ b/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); diff --git a/src/link/MachO/DebugSymbols.zig b/src/link/MachO/DebugSymbols.zig index 88a27ea48f..beef0b6b2c 100644 --- a/src/link/MachO/DebugSymbols.zig +++ b/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, -- cgit v1.2.3