authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-10-10 23:08:29+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-10-13 08:38:17+02:00
log78ec7b671de6ca8d1466bf408e906c8799dcd19c
tree5452521f38f2d6a8a7cf76dab62b16d3384dea5b
parent0b77152faa663c12b5ab3aa20db77c2ae1427ece

Add mechanism for growing/shrinking text blocks


1 files changed, 114 insertions(+), 15 deletions(-)

src/link/MachO.zig+114-15
...@@ -134,6 +134,22 @@ error_flags: File.ErrorFlags = File.ErrorFlags{},...@@ -134,6 +134,22 @@ error_flags: File.ErrorFlags = File.ErrorFlags{},
134134
135cmd_table_dirty: bool = false,135cmd_table_dirty: bool = false,
136136
137/// A list of text blocks that have surplus capacity. This list can have false
138/// positives, as functions grow and shrink over time, only sometimes being added
139/// or removed from the freelist.
140///
141/// A text block has surplus capacity when its overcapacity value is greater than
142/// minimum_text_block_size * alloc_num / alloc_den. That is, when it has so
143/// much extra capacity, that we could fit a small new symbol in it, itself with
144/// ideal_capacity or more.
145///
146/// Ideal capacity is defined by size * alloc_num / alloc_den.
147///
148/// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
149/// overcapacity can be negative. A simple way to have negative overcapacity is to
150/// allocate a fresh text block, which will have ideal capacity, and then grow it
151/// by 1 byte. It will then have -1 overcapacity.
152text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{},
137/// Pointer to the last allocated text block153/// Pointer to the last allocated text block
138last_text_block: ?*TextBlock = null,154last_text_block: ?*TextBlock = null,
139155
...@@ -755,6 +771,7 @@ fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 {...@@ -755,6 +771,7 @@ fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 {
755}771}
756772
757pub fn deinit(self: *MachO) void {773pub fn deinit(self: *MachO) void {
774 self.text_block_free_list.deinit(self.base.allocator);
758 self.offset_table.deinit(self.base.allocator);775 self.offset_table.deinit(self.base.allocator);
759 self.offset_table_free_list.deinit(self.base.allocator);776 self.offset_table_free_list.deinit(self.base.allocator);
760 self.string_table.deinit(self.base.allocator);777 self.string_table.deinit(self.base.allocator);
...@@ -767,6 +784,61 @@ pub fn deinit(self: *MachO) void {...@@ -767,6 +784,61 @@ pub fn deinit(self: *MachO) void {
767 self.load_commands.deinit(self.base.allocator);784 self.load_commands.deinit(self.base.allocator);
768}785}
769786
787fn freeTextBlock(self: *MachO, text_block: *TextBlock) void {
788 var already_have_free_list_node = false;
789 {
790 var i: usize = 0;
791 // TODO turn text_block_free_list into a hash map
792 while (i < self.text_block_free_list.items.len) {
793 if (self.text_block_free_list.items[i] == text_block) {
794 _ = self.text_block_free_list.swapRemove(i);
795 continue;
796 }
797 if (self.text_block_free_list.items[i] == text_block.prev) {
798 already_have_free_list_node = true;
799 }
800 i += 1;
801 }
802 }
803 // TODO process free list for dbg info just like we do above for vaddrs
804
805 if (self.last_text_block == text_block) {
806 // TODO shrink the __text section size here
807 self.last_text_block = text_block.prev;
808 }
809
810 if (text_block.prev) |prev| {
811 prev.next = text_block.next;
812
813 if (!already_have_free_list_node and prev.freeListEligible(self.*)) {
814 // The free list is heuristics, it doesn't have to be perfect, so we can ignore
815 // the OOM here.
816 self.text_block_free_list.append(self.base.allocator, prev) catch {};
817 }
818 } else {
819 text_block.prev = null;
820 }
821
822 if (text_block.next) |next| {
823 next.prev = text_block.prev;
824 } else {
825 text_block.next = null;
826 }
827}
828
829fn shrinkTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64) void {
830 // TODO check the new capacity, and if it crosses the size threshold into a big enough
831 // capacity, insert a free list node for it.
832}
833
834fn growTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
835 const sym = self.local_symbols.items[text_block.local_sym_index];
836 const align_ok = mem.alignBackwardGeneric(u64, sym.n_value, alignment) == sym.n_value;
837 const need_realloc = !align_ok or new_block_size > text_block.capacity(self.*);
838 if (!need_realloc) return sym.n_value;
839 return self.allocateTextBlock(text_block, new_block_size, alignment);
840}
841
770pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {842pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {
771 if (decl.link.macho.local_sym_index != 0) return;843 if (decl.link.macho.local_sym_index != 0) return;
772844
...@@ -811,24 +883,51 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -811,24 +883,51 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
811 };883 };
812884
813 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);885 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
886 assert(decl.link.macho.local_sym_index != 0); // Caller forgot to call allocateDeclIndexes()
814 const symbol = &self.local_symbols.items[decl.link.macho.local_sym_index];887 const symbol = &self.local_symbols.items[decl.link.macho.local_sym_index];
815888
816 const decl_name = mem.spanZ(decl.name);889 if (decl.link.macho.size != 0) {
817 const name_str_index = try self.makeString(decl_name);890 const capacity = decl.link.macho.capacity(self.*);
818 const addr = try self.allocateTextBlock(&decl.link.macho, code.len, required_alignment);891 const need_realloc = code.len > capacity or !mem.isAlignedGeneric(u64, symbol.n_value, required_alignment);
819 log.debug("allocated text block for {} at 0x{x}\n", .{ decl_name, addr });892 if (need_realloc) {
820893 const vaddr = try self.growTextBlock(&decl.link.macho, code.len, required_alignment);
821 symbol.* = .{894 log.debug("growing {} from 0x{x} to 0x{x}\n", .{ decl.name, symbol.n_value, vaddr });
822 .n_strx = name_str_index,895 if (vaddr != symbol.n_value) {
823 .n_type = macho.N_SECT,896 symbol.n_value = vaddr;
824 .n_sect = @intCast(u8, self.text_section_index.?) + 1,897
825 .n_desc = 0,898 log.debug(" (writing new offset table entry)\n", .{});
826 .n_value = addr,899 self.offset_table.items[decl.link.macho.offset_table_index] = vaddr;
827 };900 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);
828 self.offset_table.items[decl.link.macho.offset_table_index] = addr;901 }
902 } else if (code.len < decl.link.macho.size) {
903 self.shrinkTextBlock(&decl.link.macho, code.len);
904 }
905 decl.link.macho.size = code.len;
906 symbol.n_strx = try self.updateString(symbol.n_strx, mem.spanZ(decl.name));
907 symbol.n_type = macho.N_SECT;
908 symbol.n_sect = @intCast(u8, self.text_section_index.?) + 1;
909 symbol.n_desc = 0;
910 // TODO this write could be avoided if no fields of the symbol were changed.
911 try self.writeSymbol(decl.link.macho.local_sym_index);
912 } else {
913 const decl_name = mem.spanZ(decl.name);
914 const name_str_index = try self.makeString(decl_name);
915 const addr = try self.allocateTextBlock(&decl.link.macho, code.len, required_alignment);
916 log.debug("allocated text block for {} at 0x{x}\n", .{ decl_name, addr });
917 errdefer self.freeTextBlock(&decl.link.macho);
918
919 symbol.* = .{
920 .n_strx = name_str_index,
921 .n_type = macho.N_SECT,
922 .n_sect = @intCast(u8, self.text_section_index.?) + 1,
923 .n_desc = 0,
924 .n_value = addr,
925 };
926 self.offset_table.items[decl.link.macho.offset_table_index] = addr;
829927
830 try self.writeSymbol(decl.link.macho.local_sym_index);928 try self.writeSymbol(decl.link.macho.local_sym_index);
831 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);929 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);
930 }
832931
833 const text_section = self.sections.items[self.text_section_index.?];932 const text_section = self.sections.items[self.text_section_index.?];
834 const section_offset = symbol.n_value - text_section.addr;933 const section_offset = symbol.n_value - text_section.addr;