kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

commit fe54567af671bb0d6c2a0d5e31ab35c47dd67e18
parent 9bd45ea57728ff2c56b443b35beb4f1db8df27f2
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Sat, 13 Jun 2026 11:36:03 -0700

perf(obj): single-pass symtab counts + direct strtab write at objwrite (C4)

(cherry picked from commit 30b390acbcf31ecc51aa39f8b5d4f342fd529f0a)

Diffstat:
Msrc/obj/elf/emit.c | 20++++++--------------
Msrc/obj/macho/emit.c | 61+++++++++++++------------------------------------------------
2 files changed, 19 insertions(+), 62 deletions(-)

diff --git a/src/obj/elf/emit.c b/src/obj/elf/emit.c @@ -312,7 +312,11 @@ void emit_elf(Compiler* c, ObjBuilder* ob, Writer* w) { /* Map obj symbol id -> elf symbol index. */ u32* sym_to_elf = arena_zarray(c->scratch, u32, nobjsym + 2); - /* Two passes over obj symbols: locals, then globals/weak. */ + /* Two passes over obj symbols: locals, then globals/weak. + * sh_info on .symtab is the index of the first non-local symbol; + * since pass 0 emits exactly the LOCAL non-removed symbols, count + * them inline (seeded with 1 for STN_UNDEF) instead of re-walking. */ + u32 nlocals = 1; for (int pass = 0; pass < 2; ++pass) { ObjSymIter* it = obj_symiter_new(ob); ObjSymEntry e; @@ -332,24 +336,12 @@ void emit_elf(Compiler* c, ObjBuilder* ob, Writer* w) { WRITE_SYM(nsyms, nameoff, info, other, shndx, value, s->size); sym_to_elf[e.id] = nsyms; nsyms++; + if (pass == 0) ++nlocals; } obj_symiter_free(it); } #undef WRITE_SYM - /* sh_info on .symtab is the index of the first non-local symbol. - * Locals = 1 (STN_UNDEF) + count of input-side LOCAL obj symbols. */ - u32 nlocals = 1; - { - ObjSymIter* it = obj_symiter_new(ob); - ObjSymEntry e; - while (obj_symiter_next(it, &e)) { - if (e.sym->removed) continue; - if (e.sym->bind == SB_LOCAL) ++nlocals; - } - obj_symiter_free(it); - } - /* Append .symtab + .strtab + .shstrtab planning records. * sh_link/sh_info for .symtab and .rela.* are filled in once we know * each section's elf index. */ diff --git a/src/obj/macho/emit.c b/src/obj/macho/emit.c @@ -391,7 +391,10 @@ void emit_macho(Compiler* c, ObjBuilder* ob, Writer* w) { } /* Emit in three passes so n_type/sect ordering matches LC_DYSYMTAB - * (locals, then extdefs, then undefs). */ + * (locals, then extdefs, then undefs). The pass index IS the on-disk + * class, so the per-pass emit counts ARE the LC_DYSYMTAB partition + * sizes — count them inline instead of re-walking the symbols. */ + u32 nlocals = 0, nextdefs = 0, nundefs = 0; for (int pass = 0; pass < 3; ++pass) { ObjSymIter* it = obj_symiter_new(ob); ObjSymEntry e; @@ -482,48 +485,12 @@ void emit_macho(Compiler* c, ObjBuilder* ob, Writer* w) { sym_obj_to_macho[e.id] = nmsyms + 1; /* 1-based index, 0 = none. */ nmsyms++; - } - obj_symiter_free(it); - } - - u32 nlocals = 0, nextdefs = 0, nundefs = 0; - for (u32 i = 0; i < nmsyms; ++i) { - u8 t = msyms[i].n_type; - u8 ext = (t & N_EXT) != 0; - u8 typ = (u8)(t & N_TYPE); - if (typ == N_UNDF && ext) { - /* Could be undef or common — common has nonzero n_value. */ - if (msyms[i].n_value != 0) - ++nextdefs; /* common is conventionally extdef-shaped */ - else - ++nundefs; - } else if (ext) { - ++nextdefs; - } else { - ++nlocals; - } - } - /* Re-derive without the common fudge by counting partition pass: we - * already wrote them in (locals,extdefs,undefs) order, so the prefix - * counts are just the per-pass counts. Mirror the spurious-UNDEF - * prune from the emit loop above so the LC_DYSYMTAB index counts - * line up with the symbols we actually wrote. */ - nlocals = 0; - nextdefs = 0; - nundefs = 0; - { - ObjSymIter* it = obj_symiter_new(ob); - ObjSymEntry e; - while (obj_symiter_next(it, &e)) { - const ObjSym* s = e.sym; - if (s->removed) continue; - int undef = sym_is_undef(s); - if (undef) - ++nundefs; - else if (sym_is_extdef(s)) + if (pass == 0) + ++nlocals; + else if (pass == 1) ++nextdefs; else - ++nlocals; + ++nundefs; } obj_symiter_free(it); } @@ -849,12 +816,10 @@ void emit_macho(Compiler* c, ObjBuilder* ob, Writer* w) { kit_writer_write(w, entry, MACHO_NLIST64_SIZE); } - /* strtab */ - { - u8* flat = (u8*)arena_alloc(c->scratch, strtab_size ? strtab_size : 1, 1); - if (strtab_size) memcpy(flat, obj_strtab_data(&strtab), strtab_size); - kit_writer_seek(w, stroff); - kit_writer_write(w, flat, strtab_size); - } + /* strtab — kit_writer_write consumes synchronously and nothing + * appends to the strtab after this point, so write its contiguous + * buffer directly (no flatten copy) and free it afterwards. */ + kit_writer_seek(w, stroff); + if (strtab_size) kit_writer_write(w, obj_strtab_data(&strtab), strtab_size); obj_strtab_fini(&strtab); }