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:
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);
}