| ... | @@ -21,6 +21,15 @@ const Alignment = MappedFile.Alignment; | ... | @@ -21,6 +21,15 @@ const Alignment = MappedFile.Alignment; |
| 21 | base: link.File, | 21 | base: link.File, |
| 22 | options: link.File.OpenOptions, | 22 | options: link.File.OpenOptions, |
| 23 | mf: MappedFile, | 23 | mf: MappedFile, |
| | 24 | |
| | 25 | /// These fields could simply be read from the ehdr node when needed, but it turns out that they are |
| | 26 | /// accessed frequently enough that we see measurable performance gains from caching them here. |
| | 27 | ehdr_fields: struct { |
| | 28 | class: std.elf.CLASS, |
| | 29 | type: EhdrType, |
| | 30 | machine: EhdrMachine, |
| | 31 | }, |
| | 32 | |
| 24 | ni: struct { | 33 | ni: struct { |
| 25 | elf: MappedFile.Node.Index, | 34 | elf: MappedFile.Node.Index, |
| 26 | ehdr: MappedFile.Node.Index, | 35 | ehdr: MappedFile.Node.Index, |
| ... | @@ -5115,6 +5124,11 @@ fn create( | ... | @@ -5115,6 +5124,11 @@ fn create( |
| 5115 | }, | 5124 | }, |
| 5116 | .options = options, | 5125 | .options = options, |
| 5117 | .mf = try .init(file, comp.gpa, io), | 5126 | .mf = try .init(file, comp.gpa, io), |
| | 5127 | .ehdr_fields = .{ |
| | 5128 | .class = class, |
| | 5129 | .type = @"type", |
| | 5130 | .machine = machine, |
| | 5131 | }, |
| 5118 | .ni = .{ | 5132 | .ni = .{ |
| 5119 | .elf = undefined, | 5133 | .elf = undefined, |
| 5120 | .ehdr = undefined, | 5134 | .ehdr = undefined, |
| ... | @@ -7007,7 +7021,9 @@ fn flushMovedNodeRelocs( | ... | @@ -7007,7 +7021,9 @@ fn flushMovedNodeRelocs( |
| 7007 | } | 7021 | } |
| 7008 | | 7022 | |
| 7009 | fn identClass(elf: *const Elf) std.elf.CLASS { | 7023 | fn identClass(elf: *const Elf) std.elf.CLASS { |
| 7010 | return @fromBackingInt(elf.ni.elf.sliceConst(&elf.mf)[std.elf.EI.CLASS]); | 7024 | // We used to get this value from `elf.ni.ehdr`, but it turns out this function is called often |
| | 7025 | // enough to see measurable performance gains from memoizing its result on `Elf`. |
| | 7026 | return elf.ehdr_fields.class; |
| 7011 | } | 7027 | } |
| 7012 | | 7028 | |
| 7013 | /// Like `std.elf.ET`, but only includes the ELF machine architectures we support, so that we can | 7029 | /// Like `std.elf.ET`, but only includes the ELF machine architectures we support, so that we can |
| ... | @@ -7039,24 +7055,14 @@ const EhdrType = enum(u16) { | ... | @@ -7039,24 +7055,14 @@ const EhdrType = enum(u16) { |
| 7039 | } | 7055 | } |
| 7040 | }; | 7056 | }; |
| 7041 | fn ehdrMachine(elf: *const Elf) EhdrMachine { | 7057 | fn ehdrMachine(elf: *const Elf) EhdrMachine { |
| 7042 | const ehdr_slice = elf.ni.ehdr.sliceConst(&elf.mf); | 7058 | // We used to get this value from `elf.ni.ehdr`, but it turns out this function is called often |
| 7043 | switch (elf.identClass()) { | 7059 | // enough to see measurable performance gains from memoizing its result on `Elf`. |
| 7044 | .NONE, _ => unreachable, | 7060 | return elf.ehdr_fields.machine; |
| 7045 | inline else => |class| { | | |
| 7046 | const ehdr: *const class.ElfN().Ehdr = @ptrCast(@alignCast(ehdr_slice)); | | |
| 7047 | return @bitCast(elf.targetLoad(&ehdr.machine)); | | |
| 7048 | }, | | |
| 7049 | } | | |
| 7050 | } | 7061 | } |
| 7051 | fn ehdrType(elf: *const Elf) EhdrType { | 7062 | fn ehdrType(elf: *const Elf) EhdrType { |
| 7052 | const ehdr_slice = elf.ni.ehdr.sliceConst(&elf.mf); | 7063 | // We used to get this value from `elf.ni.ehdr`, but it turns out this function is called often |
| 7053 | switch (elf.identClass()) { | 7064 | // enough to see measurable performance gains from memoizing its result on `Elf`. |
| 7054 | .NONE, _ => unreachable, | 7065 | return elf.ehdr_fields.type; |
| 7055 | inline else => |class| { | | |
| 7056 | const ehdr: *const class.ElfN().Ehdr = @ptrCast(@alignCast(ehdr_slice)); | | |
| 7057 | return @bitCast(elf.targetLoad(&ehdr.type)); | | |
| 7058 | }, | | |
| 7059 | } | | |
| 7060 | } | 7066 | } |
| 7061 | | 7067 | |
| 7062 | fn baseLoadableSegmentVAddr(elf: *const Elf) u64 { | 7068 | fn baseLoadableSegmentVAddr(elf: *const Elf) u64 { |
| ... | @@ -7279,11 +7285,17 @@ const SymPtr = union(std.elf.CLASS) { | ... | @@ -7279,11 +7285,17 @@ const SymPtr = union(std.elf.CLASS) { |
| 7279 | @"32": *std.elf.Elf32.Sym, | 7285 | @"32": *std.elf.Elf32.Sym, |
| 7280 | @"64": *std.elf.Elf64.Sym, | 7286 | @"64": *std.elf.Elf64.Sym, |
| 7281 | }; | 7287 | }; |
| 7282 | fn symPtr(elf: *Elf, index: Symbol.Index) SymPtr { | 7288 | /// This function is marked as `inline` because performance measurements (specifically, using this |
| | 7289 | /// linker to link the Zig compiler against a static build of LLVM 23) show a measurable performance |
| | 7290 | /// improvement from doing so---it seems that this is one of the hottest functions in this linker! |
| | 7291 | inline fn symPtr(elf: *Elf, index: Symbol.Index) SymPtr { |
| 7283 | const raw_slice = Section.Index.symtab.node(elf).slice(&elf.mf); | 7292 | const raw_slice = Section.Index.symtab.node(elf).slice(&elf.mf); |
| 7284 | switch (elf.shdrPtr(.symtab)) { | 7293 | // Avoid calling `shdrPtr` here: this function is extremely hot, so having to locate the shdr |
| 7285 | inline else => |shdr, class| { | 7294 | // actually noticably affects performance! |
| 7286 | const size = elf.targetLoad(&shdr.size); | 7295 | switch (elf.identClass()) { |
| | 7296 | .NONE, _ => unreachable, |
| | 7297 | inline else => |class| { |
| | 7298 | const size = elf.symtab.items.len * @sizeOf(class.ElfN().Sym); |
| 7287 | const slice: []class.ElfN().Sym = @ptrCast(@alignCast(raw_slice[0..@intCast(size)])); | 7299 | const slice: []class.ElfN().Sym = @ptrCast(@alignCast(raw_slice[0..@intCast(size)])); |
| 7288 | return @unionInit(SymPtr, @tagName(class), &slice[@backingInt(index)]); | 7300 | return @unionInit(SymPtr, @tagName(class), &slice[@backingInt(index)]); |
| 7289 | }, | 7301 | }, |