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) {
415415}
416416
417417pub fn add(comptime T: type, a: T, b: T) (error{Overflow}!T) {
418 if (T == comptime_int) return a + b;
418419 var answer: T = undefined;
419420 return if (@addWithOverflow(T, a, b, &answer)) error.Overflow else answer;
420421}
src/link/Elf.zig+31-29
......@@ -102,11 +102,11 @@ error_flags: File.ErrorFlags = File.ErrorFlags{},
102102/// or removed from the freelist.
103103///
104104/// 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 so
105/// padToIdeal(minimum_text_block_size). That is, when it has so
106106/// much extra capacity, that we could fit a small new symbol in it, itself with
107107/// ideal_capacity or more.
108108///
109/// Ideal capacity is defined by size * alloc_num / alloc_den.
109/// Ideal capacity is defined by size + (size / ideal_factor)
110110///
111111/// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
112112/// 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) = .{},
127127dbg_info_decl_first: ?*TextBlock = null,
128128dbg_info_decl_last: ?*TextBlock = null,
129129
130/// `alloc_num / alloc_den` is the factor of padding when allocating.
131const alloc_num = 4;
132const alloc_den = 3;
130/// When allocating, the ideal_capacity is calculated by
131/// actual_capacity + (actual_capacity / ideal_factor)
132const ideal_factor = 3;
133133
134134/// In order for a slice of bytes to be considered eligible to keep metadata pointing at
135135/// it as a possible place to put new symbols, it must have enough room for this many bytes
136136/// (plus extra for reserved capacity).
137137const 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
140140pub const PtrWidth = enum { p32, p64 };
141141
......@@ -154,7 +154,7 @@ pub const TextBlock = struct {
154154 prev: ?*TextBlock,
155155 next: ?*TextBlock,
156156
157 /// Previous/next linked list pointers. This value is `next ^ prev`.
157 /// Previous/next linked list pointers.
158158 /// This is the linked list node for this Decl's corresponding .debug_info tag.
159159 dbg_info_prev: ?*TextBlock,
160160 dbg_info_next: ?*TextBlock,
......@@ -194,7 +194,7 @@ pub const TextBlock = struct {
194194 const self_sym = elf_file.local_symbols.items[self.local_sym_index];
195195 const next_sym = elf_file.local_symbols.items[next.local_sym_index];
196196 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);
198198 if (cap <= ideal_cap) return false;
199199 const surplus = cap - ideal_cap;
200200 return surplus >= min_text_capacity;
......@@ -338,12 +338,12 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
338338 if (start < ehdr_size)
339339 return ehdr_size;
340340
341 const end = start + satMul(size, alloc_num) / alloc_den;
341 const end = start + padToIdeal(size);
342342
343343 if (self.shdr_table_offset) |off| {
344344 const shdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Shdr) else @sizeOf(elf.Elf64_Shdr);
345345 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);
347347 const test_end = off + increased_size;
348348 if (end > off and start < test_end) {
349349 return test_end;
......@@ -353,7 +353,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
353353 if (self.phdr_table_offset) |off| {
354354 const phdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Phdr) else @sizeOf(elf.Elf64_Phdr);
355355 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);
357357 const test_end = off + increased_size;
358358 if (end > off and start < test_end) {
359359 return test_end;
......@@ -361,14 +361,14 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
361361 }
362362
363363 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);
365365 const test_end = section.sh_offset + increased_size;
366366 if (end > section.sh_offset and start < test_end) {
367367 return test_end;
368368 }
369369 }
370370 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);
372372 const test_end = program_header.p_offset + increased_size;
373373 if (end > program_header.p_offset and start < test_end) {
374374 return test_end;
......@@ -1956,7 +1956,7 @@ fn growTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, alignm
19561956fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
19571957 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
19581958 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
19611961 // We use these to indicate our intention to update metadata, placing the new block,
19621962 // and possibly removing a free list node.
......@@ -1976,7 +1976,7 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al
19761976 // Is it enough that we could fit this new text block?
19771977 const sym = self.local_symbols.items[big_block.local_sym_index];
19781978 const capacity = big_block.capacity(self.*);
1979 const ideal_capacity = capacity * alloc_num / alloc_den;
1979 const ideal_capacity = padToIdeal(capacity);
19801980 const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity;
19811981 const capacity_end_vaddr = sym.st_value + capacity;
19821982 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
20062006 break :blk new_start_vaddr;
20072007 } else if (self.last_text_block) |last| {
20082008 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);
20102010 const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity;
20112011 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);
20122012 // 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 {
23702370
23712371 // 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` for
2373 // This logic is nearly identical to the logic below in `updateDeclDebugInfoAllocation` for
23742374 // `TextBlock` and the .debug_info. If you are editing this logic, you
23752375 // probably need to edit that logic too.
23762376
......@@ -2386,6 +2386,7 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
23862386 _ = self.dbg_line_fn_free_list.put(self.base.allocator, prev, {}) catch {};
23872387 prev.next = src_fn.next;
23882388 }
2389 assert(src_fn.prev != next);
23892390 next.prev = src_fn.prev;
23902391 src_fn.next = null;
23912392 // Populate where it used to be with NOPs.
......@@ -2396,23 +2397,24 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
23962397 last.next = src_fn;
23972398 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);
24002401 }
24012402 } else if (src_fn.prev == null) {
24022403 // Append new function.
24032404 // TODO Look at the free list before appending at the end.
2405 assert(src_fn != last);
24042406 src_fn.prev = last;
24052407 last.next = src_fn;
24062408 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);
24092411 }
24102412 } else {
24112413 // This is the first function of the Line Number Program.
24122414 self.dbg_line_fn_first = src_fn;
24132415 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());
24162418 }
24172419
24182420 const last_src_fn = self.dbg_line_fn_last.?;
......@@ -2544,7 +2546,7 @@ fn updateDeclDebugInfoAllocation(self: *Elf, text_block: *TextBlock, len: u32) !
25442546 last.dbg_info_next = text_block;
25452547 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);
25482550 }
25492551 } else if (text_block.dbg_info_prev == null) {
25502552 // Append new Decl.
......@@ -2553,14 +2555,14 @@ fn updateDeclDebugInfoAllocation(self: *Elf, text_block: *TextBlock, len: u32) !
25532555 last.dbg_info_next = text_block;
25542556 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);
25572559 }
25582560 } else {
25592561 // This is the first Decl of the .debug_info
25602562 self.dbg_info_decl_first = text_block;
25612563 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());
25642566 }
25652567}
25662568
......@@ -3127,12 +3129,6 @@ fn pwriteDbgInfoNops(
31273129 try self.base.file.?.pwritevAll(vecs[0..vec_index], offset - prev_padding_size);
31283130}
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
31363132fn bswapAllFields(comptime S: type, ptr: *S) void {
31373133 @panic("TODO implement bswapAllFields");
31383134}
......@@ -3194,3 +3190,9 @@ fn getLDMOption(target: std.Target) ?[]const u8 {
31943190 else => return null,
31953191 }
31963192}
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 {
234234 prev: ?*TextBlock,
235235 next: ?*TextBlock,
236236
237 /// Previous/next linked list pointers. This value is `next ^ prev`.
237 /// Previous/next linked list pointers.
238238 /// This is the linked list node for this Decl's corresponding .debug_info tag.
239239 dbg_info_prev: ?*TextBlock,
240240 dbg_info_next: ?*TextBlock,