authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-19 13:45:36-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-19 13:47:51-07:00
logd5d0619aacccd5e3b933b3196fba5ab6f8f9da47
treec6705b09ed101a0fb362f1a646b1a44b9aac3d30
parentd5b0a963d1bf3399e3d8b63b03ea61f7d771adbd

stage2: ELF: avoid multiplication for ideal capacity

ideal capacity is now determined by e.g. x += x / f rather than x = x * b / a This turns a multiplication into an addition, making it less likely to overflow the integer. This commit also introduces padToIdeal() which does saturating arithmetic so that no overflow is possible when calculating ideal capacity. closes #7830

3 files changed, 33 insertions(+), 30 deletions(-)

lib/std/math.zig+1
...@@ -415,6 +415,7 @@ pub fn mul(comptime T: type, a: T, b: T) (error{Overflow}!T) {...@@ -415,6 +415,7 @@ pub fn mul(comptime T: type, a: T, b: T) (error{Overflow}!T) {
415}415}
416416
417pub fn add(comptime T: type, a: T, b: T) (error{Overflow}!T) {417pub fn add(comptime T: type, a: T, b: T) (error{Overflow}!T) {
418 if (T == comptime_int) return a + b;
418 var answer: T = undefined;419 var answer: T = undefined;
419 return if (@addWithOverflow(T, a, b, &answer)) error.Overflow else answer;420 return if (@addWithOverflow(T, a, b, &answer)) error.Overflow else answer;
420}421}
src/link/Elf.zig+31-29
...@@ -102,11 +102,11 @@ error_flags: File.ErrorFlags = File.ErrorFlags{},...@@ -102,11 +102,11 @@ error_flags: File.ErrorFlags = File.ErrorFlags{},
102/// or removed from the freelist.102/// or removed from the freelist.
103///103///
104/// A text block has surplus capacity when its overcapacity value is greater than104/// A text block has surplus capacity when its overcapacity value is greater than
105/// minimum_text_block_size * alloc_num / alloc_den. That is, when it has so105/// padToIdeal(minimum_text_block_size). That is, when it has so
106/// much extra capacity, that we could fit a small new symbol in it, itself with106/// much extra capacity, that we could fit a small new symbol in it, itself with
107/// ideal_capacity or more.107/// ideal_capacity or more.
108///108///
109/// Ideal capacity is defined by size * alloc_num / alloc_den.109/// Ideal capacity is defined by size + (size / ideal_factor)
110///110///
111/// Overcapacity is measured by actual_capacity - ideal_capacity. Note that111/// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
112/// overcapacity can be negative. A simple way to have negative overcapacity is to112/// overcapacity can be negative. A simple way to have negative overcapacity is to
...@@ -127,15 +127,15 @@ dbg_info_decl_free_list: std.AutoHashMapUnmanaged(*TextBlock, void) = .{},...@@ -127,15 +127,15 @@ dbg_info_decl_free_list: std.AutoHashMapUnmanaged(*TextBlock, void) = .{},
127dbg_info_decl_first: ?*TextBlock = null,127dbg_info_decl_first: ?*TextBlock = null,
128dbg_info_decl_last: ?*TextBlock = null,128dbg_info_decl_last: ?*TextBlock = null,
129129
130/// `alloc_num / alloc_den` is the factor of padding when allocating.130/// When allocating, the ideal_capacity is calculated by
131const alloc_num = 4;131/// actual_capacity + (actual_capacity / ideal_factor)
132const alloc_den = 3;132const ideal_factor = 3;
133133
134/// In order for a slice of bytes to be considered eligible to keep metadata pointing at134/// In order for a slice of bytes to be considered eligible to keep metadata pointing at
135/// it as a possible place to put new symbols, it must have enough room for this many bytes135/// it as a possible place to put new symbols, it must have enough room for this many bytes
136/// (plus extra for reserved capacity).136/// (plus extra for reserved capacity).
137const minimum_text_block_size = 64;137const minimum_text_block_size = 64;
138const min_text_capacity = minimum_text_block_size * alloc_num / alloc_den;138const min_text_capacity = padToIdeal(minimum_text_block_size);
139139
140pub const PtrWidth = enum { p32, p64 };140pub const PtrWidth = enum { p32, p64 };
141141
...@@ -154,7 +154,7 @@ pub const TextBlock = struct {...@@ -154,7 +154,7 @@ pub const TextBlock = struct {
154 prev: ?*TextBlock,154 prev: ?*TextBlock,
155 next: ?*TextBlock,155 next: ?*TextBlock,
156156
157 /// Previous/next linked list pointers. This value is `next ^ prev`.157 /// Previous/next linked list pointers.
158 /// This is the linked list node for this Decl's corresponding .debug_info tag.158 /// This is the linked list node for this Decl's corresponding .debug_info tag.
159 dbg_info_prev: ?*TextBlock,159 dbg_info_prev: ?*TextBlock,
160 dbg_info_next: ?*TextBlock,160 dbg_info_next: ?*TextBlock,
...@@ -194,7 +194,7 @@ pub const TextBlock = struct {...@@ -194,7 +194,7 @@ pub const TextBlock = struct {
194 const self_sym = elf_file.local_symbols.items[self.local_sym_index];194 const self_sym = elf_file.local_symbols.items[self.local_sym_index];
195 const next_sym = elf_file.local_symbols.items[next.local_sym_index];195 const next_sym = elf_file.local_symbols.items[next.local_sym_index];
196 const cap = next_sym.st_value - self_sym.st_value;196 const cap = next_sym.st_value - self_sym.st_value;
197 const ideal_cap = self_sym.st_size * alloc_num / alloc_den;197 const ideal_cap = padToIdeal(self_sym.st_size);
198 if (cap <= ideal_cap) return false;198 if (cap <= ideal_cap) return false;
199 const surplus = cap - ideal_cap;199 const surplus = cap - ideal_cap;
200 return surplus >= min_text_capacity;200 return surplus >= min_text_capacity;
...@@ -338,12 +338,12 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {...@@ -338,12 +338,12 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
338 if (start < ehdr_size)338 if (start < ehdr_size)
339 return ehdr_size;339 return ehdr_size;
340340
341 const end = start + satMul(size, alloc_num) / alloc_den;341 const end = start + padToIdeal(size);
342342
343 if (self.shdr_table_offset) |off| {343 if (self.shdr_table_offset) |off| {
344 const shdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Shdr) else @sizeOf(elf.Elf64_Shdr);344 const shdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Shdr) else @sizeOf(elf.Elf64_Shdr);
345 const tight_size = self.sections.items.len * shdr_size;345 const tight_size = self.sections.items.len * shdr_size;
346 const increased_size = satMul(tight_size, alloc_num) / alloc_den;346 const increased_size = padToIdeal(tight_size);
347 const test_end = off + increased_size;347 const test_end = off + increased_size;
348 if (end > off and start < test_end) {348 if (end > off and start < test_end) {
349 return test_end;349 return test_end;
...@@ -353,7 +353,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {...@@ -353,7 +353,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
353 if (self.phdr_table_offset) |off| {353 if (self.phdr_table_offset) |off| {
354 const phdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Phdr) else @sizeOf(elf.Elf64_Phdr);354 const phdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Phdr) else @sizeOf(elf.Elf64_Phdr);
355 const tight_size = self.sections.items.len * phdr_size;355 const tight_size = self.sections.items.len * phdr_size;
356 const increased_size = satMul(tight_size, alloc_num) / alloc_den;356 const increased_size = padToIdeal(tight_size);
357 const test_end = off + increased_size;357 const test_end = off + increased_size;
358 if (end > off and start < test_end) {358 if (end > off and start < test_end) {
359 return test_end;359 return test_end;
...@@ -361,14 +361,14 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {...@@ -361,14 +361,14 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
361 }361 }
362362
363 for (self.sections.items) |section| {363 for (self.sections.items) |section| {
364 const increased_size = satMul(section.sh_size, alloc_num) / alloc_den;364 const increased_size = padToIdeal(section.sh_size);
365 const test_end = section.sh_offset + increased_size;365 const test_end = section.sh_offset + increased_size;
366 if (end > section.sh_offset and start < test_end) {366 if (end > section.sh_offset and start < test_end) {
367 return test_end;367 return test_end;
368 }368 }
369 }369 }
370 for (self.program_headers.items) |program_header| {370 for (self.program_headers.items) |program_header| {
371 const increased_size = satMul(program_header.p_filesz, alloc_num) / alloc_den;371 const increased_size = padToIdeal(program_header.p_filesz);
372 const test_end = program_header.p_offset + increased_size;372 const test_end = program_header.p_offset + increased_size;
373 if (end > program_header.p_offset and start < test_end) {373 if (end > program_header.p_offset and start < test_end) {
374 return test_end;374 return test_end;
...@@ -1956,7 +1956,7 @@ fn growTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, alignm...@@ -1956,7 +1956,7 @@ fn growTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, alignm
1956fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {1956fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
1957 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];1957 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
1958 const shdr = &self.sections.items[self.text_section_index.?];1958 const shdr = &self.sections.items[self.text_section_index.?];
1959 const new_block_ideal_capacity = new_block_size * alloc_num / alloc_den;1959 const new_block_ideal_capacity = padToIdeal(new_block_size);
19601960
1961 // We use these to indicate our intention to update metadata, placing the new block,1961 // We use these to indicate our intention to update metadata, placing the new block,
1962 // and possibly removing a free list node.1962 // and possibly removing a free list node.
...@@ -1976,7 +1976,7 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al...@@ -1976,7 +1976,7 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al
1976 // Is it enough that we could fit this new text block?1976 // Is it enough that we could fit this new text block?
1977 const sym = self.local_symbols.items[big_block.local_sym_index];1977 const sym = self.local_symbols.items[big_block.local_sym_index];
1978 const capacity = big_block.capacity(self.*);1978 const capacity = big_block.capacity(self.*);
1979 const ideal_capacity = capacity * alloc_num / alloc_den;1979 const ideal_capacity = padToIdeal(capacity);
1980 const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity;1980 const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity;
1981 const capacity_end_vaddr = sym.st_value + capacity;1981 const capacity_end_vaddr = sym.st_value + capacity;
1982 const new_start_vaddr_unaligned = capacity_end_vaddr - new_block_ideal_capacity;1982 const new_start_vaddr_unaligned = capacity_end_vaddr - new_block_ideal_capacity;
...@@ -2006,7 +2006,7 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al...@@ -2006,7 +2006,7 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al
2006 break :blk new_start_vaddr;2006 break :blk new_start_vaddr;
2007 } else if (self.last_text_block) |last| {2007 } else if (self.last_text_block) |last| {
2008 const sym = self.local_symbols.items[last.local_sym_index];2008 const sym = self.local_symbols.items[last.local_sym_index];
2009 const ideal_capacity = sym.st_size * alloc_num / alloc_den;2009 const ideal_capacity = padToIdeal(sym.st_size);
2010 const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity;2010 const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity;
2011 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);2011 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);
2012 // Set up the metadata to be updated, after errors are no longer possible.2012 // Set up the metadata to be updated, after errors are no longer possible.
...@@ -2370,7 +2370,7 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {...@@ -2370,7 +2370,7 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
23702370
2371 // Now we have the full contents and may allocate a region to store it.2371 // Now we have the full contents and may allocate a region to store it.
23722372
2373 // This logic is nearly identical to the logic below in `updateDeclDebugInfo` for2373 // This logic is nearly identical to the logic below in `updateDeclDebugInfoAllocation` for
2374 // `TextBlock` and the .debug_info. If you are editing this logic, you2374 // `TextBlock` and the .debug_info. If you are editing this logic, you
2375 // probably need to edit that logic too.2375 // probably need to edit that logic too.
23762376
...@@ -2386,6 +2386,7 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {...@@ -2386,6 +2386,7 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
2386 _ = self.dbg_line_fn_free_list.put(self.base.allocator, prev, {}) catch {};2386 _ = self.dbg_line_fn_free_list.put(self.base.allocator, prev, {}) catch {};
2387 prev.next = src_fn.next;2387 prev.next = src_fn.next;
2388 }2388 }
2389 assert(src_fn.prev != next);
2389 next.prev = src_fn.prev;2390 next.prev = src_fn.prev;
2390 src_fn.next = null;2391 src_fn.next = null;
2391 // Populate where it used to be with NOPs.2392 // Populate where it used to be with NOPs.
...@@ -2396,23 +2397,24 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {...@@ -2396,23 +2397,24 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
2396 last.next = src_fn;2397 last.next = src_fn;
2397 self.dbg_line_fn_last = src_fn;2398 self.dbg_line_fn_last = src_fn;
23982399
2399 src_fn.off = last.off + (last.len * alloc_num / alloc_den);2400 src_fn.off = last.off + padToIdeal(last.len);
2400 }2401 }
2401 } else if (src_fn.prev == null) {2402 } else if (src_fn.prev == null) {
2402 // Append new function.2403 // Append new function.
2403 // TODO Look at the free list before appending at the end.2404 // TODO Look at the free list before appending at the end.
2405 assert(src_fn != last);
2404 src_fn.prev = last;2406 src_fn.prev = last;
2405 last.next = src_fn;2407 last.next = src_fn;
2406 self.dbg_line_fn_last = src_fn;2408 self.dbg_line_fn_last = src_fn;
24072409
2408 src_fn.off = last.off + (last.len * alloc_num / alloc_den);2410 src_fn.off = last.off + padToIdeal(last.len);
2409 }2411 }
2410 } else {2412 } else {
2411 // This is the first function of the Line Number Program.2413 // This is the first function of the Line Number Program.
2412 self.dbg_line_fn_first = src_fn;2414 self.dbg_line_fn_first = src_fn;
2413 self.dbg_line_fn_last = src_fn;2415 self.dbg_line_fn_last = src_fn;
24142416
2415 src_fn.off = self.dbgLineNeededHeaderBytes() * alloc_num / alloc_den;2417 src_fn.off = padToIdeal(self.dbgLineNeededHeaderBytes());
2416 }2418 }
24172419
2418 const last_src_fn = self.dbg_line_fn_last.?;2420 const last_src_fn = self.dbg_line_fn_last.?;
...@@ -2544,7 +2546,7 @@ fn updateDeclDebugInfoAllocation(self: *Elf, text_block: *TextBlock, len: u32) !...@@ -2544,7 +2546,7 @@ fn updateDeclDebugInfoAllocation(self: *Elf, text_block: *TextBlock, len: u32) !
2544 last.dbg_info_next = text_block;2546 last.dbg_info_next = text_block;
2545 self.dbg_info_decl_last = text_block;2547 self.dbg_info_decl_last = text_block;
25462548
2547 text_block.dbg_info_off = last.dbg_info_off + (last.dbg_info_len * alloc_num / alloc_den);2549 text_block.dbg_info_off = last.dbg_info_off + padToIdeal(last.dbg_info_len);
2548 }2550 }
2549 } else if (text_block.dbg_info_prev == null) {2551 } else if (text_block.dbg_info_prev == null) {
2550 // Append new Decl.2552 // Append new Decl.
...@@ -2553,14 +2555,14 @@ fn updateDeclDebugInfoAllocation(self: *Elf, text_block: *TextBlock, len: u32) !...@@ -2553,14 +2555,14 @@ fn updateDeclDebugInfoAllocation(self: *Elf, text_block: *TextBlock, len: u32) !
2553 last.dbg_info_next = text_block;2555 last.dbg_info_next = text_block;
2554 self.dbg_info_decl_last = text_block;2556 self.dbg_info_decl_last = text_block;
25552557
2556 text_block.dbg_info_off = last.dbg_info_off + (last.dbg_info_len * alloc_num / alloc_den);2558 text_block.dbg_info_off = last.dbg_info_off + padToIdeal(last.dbg_info_len);
2557 }2559 }
2558 } else {2560 } else {
2559 // This is the first Decl of the .debug_info2561 // This is the first Decl of the .debug_info
2560 self.dbg_info_decl_first = text_block;2562 self.dbg_info_decl_first = text_block;
2561 self.dbg_info_decl_last = text_block;2563 self.dbg_info_decl_last = text_block;
25622564
2563 text_block.dbg_info_off = self.dbgInfoNeededHeaderBytes() * alloc_num / alloc_den;2565 text_block.dbg_info_off = padToIdeal(self.dbgInfoNeededHeaderBytes());
2564 }2566 }
2565}2567}
25662568
...@@ -3127,12 +3129,6 @@ fn pwriteDbgInfoNops(...@@ -3127,12 +3129,6 @@ fn pwriteDbgInfoNops(
3127 try self.base.file.?.pwritevAll(vecs[0..vec_index], offset - prev_padding_size);3129 try self.base.file.?.pwritevAll(vecs[0..vec_index], offset - prev_padding_size);
3128}3130}
31293131
3130/// Saturating multiplication
3131fn satMul(a: anytype, b: anytype) @TypeOf(a, b) {
3132 const T = @TypeOf(a, b);
3133 return std.math.mul(T, a, b) catch std.math.maxInt(T);
3134}
3135
3136fn bswapAllFields(comptime S: type, ptr: *S) void {3132fn bswapAllFields(comptime S: type, ptr: *S) void {
3137 @panic("TODO implement bswapAllFields");3133 @panic("TODO implement bswapAllFields");
3138}3134}
...@@ -3194,3 +3190,9 @@ fn getLDMOption(target: std.Target) ?[]const u8 {...@@ -3194,3 +3190,9 @@ fn getLDMOption(target: std.Target) ?[]const u8 {
3194 else => return null,3190 else => return null,
3195 }3191 }
3196}3192}
3193
3194fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
3195 // TODO https://github.com/ziglang/zig/issues/1284
3196 return std.math.add(@TypeOf(actual_size), actual_size, actual_size / ideal_factor) catch
3197 std.math.maxInt(@TypeOf(actual_size));
3198}
src/link/MachO.zig+1-1
...@@ -234,7 +234,7 @@ pub const TextBlock = struct {...@@ -234,7 +234,7 @@ pub const TextBlock = struct {
234 prev: ?*TextBlock,234 prev: ?*TextBlock,
235 next: ?*TextBlock,235 next: ?*TextBlock,
236236
237 /// Previous/next linked list pointers. This value is `next ^ prev`.237 /// Previous/next linked list pointers.
238 /// This is the linked list node for this Decl's corresponding .debug_info tag.238 /// This is the linked list node for this Decl's corresponding .debug_info tag.
239 dbg_info_prev: ?*TextBlock,239 dbg_info_prev: ?*TextBlock,
240 dbg_info_next: ?*TextBlock,240 dbg_info_next: ?*TextBlock,