| ... | @@ -134,6 +134,22 @@ error_flags: File.ErrorFlags = File.ErrorFlags{}, | ... | @@ -134,6 +134,22 @@ error_flags: File.ErrorFlags = File.ErrorFlags{}, |
| 134 | | 134 | |
| 135 | cmd_table_dirty: bool = false, | 135 | cmd_table_dirty: bool = false, |
| 136 | | 136 | |
| | 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. |
| | 152 | text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{}, |
| 137 | /// Pointer to the last allocated text block | 153 | /// Pointer to the last allocated text block |
| 138 | last_text_block: ?*TextBlock = null, | 154 | last_text_block: ?*TextBlock = null, |
| 139 | | 155 | |
| ... | @@ -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 | } |
| 756 | | 772 | |
| 757 | pub fn deinit(self: *MachO) void { | 773 | pub 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 | } |
| 769 | | 786 | |
| | 787 | fn 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 | |
| | 829 | fn 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 | |
| | 834 | fn 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 | |
| 770 | pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void { | 842 | pub 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; |
| 772 | | 844 | |
| ... | @@ -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 | }; |
| 812 | | 884 | |
| 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]; |
| 815 | | 888 | |
| 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) { |
| 820 | | 893 | 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; |
| 829 | | 927 | |
| 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 | } |
| 832 | | 931 | |
| 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; |