authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-10-17 12:34:16+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-10-19 18:30:33+01:00
log8f183c8c185f43db596b6aac1983f97403503592
tree8ba0adac8a2c240da058c8d9ca17412b11191c2f
parent64caa0e324d522915769dd5cc8a11374f919f01f
signature Commit is signed but in an unrecognized format.

MappedFile: slight tweak to node deletion

This strategy is a bit simpler and lets us reuse nodes more quickly, at the cost of a single bit (of which we had 17 spare anyway).

4 files changed, 56 insertions(+), 24 deletions(-)

src/link/Coff.zig+1-1
...@@ -6100,7 +6100,7 @@ pub fn idle(coff: *Coff) !bool {...@@ -6100,7 +6100,7 @@ pub fn idle(coff: *Coff) !bool {
6100 break :task;6100 break :task;
6101 }6101 }
6102 while (coff.mf.updates.pop()) |ni| : (coff.mf.update_prog_node.completeOne()) {6102 while (coff.mf.updates.pop()) |ni| : (coff.mf.update_prog_node.completeOne()) {
6103 if (ni.pendingDelete(&coff.mf)) continue;6103 ni.cleanUpdated(&coff.mf);
6104 const clean_moved = ni.cleanMoved(&coff.mf);6104 const clean_moved = ni.cleanMoved(&coff.mf);
6105 const clean_resized = ni.cleanResized(&coff.mf);6105 const clean_resized = ni.cleanResized(&coff.mf);
6106 const clean_next_moved = ni.cleanNextMoved(&coff.mf);6106 const clean_next_moved = ni.cleanNextMoved(&coff.mf);
src/link/Elf2.zig+1-1
...@@ -10857,7 +10857,7 @@ pub fn idle(elf: *Elf) link.Error!bool {...@@ -10857,7 +10857,7 @@ pub fn idle(elf: *Elf) link.Error!bool {
10857 break :task;10857 break :task;
10858 }10858 }
10859 while (elf.mf.updates.pop()) |ni| : (elf.mf.update_prog_node.completeOne()) {10859 while (elf.mf.updates.pop()) |ni| : (elf.mf.update_prog_node.completeOne()) {
10860 if (ni.pendingDelete(&elf.mf)) continue;10860 ni.cleanUpdated(&elf.mf);
10861 const clean_moved = ni.cleanMoved(&elf.mf);10861 const clean_moved = ni.cleanMoved(&elf.mf);
10862 const clean_resized = ni.cleanResized(&elf.mf);10862 const clean_resized = ni.cleanResized(&elf.mf);
10863 const clean_next_moved = ni.cleanNextMoved(&elf.mf);10863 const clean_next_moved = ni.cleanNextMoved(&elf.mf);
src/link/MachO2.zig+1
...@@ -3342,6 +3342,7 @@ pub fn idle(macho: *MachO) link.Error!bool {...@@ -3342,6 +3342,7 @@ pub fn idle(macho: *MachO) link.Error!bool {
3342 }3342 }
33433343
3344 while (macho.mf.updates.pop()) |ni| {3344 while (macho.mf.updates.pop()) |ni| {
3345 ni.cleanUpdated(&macho.mf);
3345 const clean_moved = ni.cleanMoved(&macho.mf);3346 const clean_moved = ni.cleanMoved(&macho.mf);
3346 const clean_resized = ni.cleanResized(&macho.mf);3347 const clean_resized = ni.cleanResized(&macho.mf);
3347 const clean_next_moved = ni.cleanNextMoved(&macho.mf);3348 const clean_next_moved = ni.cleanNextMoved(&macho.mf);
src/link/MappedFile.zig+53-22
...@@ -207,6 +207,7 @@ pub fn init(file: Io.File, gpa: Allocator, io: Io) (Allocator.Error || Io.Cancel...@@ -207,6 +207,7 @@ pub fn init(file: Io.File, gpa: Allocator, io: Io) (Allocator.Error || Io.Cancel
207 .resized = false,207 .resized = false,
208 .next_moved = false,208 .next_moved = false,
209 .has_content = false,209 .has_content = false,
210 .updated = false,
210 },211 },
211 .location_payload = switch (root_location) {212 .location_payload = switch (root_location) {
212 .small => |small| .{ .small = small },213 .small => |small| .{ .small = small },
...@@ -287,7 +288,14 @@ pub const Node = extern struct {...@@ -287,7 +288,14 @@ pub const Node = extern struct {
287 next_moved: bool,288 next_moved: bool,
288 /// Whether this node might contain initialized bytes.289 /// Whether this node might contain initialized bytes.
289 has_content: bool,290 has_content: bool,
290 unused: u17 = 0,291
292 /// Whether this node is currently in `updates`. Usually this is just equal to whether any
293 /// of `moved`, `resized`, or `next_moved` is set, but when a node is deleted and later
294 /// reused, it may still be in `updates`, so preserving this flag through the deletion
295 /// prevents us from adding it to `updates` a second time.
296 updated: bool,
297
298 unused: u16 = 0,
291 };299 };
292300
293 pub const Location = union(enum(u1)) {301 pub const Location = union(enum(u1)) {
...@@ -494,6 +502,11 @@ pub const Node = extern struct {...@@ -494,6 +502,11 @@ pub const Node = extern struct {
494 }502 }
495 }503 }
496504
505 pub fn cleanUpdated(ni: Node.Index, mf: *MappedFile) void {
506 assert(ni.get(mf).flags.updated);
507 ni.get(mf).flags.updated = false;
508 }
509
497 pub fn hasMoved(ni: Node.Index, mf: *const MappedFile) bool {510 pub fn hasMoved(ni: Node.Index, mf: *const MappedFile) bool {
498 var cur_ni = ni;511 var cur_ni = ni;
499 while (cur_ni != .root) {512 while (cur_ni != .root) {
...@@ -518,9 +531,11 @@ pub const Node = extern struct {...@@ -518,9 +531,11 @@ pub const Node = extern struct {
518 if (node.prev.unwrap()) |prev_ni| prev_ni.nextMovedAssumeCapacity(mf);531 if (node.prev.unwrap()) |prev_ni| prev_ni.nextMovedAssumeCapacity(mf);
519 if (ni.hasMoved(mf)) return;532 if (ni.hasMoved(mf)) return;
520 node.flags.moved = true;533 node.flags.moved = true;
521 if (node.flags.resized or node.flags.next_moved) return;534 if (!node.flags.updated) {
522 mf.updates.appendAssumeCapacity(ni);535 node.flags.updated = true;
523 mf.update_prog_node.increaseEstimatedTotalItems(1);536 mf.updates.appendAssumeCapacity(ni);
537 mf.update_prog_node.increaseEstimatedTotalItems(1);
538 }
524 }539 }
525540
526 pub fn hasResized(ni: Node.Index, mf: *const MappedFile) bool {541 pub fn hasResized(ni: Node.Index, mf: *const MappedFile) bool {
...@@ -539,9 +554,11 @@ pub const Node = extern struct {...@@ -539,9 +554,11 @@ pub const Node = extern struct {
539 const node = ni.get(mf);554 const node = ni.get(mf);
540 if (node.flags.resized) return;555 if (node.flags.resized) return;
541 node.flags.resized = true;556 node.flags.resized = true;
542 if (node.flags.moved or node.flags.next_moved) return;557 if (!node.flags.updated) {
543 mf.updates.appendAssumeCapacity(ni);558 node.flags.updated = true;
544 mf.update_prog_node.increaseEstimatedTotalItems(1);559 mf.updates.appendAssumeCapacity(ni);
560 mf.update_prog_node.increaseEstimatedTotalItems(1);
561 }
545 }562 }
546563
547 pub fn hasNextMoved(ni: Node.Index, mf: *const MappedFile) bool {564 pub fn hasNextMoved(ni: Node.Index, mf: *const MappedFile) bool {
...@@ -560,9 +577,11 @@ pub const Node = extern struct {...@@ -560,9 +577,11 @@ pub const Node = extern struct {
560 const node = ni.get(mf);577 const node = ni.get(mf);
561 if (!node.flags.enable_next_moved or node.flags.next_moved) return;578 if (!node.flags.enable_next_moved or node.flags.next_moved) return;
562 node.flags.next_moved = true;579 node.flags.next_moved = true;
563 if (node.flags.moved or node.flags.resized) return;580 if (!node.flags.updated) {
564 mf.updates.appendAssumeCapacity(ni);581 node.flags.updated = true;
565 mf.update_prog_node.increaseEstimatedTotalItems(1);582 mf.updates.appendAssumeCapacity(ni);
583 mf.update_prog_node.increaseEstimatedTotalItems(1);
584 }
566 }585 }
567586
568 pub fn alignment(ni: Node.Index, mf: *const MappedFile) Alignment {587 pub fn alignment(ni: Node.Index, mf: *const MappedFile) Alignment {
...@@ -647,19 +666,30 @@ pub const Node = extern struct {...@@ -647,19 +666,30 @@ pub const Node = extern struct {
647 pub fn delete(ni: Node.Index, gpa: Allocator, mf: *MappedFile) Allocator.Error!void {666 pub fn delete(ni: Node.Index, gpa: Allocator, mf: *MappedFile) Allocator.Error!void {
648 const node = ni.get(mf);667 const node = ni.get(mf);
649 assert(node.first == .none and node.last == .none); // has children668 assert(node.first == .none and node.last == .none); // has children
650 mf.removeNodesFromChildList(gpa, ni, ni);669 try mf.removeNodesFromChildList(gpa, ni, ni);
651 const updated = node.flags.moved or node.flags.resized or node.flags.next_moved;670 node.* = .{
652 node.* = undefined;671 .parent = undefined,
653 node.next = ni.toOptional();672 .prev = undefined,
654 if (!updated) assert(ni.pendingDelete(mf));673 .next = mf.free_ni,
655 }674 .first = undefined,
656675 .last = undefined,
657 pub fn pendingDelete(ni: Node.Index, mf: *MappedFile) bool {676 .flags = .{
658 const node = ni.get(mf);677 .position = .floating,
659 if (node.next != ni.toOptional()) return false;678 .alignment = .@"1",
660 node.next = mf.free_ni;679 .bubbles_moved = false,
680 .enable_next_moved = false,
681 .location_tag = .small,
682 .moved = false,
683 .resized = false,
684 .next_moved = false,
685 .has_content = false,
686 // It is important that we preserve this flag! When the node gets reused, this
687 // prevents it being redundantly added to `mf.updates` if it is already there.
688 .updated = node.flags.updated,
689 },
690 .location_payload = undefined,
691 };
661 mf.free_ni = ni.toOptional();692 mf.free_ni = ni.toOptional();
662 return true;
663 }693 }
664694
665 /// Ensures that the size of `ni` is at least `min_size`. Valid for any node.695 /// Ensures that the size of `ni` is at least `min_size`. Valid for any node.
...@@ -946,6 +976,7 @@ fn addNode(mf: *MappedFile, gpa: Allocator, opts: struct {...@@ -946,6 +976,7 @@ fn addNode(mf: *MappedFile, gpa: Allocator, opts: struct {
946 .resized = false,976 .resized = false,
947 .next_moved = false,977 .next_moved = false,
948 .has_content = false,978 .has_content = false,
979 .updated = false,
949 },980 },
950 .location_payload = switch (location) {981 .location_payload = switch (location) {
951 .small => |small| .{ .small = small },982 .small => |small| .{ .small = small },