kit

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

commit 42ce19068af007468976b1b1cfcbfbde2594abc5
parent 81614f4229f8b1ba480149409457610863a5c113
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Sat,  9 May 2026 15:14:48 -0700

core: extract bytes/util/vec/hashmap and zalloc/buf-walk helpers

Consolidate duplicated low-level patterns spread across obj/, link/,
arch/, emu/, and pp/ into reusable core headers:

- core/util.h: ALIGN_UP/DOWN, MIN/MAX/CLAMP, BIT/MASK, ARRAY_LEN,
  CONTAINER_OF, IS_POW2, UNUSED. Replaces five near-identical
  align_up_u64 definitions and the ad-hoc align_up_size in arena.c.

- core/bytes.h: wr/rd_u{16,32,64}_le and wr/rd_u32_be. Replaces three
  redefinitions of wr_u32_le (link_reloc.c, link_layout.c, elf.h's
  Writer wrappers) and open-coded byte sequences in arch/mc.c and
  api/ar.c.

- core/vec.h + vec.c: VEC_GROW(heap, ptr, cap, want) — single doubling-
  growth primitive replacing ~10 hand-written *_grow functions across
  obj.c (sections/symbols/relocs/groups), link.c (inputs/archives),
  link_layout.c (syms/relocs/GcQueue/GOT slots), and link_elf.c
  (StrBuilder).

- core/hashmap.h: HASHMAP_DEFINE(NAME, K, V, HASH_FN) macro template
  emitting a typed open-addressed table with linear probing, 75% load
  factor, and tombstoneless delete. Built-in hash_u32 and hash_u64
  mixers. Refactors three hand-written tables: SymHash (link, kept
  symhash_* wrappers), MacroMap (pp, with #undef deletion), and
  EmuCodeCache (emu/runtime, embedded as PcMap).

- arena_zalloc + arena_zarray/arena_znew: collapse arena_array+memset
  patterns in elf_emit.c, elf_read.c, and pp.c.

- buf.c: collapse buf_patch and buf_read into a single buf_walk helper.

Net: 21 files, -662/+618. All test-elf, test-link, test-pp, test-pp-err,
and test-ar suites pass.

Diffstat:
Msrc/api/ar.c | 6++----
Msrc/arch/mc.c | 21++++++---------------
Msrc/core/arena.c | 10++++++----
Msrc/core/arena.h | 7+++++--
Msrc/core/buf.c | 49+++++++++++++++++++++----------------------------
Asrc/core/bytes.h | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/core/hashmap.h | 201+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/core/util.h | 36++++++++++++++++++++++++++++++++++++
Asrc/core/vec.c | 24++++++++++++++++++++++++
Asrc/core/vec.h | 27+++++++++++++++++++++++++++
Msrc/emu/runtime.c | 86++++++++++++++-----------------------------------------------------------------
Msrc/link/link.c | 142+++++--------------------------------------------------------------------------
Msrc/link/link_elf.c | 20++++++--------------
Msrc/link/link_internal.h | 46++++++++++++++++++++--------------------------
Msrc/link/link_jit.c | 5++---
Msrc/link/link_layout.c | 84+++++++++++++++++++------------------------------------------------------------
Msrc/link/link_reloc.c | 28++--------------------------
Msrc/obj/elf.h | 39+++++++++++++--------------------------
Msrc/obj/elf_emit.c | 27++++++---------------------
Msrc/obj/elf_read.c | 6++----
Msrc/obj/obj.c | 71+++++------------------------------------------------------------------
Msrc/pp/pp.c | 119+++++++++++--------------------------------------------------------------------
22 files changed, 507 insertions(+), 610 deletions(-)

diff --git a/src/api/ar.c b/src/api/ar.c @@ -11,6 +11,7 @@ #include <cfree.h> +#include "core/bytes.h" #include "core/core.h" /* ============================================================ @@ -39,10 +40,7 @@ static void wh_ar_num(char* dst, int width, u64 v) static void wh_be32(Writer* w, u32 v) { u8 b[4]; - b[0] = (u8)((v >> 24) & 0xff); - b[1] = (u8)((v >> 16) & 0xff); - b[2] = (u8)((v >> 8) & 0xff); - b[3] = (u8)( v & 0xff); + wr_u32_be(b, v); wh_bytes(w, b, 4); } diff --git a/src/arch/mc.c b/src/arch/mc.c @@ -29,6 +29,7 @@ #include "arch/arch.h" #include "core/arena.h" +#include "core/bytes.h" #include "obj/obj.h" #include <string.h> @@ -82,12 +83,8 @@ static void apply_fixup(MCImpl* mc, const MCFixup* fx, u32 target_offset) switch (fx->kind) { case R_PC32: { - i32 v = (i32)disp; u8 bytes[4]; - bytes[0] = (u8)(v & 0xff); - bytes[1] = (u8)((v>>8) & 0xff); - bytes[2] = (u8)((v>>16) & 0xff); - bytes[3] = (u8)((v>>24) & 0xff); + wr_u32_le(bytes, (u32)(i32)disp); obj_patch(mc->base.obj, fx->sec_id, fx->offset, bytes, 4); break; } @@ -110,12 +107,9 @@ static void apply_fixup(MCImpl* mc, const MCFixup* fx, u32 target_offset) const Section* s = obj_section_get(mc->base.obj, fx->sec_id); if (!s) break; buf_read(&s->bytes, fx->offset, cur, 4); - u32 word = (u32)cur[0] | ((u32)cur[1] << 8) | ((u32)cur[2] << 16) | ((u32)cur[3] << 24); + u32 word = rd_u32_le(cur); word = (word & ~0x03ffffffu) | imm26; - cur[0] = (u8)(word & 0xff); - cur[1] = (u8)((word >> 8) & 0xff); - cur[2] = (u8)((word >> 16)& 0xff); - cur[3] = (u8)((word >> 24)& 0xff); + wr_u32_le(cur, word); obj_patch(mc->base.obj, fx->sec_id, fx->offset, cur, 4); break; } @@ -127,12 +121,9 @@ static void apply_fixup(MCImpl* mc, const MCFixup* fx, u32 target_offset) if (!s) break; u8 cur[4]; buf_read(&s->bytes, fx->offset, cur, 4); - u32 word = (u32)cur[0] | ((u32)cur[1] << 8) | ((u32)cur[2] << 16) | ((u32)cur[3] << 24); + u32 word = rd_u32_le(cur); word = (word & ~(0x7ffffu << 5)) | (imm19 << 5); - cur[0] = (u8)(word & 0xff); - cur[1] = (u8)((word >> 8) & 0xff); - cur[2] = (u8)((word >> 16)& 0xff); - cur[3] = (u8)((word >> 24)& 0xff); + wr_u32_le(cur, word); obj_patch(mc->base.obj, fx->sec_id, fx->offset, cur, 4); break; } diff --git a/src/core/arena.c b/src/core/arena.c @@ -4,6 +4,7 @@ * O(1)). Oversize allocations get their own dedicated block. */ #include "core/arena.h" +#include "core/util.h" #include <string.h> @@ -64,10 +65,11 @@ void arena_reset(Arena* a) } } -static size_t align_up_size(size_t v, size_t align) +void* arena_zalloc(Arena* a, size_t size, size_t align) { - size_t mask = align - 1; - return (v + mask) & ~mask; + void* p = arena_alloc(a, size, align); + if (p) memset(p, 0, size); + return p; } void* arena_alloc(Arena* a, size_t size, size_t align) @@ -85,7 +87,7 @@ void* arena_alloc(Arena* a, size_t size, size_t align) } } /* New block. */ - need = align_up_size(size, align); + need = ALIGN_UP(size, align); if (need < a->block_size) need = a->block_size; { ArenaBlock* b = block_new(a->heap, need); diff --git a/src/core/arena.h b/src/core/arena.h @@ -18,9 +18,12 @@ void arena_init(Arena*, Heap*, size_t block_size); void arena_fini(Arena*); void arena_reset(Arena*); void* arena_alloc(Arena*, size_t size, size_t align); +void* arena_zalloc(Arena*, size_t size, size_t align); /* zeroed; NULL on OOM */ char* arena_strdup(Arena*, const char* s, size_t len); -#define arena_new(a, T) ((T*)arena_alloc((a), sizeof(T), _Alignof(T))) -#define arena_array(a, T, n) ((T*)arena_alloc((a), sizeof(T) * (size_t)(n), _Alignof(T))) +#define arena_new(a, T) ((T*)arena_alloc((a), sizeof(T), _Alignof(T))) +#define arena_znew(a, T) ((T*)arena_zalloc((a), sizeof(T), _Alignof(T))) +#define arena_array(a, T, n) ((T*)arena_alloc((a), sizeof(T) * (size_t)(n), _Alignof(T))) +#define arena_zarray(a, T, n) ((T*)arena_zalloc((a), sizeof(T) * (size_t)(n), _Alignof(T))) #endif diff --git a/src/core/buf.c b/src/core/buf.c @@ -78,50 +78,43 @@ u8* buf_reserve(Buf* b, size_t n) u32 buf_pos(const Buf* b) { return b->total; } -void buf_patch(Buf* b, u32 ofs, const void* data, size_t n) +/* Walk the chunk list intersecting [ofs, ofs+n), invoking copy(...) for + * each contiguous slice. Patch and read share this; the only difference + * is the direction of the memcpy. + * + * `direction`: 0 = copy from `external` into chunk; 1 = copy from chunk + * into `external`. Inlined at both call sites by clang. */ +static inline void buf_walk(BufChunk* head, u32 ofs, void* external, + size_t n, int from_chunk) { - BufChunk* c = b->head; - u32 chunk_start = 0; - const u8* p = (const u8*)data; + BufChunk* c = head; + u32 chunk_start = 0; + u8* ext = (u8*)external; while (c && n) { u32 chunk_end = chunk_start + c->used; if (ofs < chunk_end) { u32 within = ofs - chunk_start; u32 avail = c->used - within; u32 take = (u32)(n < avail ? n : avail); - memcpy(c->data + within, p, take); - p += take; + if (from_chunk) memcpy(ext, c->data + within, take); + else memcpy(c->data + within, ext, take); + ext += take; n -= take; ofs += take; } chunk_start = chunk_end; c = c->next; } - /* Patches must lie inside the written range; if n != 0 here, the - * caller exceeded buf_pos and this is a contract violation. Silent - * drop matches buf_write's allocation-failure policy. */ + /* Caller's range must lie inside the written range; if n != 0 here, + * the caller exceeded buf_pos and this is a contract violation. + * Silent drop matches buf_write's allocation-failure policy. */ } +void buf_patch(Buf* b, u32 ofs, const void* data, size_t n) +{ buf_walk(b->head, ofs, (void*)data, n, /*from_chunk=*/0); } + void buf_read(const Buf* b, u32 ofs, void* dst, size_t n) -{ - BufChunk* c = b->head; - u32 chunk_start = 0; - u8* p = (u8*)dst; - while (c && n) { - u32 chunk_end = chunk_start + c->used; - if (ofs < chunk_end) { - u32 within = ofs - chunk_start; - u32 avail = c->used - within; - u32 take = (u32)(n < avail ? n : avail); - memcpy(p, c->data + within, take); - p += take; - n -= take; - ofs += take; - } - chunk_start = chunk_end; - c = c->next; - } -} +{ buf_walk(b->head, ofs, dst, n, /*from_chunk=*/1); } void buf_flatten(const Buf* b, u8* dst) { diff --git a/src/core/bytes.h b/src/core/bytes.h @@ -0,0 +1,63 @@ +#ifndef CFREE_BYTES_H +#define CFREE_BYTES_H + +/* Byte read/write helpers — canonical for encoding multi-byte integers + * into byte buffers. ELF, AArch64 instruction emission, relocation + * patches all use little-endian. The only big-endian site is ar archive + * symbol-index headers (POSIX ar(5) §"Archive symbol table"). */ + +#include "core/core.h" + +static inline u16 rd_u16_le(const u8* p) +{ + return (u16)((u32)p[0] | ((u32)p[1] << 8)); +} + +static inline u32 rd_u32_le(const u8* p) +{ + return (u32)p[0] | ((u32)p[1] << 8) | + ((u32)p[2] << 16) | ((u32)p[3] << 24); +} + +static inline u64 rd_u64_le(const u8* p) +{ + return (u64)rd_u32_le(p) | ((u64)rd_u32_le(p + 4) << 32); +} + +static inline void wr_u16_le(u8* p, u16 v) +{ + p[0] = (u8)(v); + p[1] = (u8)(v >> 8); +} + +static inline void wr_u32_le(u8* p, u32 v) +{ + p[0] = (u8)(v); + p[1] = (u8)(v >> 8); + p[2] = (u8)(v >> 16); + p[3] = (u8)(v >> 24); +} + +static inline void wr_u64_le(u8* p, u64 v) +{ + wr_u32_le(p, (u32)(v)); + wr_u32_le(p + 4, (u32)(v >> 32)); +} + +/* Big-endian — ar symbol index. */ + +static inline u32 rd_u32_be(const u8* p) +{ + return ((u32)p[0] << 24) | ((u32)p[1] << 16) | + ((u32)p[2] << 8) | (u32)p[3]; +} + +static inline void wr_u32_be(u8* p, u32 v) +{ + p[0] = (u8)(v >> 24); + p[1] = (u8)(v >> 16); + p[2] = (u8)(v >> 8); + p[3] = (u8)(v); +} + +#endif diff --git a/src/core/hashmap.h b/src/core/hashmap.h @@ -0,0 +1,201 @@ +#ifndef CFREE_HASHMAP_H +#define CFREE_HASHMAP_H + +/* Generic open-addressed hashmap as a typed-macro template. + * + * Linear probing; doubling rehash at 75% load. Tombstoneless deletion + * via cluster rehash. Empty-key sentinel is 0 — slots are zero-initialized + * by allocation, and callers must never insert a key whose value compares + * equal to 0. (Sym=0 already means "none" per core.h:42; OBJ_SEC_NONE=0; + * pointer keys avoid 0 by construction.) + * + * HASHMAP_DEFINE(NAME, KT, VT, HASH_FN) + * NAME — struct typedef name for this instance. + * KT — key type (must support `== 0` and `==` against another KT). + * VT — value type (any assignable type). + * HASH_FN — function-like expression mapping KT -> u32. + * + * Emits typedef NAME, NAME##Slot, and these static functions: + * void NAME##_init (NAME*, Heap*) — default initial cap + * void NAME##_init_cap(NAME*, Heap*, u32 cap) — caller picks initial cap + * void NAME##_fini (NAME*) + * VT* NAME##_get (const NAME*, KT) — NULL if absent + * int NAME##_set (NAME*, KT, VT) — 1 inserted, 0 updated + * void NAME##_del (NAME*, KT) — no-op if absent + * + * Equality is `==` on KT. That covers all keys we use (Sym which is u32, + * u64 guest_pc). A string-keyed instance would need a small extension. + * + * Built-in mixers below cover u32 (hash_u32) and u64 (hash_u64) keys. */ + +#include "core/core.h" +#include "core/heap.h" + +#include <string.h> + +/* xorshift mixer suitable for dense u32 keys (interned Sym ids etc.). */ +static inline u32 hash_u32(u32 x) +{ + x += 0x9e3779b9u; + x ^= x >> 16; x *= 0x7feb352du; + x ^= x >> 15; x *= 0x846ca68bu; + x ^= x >> 16; + return x; +} + +/* SplitMix-style mixer for u64 keys (e.g. guest PC). */ +static inline u32 hash_u64(u64 x) +{ + x ^= x >> 33; x *= 0xff51afd7ed558ccdULL; + x ^= x >> 33; x *= 0xc4ceb9fe1a85ec53ULL; + x ^= x >> 33; + return (u32)x; +} + +#define HASHMAP_LOAD_NUM 3u +#define HASHMAP_LOAD_DEN 4u +#define HASHMAP_INIT_CAP 16u + +#define HASHMAP_DEFINE(NAME, KT, VT, HASH_FN) \ + typedef struct NAME##Slot { KT k; VT v; } NAME##Slot; \ + typedef struct NAME { \ + Heap* heap; \ + NAME##Slot* slots; \ + u32 cap; \ + u32 used; \ + } NAME; \ + \ + __attribute__((unused)) \ + static void NAME##_resize(NAME* m, u32 new_cap) \ + { \ + NAME##Slot* fresh; \ + u32 i, mask; \ + fresh = (NAME##Slot*)m->heap->alloc( \ + m->heap, sizeof(*fresh) * new_cap, _Alignof(NAME##Slot)); \ + if (!fresh) return; \ + memset(fresh, 0, sizeof(*fresh) * new_cap); \ + mask = new_cap - 1u; \ + for (i = 0; i < m->cap; ++i) { \ + KT k = m->slots[i].k; \ + u32 j; \ + if (!(k)) continue; \ + j = HASH_FN(k) & mask; \ + while (fresh[j].k) j = (j + 1u) & mask; \ + fresh[j] = m->slots[i]; \ + } \ + if (m->slots) m->heap->free( \ + m->heap, m->slots, sizeof(*m->slots) * m->cap); \ + m->slots = fresh; m->cap = new_cap; \ + } \ + \ + __attribute__((unused)) \ + static inline void NAME##_init_cap(NAME* m, Heap* h, u32 cap) \ + { \ + m->heap = h; m->slots = NULL; m->cap = 0; m->used = 0; \ + if (cap) NAME##_resize(m, cap); \ + } \ + \ + __attribute__((unused)) \ + static inline void NAME##_init(NAME* m, Heap* h) \ + { NAME##_init_cap(m, h, HASHMAP_INIT_CAP); } \ + \ + __attribute__((unused)) \ + static inline void NAME##_fini(NAME* m) \ + { \ + if (m->slots) m->heap->free( \ + m->heap, m->slots, sizeof(*m->slots) * m->cap); \ + m->slots = NULL; m->cap = m->used = 0; \ + } \ + \ + __attribute__((unused)) \ + static inline VT* NAME##_get(const NAME* m, KT k) \ + { \ + u32 mask, j; \ + if (m->cap == 0 || !(k)) return NULL; \ + mask = m->cap - 1u; \ + j = HASH_FN(k) & mask; \ + while (m->slots[j].k) { \ + if (m->slots[j].k == (k)) return &m->slots[j].v; \ + j = (j + 1u) & mask; \ + } \ + return NULL; \ + } \ + \ + __attribute__((unused)) \ + static inline int NAME##_set(NAME* m, KT k, VT v) \ + { \ + u32 mask, j; \ + if (m->cap == 0 || \ + m->used * HASHMAP_LOAD_DEN >= m->cap * HASHMAP_LOAD_NUM) \ + NAME##_resize(m, m->cap ? m->cap * 2u : HASHMAP_INIT_CAP); \ + mask = m->cap - 1u; \ + j = HASH_FN(k) & mask; \ + while (m->slots[j].k) { \ + if (m->slots[j].k == (k)) { m->slots[j].v = (v); return 0; } \ + j = (j + 1u) & mask; \ + } \ + m->slots[j].k = (k); \ + m->slots[j].v = (v); \ + m->used++; \ + return 1; \ + } \ + \ + /* Insert if absent. Returns 1 if newly inserted; 0 if k was present \ + * (in that case writes the existing value to *existing_out when \ + * existing_out is non-NULL). */ \ + __attribute__((unused)) \ + static inline int NAME##_try_insert(NAME* m, KT k, VT v, VT* existing_out) \ + { \ + u32 mask, j; \ + if (m->cap == 0 || \ + m->used * HASHMAP_LOAD_DEN >= m->cap * HASHMAP_LOAD_NUM) \ + NAME##_resize(m, m->cap ? m->cap * 2u : HASHMAP_INIT_CAP); \ + mask = m->cap - 1u; \ + j = HASH_FN(k) & mask; \ + while (m->slots[j].k) { \ + if (m->slots[j].k == (k)) { \ + if (existing_out) *existing_out = m->slots[j].v; \ + return 0; \ + } \ + j = (j + 1u) & mask; \ + } \ + m->slots[j].k = (k); \ + m->slots[j].v = (v); \ + m->used++; \ + return 1; \ + } \ + \ + __attribute__((unused)) \ + static inline void NAME##_del(NAME* m, KT k) \ + { \ + u32 mask, j; \ + if (m->cap == 0 || !(k)) return; \ + mask = m->cap - 1u; \ + j = HASH_FN(k) & mask; \ + while (m->slots[j].k) { \ + if (m->slots[j].k == (k)) { \ + u32 i = (j + 1u) & mask; \ + m->slots[j].k = 0; \ + m->used--; \ + while (m->slots[i].k) { \ + KT rk = m->slots[i].k; \ + VT rv = m->slots[i].v; \ + u32 nh; \ + m->slots[i].k = 0; \ + m->used--; \ + nh = HASH_FN(rk) & mask; \ + while (m->slots[nh].k) nh = (nh + 1u) & mask; \ + m->slots[nh].k = rk; \ + m->slots[nh].v = rv; \ + m->used++; \ + i = (i + 1u) & mask; \ + } \ + return; \ + } \ + j = (j + 1u) & mask; \ + } \ + } \ + /* trailing struct decl swallows the macro-call's semicolon */ \ + struct NAME + +#endif diff --git a/src/core/util.h b/src/core/util.h @@ -0,0 +1,36 @@ +#ifndef CFREE_UTIL_H +#define CFREE_UTIL_H + +/* Small generic helpers — alignment, min/max, array length, container_of. + * + * Header-only. Intended to be includable from any TU; depends only on + * stddef.h (via core.h) for offsetof. + * + * Naming: short uppercase macros without prefix. The codebase is + * freestanding (-ffreestanding) and core.h pulls in only stddef/stdint/ + * setjmp/stdarg, so MIN/MAX collisions with system headers don't occur. */ + +#include "core/core.h" + +#include <stddef.h> + +#define ARRAY_LEN(a) (sizeof(a) / sizeof((a)[0])) + +#define BIT(n) (1u << (n)) + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define CLAMP(x, lo, hi) ((x) < (lo) ? (lo) : (x) > (hi) ? (hi) : (x)) + +#define IS_POW2(x) ((x) != 0 && (((x) & ((x) - 1)) == 0)) + +/* ALIGN_UP(v, a): round v up to a multiple of a. a must be a power of two. + * Generic over u32/u64/size_t — operand promotion picks the wider type. */ +#define ALIGN_UP(v, a) (((v) + ((a) - 1)) & ~((a) - 1)) +#define ALIGN_DOWN(v, a) ((v) & ~((a) - 1)) + +#define CONTAINER_OF(p, T, m) ((T*)((char*)(p) - offsetof(T, m))) + +#define UNUSED(x) ((void)(x)) + +#endif diff --git a/src/core/vec.c b/src/core/vec.c @@ -0,0 +1,24 @@ +/* See core/vec.h. Doubling growth, heap-backed. */ + +#include "core/vec.h" + +#define VEC_INIT_CAP 8u + +int vec_grow_(Heap* h, void** ptr, u32* cap, u32 want, + size_t elem_size, size_t elem_align) +{ + void* p; + u32 old_cap = *cap; + u32 new_cap; + if (want <= old_cap) return 0; + new_cap = old_cap ? old_cap : VEC_INIT_CAP; + while (new_cap < want) new_cap *= 2; + p = h->realloc(h, *ptr, + elem_size * old_cap, + elem_size * new_cap, + elem_align); + if (!p) return 1; + *ptr = p; + *cap = new_cap; + return 0; +} diff --git a/src/core/vec.h b/src/core/vec.h @@ -0,0 +1,27 @@ +#ifndef CFREE_VEC_H +#define CFREE_VEC_H + +/* Generic doubling growth for embedded {T* ptr; u32 cap} pairs. + * + * Replaces the per-type *_grow functions previously hand-written in + * obj.c, link.c, link_layout.c. Heap-backed only; arena-backed callers + * (cg/aarch64.c AASlot) keep their own logic since the arena interface + * cannot reallocate. */ + +#include "core/core.h" +#include "core/heap.h" + +/* Ensures *cap >= want. On growth, calls heap->realloc and updates *ptr + * and *cap. Returns 0 on success (including the no-op case when *cap is + * already large enough), or 1 on allocation failure. Initial cap when + * growing from 0 is VEC_INIT_CAP (8). */ +int vec_grow_(Heap* h, void** ptr, u32* cap, u32 want, + size_t elem_size, size_t elem_align); + +/* VEC_GROW(heap, ptr_lvalue, cap_lvalue, want) + * — derives element size/alignment from *ptr's type. */ +#define VEC_GROW(h, ptr, cap, want) \ + vec_grow_((h), (void**)&(ptr), &(cap), (u32)(want), \ + sizeof(*(ptr)), __alignof__(*(ptr))) + +#endif diff --git a/src/emu/runtime.c b/src/emu/runtime.c @@ -11,6 +11,7 @@ #include "emu/emu.h" #include "core/heap.h" +#include "core/util.h" #include <cfree.h> @@ -43,11 +44,6 @@ static u64 page_size_bytes(const CfreeExecMem* m) return m->page_size ? (u64)m->page_size : 0x4000u; } -static u64 align_up_u64(u64 v, u64 a) -{ - return (v + (a - 1u)) & ~(a - 1u); -} - struct EmuCodeRegion { Compiler* c; CfreeExecMemRegion region; /* dual-aliased on hosts that support it */ @@ -66,7 +62,7 @@ EmuCodeRegion* emu_code_region_new(Compiler* c, size_t reserve_size) if (!c) return NULL; h = (Heap*)c->env->heap; mem = require_execmem(c); - aligned = (size_t)align_up_u64((u64)reserve_size, page_size_bytes(mem)); + aligned = (size_t)ALIGN_UP((u64)reserve_size, page_size_bytes(mem)); /* Reserve as a code region. The host returns dual-mapped memory * (writable alias / runtime alias) so the linker can write through @@ -117,7 +113,7 @@ void emu_code_region_commit_rx_to(EmuCodeRegion* r, uintptr_t end) if (!r) return; mem = require_execmem(r->c); base = (uintptr_t)r->region.runtime; - page_end = (uintptr_t)align_up_u64((u64)end, page_size_bytes(mem)); + page_end = (uintptr_t)ALIGN_UP((u64)end, page_size_bytes(mem)); /* Monotonic: never lower the high-water; chaining patches * already-committed code and depends on it staying RX. */ if (page_end <= r->rx_end) return; @@ -145,49 +141,15 @@ void emu_code_region_commit_rx_to(EmuCodeRegion* r, uintptr_t end) * Open-addressed linear-probe hash on the guest PC. Capacity grows * by doubling; v1 never evicts. */ -typedef struct EmuCacheEntry { - u64 guest_pc; /* 0 means empty slot */ - void* host_entry; -} EmuCacheEntry; +#include "core/hashmap.h" + +HASHMAP_DEFINE(PcMap, u64, void*, hash_u64); struct EmuCodeCache { - Compiler* c; - EmuCacheEntry* slots; - u32 cap; - u32 used; + Compiler* c; + PcMap map; }; -static u64 mix_pc(u64 x) -{ - x ^= x >> 33; x *= 0xff51afd7ed558ccdull; - x ^= x >> 33; x *= 0xc4ceb9fe1a85ec53ull; - x ^= x >> 33; - return x; -} - -static void cache_resize(EmuCodeCache* c, u32 new_cap) -{ - Heap* h = (Heap*)c->c->env->heap; - EmuCacheEntry* fresh; - u32 i, mask; - fresh = (EmuCacheEntry*)h->alloc(h, sizeof(*fresh) * new_cap, - _Alignof(EmuCacheEntry)); - if (!fresh) return; - memset(fresh, 0, sizeof(*fresh) * new_cap); - mask = new_cap - 1u; - for (i = 0; i < c->cap; ++i) { - u64 pc = c->slots[i].guest_pc; - u32 j; - if (pc == 0) continue; - j = (u32)mix_pc(pc) & mask; - while (fresh[j].guest_pc != 0) j = (j + 1u) & mask; - fresh[j] = c->slots[i]; - } - if (c->slots) h->free(h, c->slots, sizeof(*c->slots) * c->cap); - c->slots = fresh; - c->cap = new_cap; -} - EmuCodeCache* emu_cache_new(Compiler* c) { Heap* h; @@ -198,7 +160,7 @@ EmuCodeCache* emu_cache_new(Compiler* c) if (!k) return NULL; memset(k, 0, sizeof(*k)); k->c = c; - cache_resize(k, 64u); + PcMap_init_cap(&k->map, h, 64u); return k; } @@ -207,40 +169,22 @@ void emu_cache_free(EmuCodeCache* c) Heap* h; if (!c) return; h = (Heap*)c->c->env->heap; - if (c->slots) h->free(h, c->slots, sizeof(*c->slots) * c->cap); + PcMap_fini(&c->map); h->free(h, c, sizeof(*c)); } void emu_cache_insert(EmuCodeCache* c, u64 guest_pc, void* host_entry) { - u32 mask, j; if (!c || guest_pc == 0) return; - if (c->used * 4u >= c->cap * 3u) cache_resize(c, c->cap * 2u); - mask = c->cap - 1u; - j = (u32)mix_pc(guest_pc) & mask; - while (c->slots[j].guest_pc != 0) { - if (c->slots[j].guest_pc == guest_pc) { - c->slots[j].host_entry = host_entry; - return; - } - j = (j + 1u) & mask; - } - c->slots[j].guest_pc = guest_pc; - c->slots[j].host_entry = host_entry; - c->used++; + PcMap_set(&c->map, guest_pc, host_entry); } void* emu_cache_lookup(const EmuCodeCache* c, u64 guest_pc) { - u32 mask, j; - if (!c || c->cap == 0 || guest_pc == 0) return NULL; - mask = c->cap - 1u; - j = (u32)mix_pc(guest_pc) & mask; - while (c->slots[j].guest_pc != 0) { - if (c->slots[j].guest_pc == guest_pc) return c->slots[j].host_entry; - j = (j + 1u) & mask; - } - return NULL; + void** slot; + if (!c) return NULL; + slot = PcMap_get(&c->map, guest_pc); + return slot ? *slot : NULL; } /* ============================================================ diff --git a/src/link/link.c b/src/link/link.c @@ -15,6 +15,7 @@ #include "core/heap.h" #include "core/pool.h" +#include "core/vec.h" #include <cfree.h> @@ -24,117 +25,12 @@ static SrcLoc no_loc(void) { SrcLoc l = {0,0,0}; return l; } -/* ---- SymHash ---- */ - -#define SYMHASH_INIT_CAP 16u - -static u32 sym_hash_mix(Sym s) -{ - /* xorshift on 32-bit; cheap and good enough for interned ids. */ - u32 x = (u32)s + 0x9e3779b9u; - x ^= x >> 16; x *= 0x7feb352du; - x ^= x >> 15; x *= 0x846ca68bu; - x ^= x >> 16; - return x; -} - -static void symhash_resize(SymHash* h, u32 new_cap) -{ - SymHashEntry* new_slots; - u32 i, mask; - new_slots = (SymHashEntry*)h->heap->alloc( - h->heap, sizeof(*new_slots) * new_cap, _Alignof(SymHashEntry)); - /* Caller is expected to have ruled out OOM at the API boundary; if - * the host heap returns NULL we leak the resize and skip. The next - * insert will retry — symhash is only used inside link_resolve which - * panics on real allocator failure upstream. */ - if (!new_slots) return; - memset(new_slots, 0, sizeof(*new_slots) * new_cap); - mask = new_cap - 1; - for (i = 0; i < h->cap; ++i) { - Sym n = h->slots[i].name; - u32 j; - if (n == 0) continue; - j = sym_hash_mix(n) & mask; - while (new_slots[j].name != 0) j = (j + 1) & mask; - new_slots[j] = h->slots[i]; - } - if (h->slots) h->heap->free(h->heap, h->slots, sizeof(*h->slots) * h->cap); - h->slots = new_slots; - h->cap = new_cap; -} - -void symhash_init(SymHash* h, Heap* heap) -{ - memset(h, 0, sizeof(*h)); - h->heap = heap; -} - -void symhash_fini(SymHash* h) -{ - if (h->slots) h->heap->free(h->heap, h->slots, sizeof(*h->slots) * h->cap); - h->slots = NULL; - h->cap = h->used = 0; -} - -static void symhash_ensure(SymHash* h) -{ - if (h->cap == 0) symhash_resize(h, SYMHASH_INIT_CAP); - /* Grow at 75% load. */ - else if (h->used * 4u >= h->cap * 3u) symhash_resize(h, h->cap * 2u); -} - -int symhash_insert(SymHash* h, Sym name, LinkSymId id, LinkSymId* existing_out) -{ - u32 mask, j; - symhash_ensure(h); - mask = h->cap - 1; - j = sym_hash_mix(name) & mask; - while (h->slots[j].name != 0) { - if (h->slots[j].name == name) { - if (existing_out) *existing_out = h->slots[j].id; - return 0; - } - j = (j + 1) & mask; - } - h->slots[j].name = name; - h->slots[j].id = id; - h->used++; - return 1; -} - -void symhash_set(SymHash* h, Sym name, LinkSymId id) -{ - u32 mask, j; - symhash_ensure(h); - mask = h->cap - 1; - j = sym_hash_mix(name) & mask; - while (h->slots[j].name != 0 && h->slots[j].name != name) - j = (j + 1) & mask; - if (h->slots[j].name == 0) { - h->slots[j].name = name; - h->used++; - } - h->slots[j].id = id; -} - -LinkSymId symhash_get(const SymHash* h, Sym name) -{ - u32 mask, j; - if (h->cap == 0 || name == 0) return LINK_SYM_NONE; - mask = h->cap - 1; - j = sym_hash_mix(name) & mask; - while (h->slots[j].name != 0) { - if (h->slots[j].name == name) return h->slots[j].id; - j = (j + 1) & mask; - } - return LINK_SYM_NONE; -} +/* SymHash is a HASHMAP_DEFINE instance — see link_internal.h. The thin + * symhash_* wrappers there preserve the historic insert-if-absent / by- + * value get API. */ /* ---- Linker lifecycle ---- */ -#define INPUTS_INIT_CAP 8u - static void linker_release(Linker* l) { u32 i, j; @@ -196,18 +92,8 @@ void link_free(Linker* l) static void inputs_grow(Linker* l) { - u32 new_cap; - LinkInput* p; - if (l->ninputs < l->inputs_cap) return; - new_cap = l->inputs_cap ? l->inputs_cap * 2u : INPUTS_INIT_CAP; - p = (LinkInput*)l->heap->realloc( - l->heap, l->inputs, - sizeof(*l->inputs) * l->inputs_cap, - sizeof(*l->inputs) * new_cap, - _Alignof(LinkInput)); - if (!p) compiler_panic(l->c, no_loc(), "link: out of memory growing inputs"); - l->inputs = p; - l->inputs_cap = new_cap; + if (VEC_GROW(l->heap, l->inputs, l->inputs_cap, l->ninputs + 1u)) + compiler_panic(l->c, no_loc(), "link: out of memory growing inputs"); } LinkInputId link_add_obj(Linker* l, ObjBuilder* ob) @@ -261,19 +147,9 @@ LinkInputId link_add_obj_bytes(Linker* l, const char* name, static void archives_grow(Linker* l) { - u32 new_cap; - LinkArchive* p; - if (l->narchives < l->archives_cap) return; - new_cap = l->archives_cap ? l->archives_cap * 2u : 4u; - p = (LinkArchive*)l->heap->realloc( - l->heap, l->archives, - sizeof(*l->archives) * l->archives_cap, - sizeof(*l->archives) * new_cap, - _Alignof(LinkArchive)); - if (!p) compiler_panic(l->c, no_loc(), - "link: out of memory growing archives"); - l->archives = p; - l->archives_cap = new_cap; + if (VEC_GROW(l->heap, l->archives, l->archives_cap, l->narchives + 1u)) + compiler_panic(l->c, no_loc(), + "link: out of memory growing archives"); } LinkInputId link_add_archive_bytes(Linker* l, const char* name, diff --git a/src/link/link_elf.c b/src/link/link_elf.c @@ -47,6 +47,8 @@ #include "obj/elf.h" #include "core/heap.h" #include "core/pool.h" +#include "core/util.h" +#include "core/vec.h" #include <string.h> @@ -111,8 +113,6 @@ typedef struct __attribute__((packed)) Shdr64 { /* ---- byte writer helpers ---- */ -static u64 align_up_u64(u64 v, u64 a) { return (v + (a - 1u)) & ~(a - 1u); } - static void write_bytes(Writer* w, const void* data, size_t n) { w->write(w, data, n); @@ -267,15 +267,7 @@ static void strb_fini(StrBuilder* s) static void strb_grow(StrBuilder* s, u32 need) { - u32 new_cap; - u8* p; - if (need <= s->cap) return; - new_cap = s->cap ? s->cap : 16u; - while (new_cap < need) new_cap *= 2u; - p = (u8*)s->heap->realloc(s->heap, s->data, s->cap, new_cap, 1); - if (!p) return; - s->data = p; - s->cap = new_cap; + (void)VEC_GROW(s->heap, s->data, s->cap, need); } static u32 strb_add(StrBuilder* s, const char* str, u32 slen) @@ -460,7 +452,7 @@ void link_emit_elf_aarch64(LinkImage* img, Writer* w) u32 nphdr_total = 1u + img->nsegments + 1u + has_tls; u64 headers_size = sizeof(Ehdr64) + (u64)nphdr_total * sizeof(Phdr64) + BUILD_ID_NOTE_BYTES; - u64 headers_load = align_up_u64(headers_size, PAGE_SIZE); + u64 headers_load = ALIGN_UP(headers_size, (u64)PAGE_SIZE); /* The build-id note lives inside the headers PT_LOAD at this offset. */ u64 build_id_off = sizeof(Ehdr64) + (u64)nphdr_total * sizeof(Phdr64); @@ -662,13 +654,13 @@ void link_emit_elf_aarch64(LinkImage* img, Writer* w) if (e > end_of_segs) end_of_segs = e; } } - u64 symtab_off = align_up_u64(end_of_segs, 8u); + u64 symtab_off = ALIGN_UP(end_of_segs, (u64)8u); u64 symtab_size = (u64)ELF64_SYM_SIZE * nsyms_emit; u64 strtab_off = symtab_off + symtab_size; u64 strtab_size = strtab.len; u64 shstrtab_off = strtab_off + strtab_size; u64 shstrtab_size = shstrtab.len; - u64 shdr_off = align_up_u64(shstrtab_off + shstrtab_size, 8u); + u64 shdr_off = ALIGN_UP(shstrtab_off + shstrtab_size, (u64)8u); /* ---- build phdrs ---- */ Phdr64* phdrs = (Phdr64*)heap->alloc(heap, sizeof(Phdr64) * nphdr_total, diff --git a/src/link/link_internal.h b/src/link/link_internal.h @@ -6,6 +6,7 @@ * not included by anything outside src/link/. */ #include "core/core.h" +#include "core/hashmap.h" #include "obj/obj.h" #include "link/link.h" @@ -25,32 +26,25 @@ typedef struct InputMap { /* Open-addressed name -> LinkSymId hash for global / weak definitions * and lookups (cfree_jit_lookup, entry-symbol resolution). Locals never - * land in this table. - * - * Capacity is always a power of two; we keep load factor < 0.75 by - * doubling on insert. Sym ids are 32-bit interned strings — compare - * with ==. id == 0 is the empty slot (Sym 0 is the "none" sentinel - * and never appears as a real name). */ -typedef struct SymHashEntry { - Sym name; - LinkSymId id; -} SymHashEntry; - -typedef struct SymHash { - Heap* heap; - SymHashEntry* slots; - u32 cap; - u32 used; -} SymHash; - -void symhash_init(SymHash*, Heap*); -void symhash_fini(SymHash*); -/* Returns 1 on insert, 0 if name already present (and writes the existing - * id into *existing_out when non-NULL). The replace policy lives in the - * caller — this is a flat insert/lookup map. */ -int symhash_insert(SymHash*, Sym name, LinkSymId id, LinkSymId* existing_out); -void symhash_set(SymHash*, Sym name, LinkSymId id); /* unconditional */ -LinkSymId symhash_get(const SymHash*, Sym name); + * land in this table. Sym 0 is the empty-slot sentinel (it's also the + * "none" id per core.h:42 and never appears as a real name). */ + +static inline u32 link_sym_hash_(Sym s) { return hash_u32((u32)s); } +HASHMAP_DEFINE(SymHash, Sym, LinkSymId, link_sym_hash_); + +/* Convenience wrappers: the existing call sites pass LinkSymId by value + * (LINK_SYM_NONE on miss) and use insert-if-absent semantics. */ +static inline void symhash_init(SymHash* h, Heap* heap) +{ SymHash_init(h, heap); } +static inline void symhash_fini(SymHash* h) +{ SymHash_fini(h); } +static inline LinkSymId symhash_get(const SymHash* h, Sym name) +{ LinkSymId* hit = SymHash_get(h, name); return hit ? *hit : LINK_SYM_NONE; } +static inline void symhash_set(SymHash* h, Sym name, LinkSymId id) +{ (void)SymHash_set(h, name, id); } +static inline int symhash_insert(SymHash* h, Sym name, LinkSymId id, + LinkSymId* existing_out) +{ return SymHash_try_insert(h, name, id, existing_out); } struct CfreeJit; /* forward; see link_jit.c */ diff --git a/src/link/link_jit.c b/src/link/link_jit.c @@ -13,6 +13,7 @@ #include "core/heap.h" #include "core/pool.h" +#include "core/util.h" #include <cfree.h> @@ -20,8 +21,6 @@ static SrcLoc no_loc(void) { SrcLoc l = {0,0,0}; return l; } -static u64 align_up_u64(u64 v, u64 a) { return (v + (a - 1u)) & ~(a - 1u); } - static const CfreeExecMem* require_execmem(Compiler* c) { const CfreeExecMem* m = c->env ? c->env->execmem : NULL; @@ -159,7 +158,7 @@ CfreeJit* cfree_jit_from_image(LinkImage* img) * data/rodata the two aliases coincide. */ for (i = 0; i < img->nsegments; ++i) { const LinkSegment* seg = &img->segments[i]; - size_t mlen = (size_t)align_up_u64(seg->mem_size, page); + size_t mlen = (size_t)ALIGN_UP(seg->mem_size, page); if (mem->reserve(mem->user, mlen, perms_for(seg->flags), &segs[i]) != 0) { u32 j; diff --git a/src/link/link_layout.c b/src/link/link_layout.c @@ -11,8 +11,11 @@ #include "link/link_internal.h" #include "core/buf.h" +#include "core/bytes.h" #include "core/heap.h" #include "core/pool.h" +#include "core/util.h" +#include "core/vec.h" #include <cfree.h> @@ -47,8 +50,6 @@ typedef enum SegBucket { SEG_NBUCKETS = 4, } SegBucket; -static u64 align_up_u64(u64 v, u64 a) { return (v + (a - 1u)) & ~(a - 1u); } - static int section_kept(const Section* s) { /* This cut keeps allocatable progbits/nobits sections only. Debug, @@ -72,19 +73,8 @@ static SegBucket bucket_for(u16 flags) static void syms_grow(LinkImage* img, u32 want) { - u32 new_cap; - LinkSymbol* p; - if (want <= img->syms_cap) return; - new_cap = img->syms_cap ? img->syms_cap : 16u; - while (new_cap < want) new_cap *= 2u; - p = (LinkSymbol*)img->heap->realloc( - img->heap, img->syms, - sizeof(*img->syms) * img->syms_cap, - sizeof(*img->syms) * new_cap, - _Alignof(LinkSymbol)); - if (!p) compiler_panic(img->c, no_loc(), "link: oom growing symbols"); - img->syms = p; - img->syms_cap = new_cap; + if (VEC_GROW(img->heap, img->syms, img->syms_cap, want)) + compiler_panic(img->c, no_loc(), "link: oom growing symbols"); } static LinkSymId append_symbol(LinkImage* img, const LinkSymbol* tmpl) @@ -100,19 +90,8 @@ static LinkSymId append_symbol(LinkImage* img, const LinkSymbol* tmpl) static void relocs_grow(LinkImage* img, u32 want) { - u32 new_cap; - LinkRelocApply* p; - if (want <= img->relocs_cap) return; - new_cap = img->relocs_cap ? img->relocs_cap : 32u; - while (new_cap < want) new_cap *= 2u; - p = (LinkRelocApply*)img->heap->realloc( - img->heap, img->relocs, - sizeof(*img->relocs) * img->relocs_cap, - sizeof(*img->relocs) * new_cap, - _Alignof(LinkRelocApply)); - if (!p) compiler_panic(img->c, no_loc(), "link: oom growing relocs"); - img->relocs = p; - img->relocs_cap = new_cap; + if (VEC_GROW(img->heap, img->relocs, img->relocs_cap, want)) + compiler_panic(img->c, no_loc(), "link: oom growing relocs"); } /* ---- per-input symbol/section maps ---- */ @@ -365,15 +344,8 @@ typedef struct GcQueue { static void gc_queue_push(GcQueue* q, Heap* h, u32 ii, ObjSecId j) { - if (q->n == q->cap) { - u32 nc = q->cap ? q->cap * 2u : 32u; - u64* nb = (u64*)h->realloc(h, q->items, - sizeof(*q->items) * q->cap, - sizeof(*q->items) * nc, _Alignof(u64)); - if (!nb) return; /* skip; caller iterates to fixed point */ - q->items = nb; - q->cap = nc; - } + if (VEC_GROW(h, q->items, q->cap, q->n + 1u)) + return; /* skip; caller iterates to fixed point */ q->items[q->n++] = GC_PACK(ii, j); } @@ -740,13 +712,13 @@ static void layout_sections(Linker* l, LinkImage* img, const GcLive* g) if (s->sem == SSEM_NOBITS) { u64 cursor = seg_size[bucket] + seg_bss_extra[bucket]; - cursor = align_up_u64(cursor, align); + cursor = ALIGN_UP(cursor, (u64)(align)); seg_bss_extra[bucket] = cursor + (u64)s->bss_size - seg_size[bucket]; ofs = cursor; } else { seg_size[bucket] += seg_bss_extra[bucket]; seg_bss_extra[bucket] = 0; - ofs = align_up_u64(seg_size[bucket], align); + ofs = ALIGN_UP(seg_size[bucket], (u64)(align)); seg_size[bucket] = ofs + (u64)s->bytes.total; } @@ -818,7 +790,7 @@ static void layout_sections(Linker* l, LinkImage* img, const GcLive* g) nat_align = seg_align[b] ? seg_align[b] : 1u; align = (u64)nat_align; if (align < layout_page_size(l)) align = layout_page_size(l); - cursor = align_up_u64(cursor, align); + cursor = ALIGN_UP(cursor, (u64)(align)); seg = &img->segments[img->nsegments]; file_size = seg_size[b]; @@ -918,7 +890,7 @@ static void layout_commons(Linker* l, LinkImage* img) u64 end = img->segments[i].vaddr + img->segments[i].mem_size; if (end > vaddr) vaddr = end; } - vaddr = align_up_u64(vaddr, layout_page_size(l)); + vaddr = ALIGN_UP(vaddr, (u64)(layout_page_size(l))); segs = (LinkSegment*)img->heap->realloc(img->heap, img->segments, sizeof(*img->segments) * img->nsegments, sizeof(*img->segments) * nseg, _Alignof(LinkSegment)); @@ -955,7 +927,7 @@ static void layout_commons(Linker* l, LinkImage* img) u32 align; if (s->kind != SK_COMMON || !s->defined) continue; align = s->common_align ? s->common_align : 1u; - bss_cursor = align_up_u64(bss_cursor, align); + bss_cursor = ALIGN_UP(bss_cursor, (u64)(align)); s->vaddr = bss_cursor; bss_cursor += s->size ? s->size : 1u; s->kind = SK_OBJ; /* no longer COMMON once placed */ @@ -1367,17 +1339,9 @@ static void layout_got(Linker* l, LinkImage* img, LinkSymId** got_map_out) target = m->sym[r->sym]; if (target == LINK_SYM_NONE) continue; if (got_map[target] != LINK_SYM_NONE) continue; - if (nslot == slot_cap) { - u32 nc = slot_cap ? slot_cap * 2u : 8u; - LinkSymId* nb = (LinkSymId*)h->realloc( - h, slot_targets, - sizeof(*slot_targets) * slot_cap, - sizeof(*slot_targets) * nc, _Alignof(LinkSymId)); - if (!nb) compiler_panic(img->c, no_loc(), - "link: oom on got slot list"); - slot_targets = nb; - slot_cap = nc; - } + if (VEC_GROW(h, slot_targets, slot_cap, nslot + 1u)) + compiler_panic(img->c, no_loc(), + "link: oom on got slot list"); slot_targets[nslot] = target; /* Mark sentinel; replaced with real slot LinkSymId below. */ got_map[target] = (LinkSymId)(nslot + 1u); @@ -1403,7 +1367,7 @@ static void layout_got(Linker* l, LinkImage* img, LinkSymId** got_map_out) u64 end = img->segments[j].vaddr + img->segments[j].mem_size; if (end > base_vaddr) base_vaddr = end; } - base_vaddr = align_up_u64(base_vaddr, page); + base_vaddr = ALIGN_UP(base_vaddr, (u64)(page)); got_size = (u64)nslot * 8u; { @@ -1535,14 +1499,6 @@ static void layout_got(Linker* l, LinkImage* img, LinkSymId** got_map_out) * vaddr is final, and before emit_reloc_records so the synthesized * stub relocs ride the same apply path as ordinary input relocs. */ -static void wr_u32_le(u8* p, u32 v) -{ - p[0] = (u8)(v & 0xffu); - p[1] = (u8)((v >> 8 ) & 0xffu); - p[2] = (u8)((v >> 16) & 0xffu); - p[3] = (u8)((v >> 24) & 0xffu); -} - static void layout_iplt(Linker* l, LinkImage* img) { Heap* h = img->heap; @@ -1575,10 +1531,10 @@ static void layout_iplt(Linker* l, LinkImage* img) u64 end = img->segments[i].vaddr + img->segments[i].mem_size; if (end > base_vaddr) base_vaddr = end; } - base_vaddr = align_up_u64(base_vaddr, page); + base_vaddr = ALIGN_UP(base_vaddr, (u64)(page)); iplt_vaddr = base_vaddr; iplt_size = (u64)nifunc * 12u; - igot_vaddr = align_up_u64(iplt_vaddr + iplt_size, page); + igot_vaddr = ALIGN_UP(iplt_vaddr + iplt_size, (u64)(page)); igot_size = (u64)nifunc * 8u; /* Grow segment / segment-bytes arrays by 2. */ diff --git a/src/link/link_reloc.c b/src/link/link_reloc.c @@ -10,36 +10,12 @@ #include "link/link_internal.h" +#include "core/bytes.h" + #include <string.h> static SrcLoc no_loc(void) { SrcLoc l = {0,0,0}; return l; } -static void wr_u16_le(u8* p, u16 v) -{ - p[0] = (u8)(v & 0xffu); - p[1] = (u8)((v >> 8) & 0xffu); -} - -static void wr_u32_le(u8* p, u32 v) -{ - p[0] = (u8)(v & 0xffu); - p[1] = (u8)((v >> 8 ) & 0xffu); - p[2] = (u8)((v >> 16) & 0xffu); - p[3] = (u8)((v >> 24) & 0xffu); -} - -static u32 rd_u32_le(const u8* p) -{ - return (u32)p[0] | ((u32)p[1] << 8) | - ((u32)p[2] << 16) | ((u32)p[3] << 24); -} - -static void wr_u64_le(u8* p, u64 v) -{ - wr_u32_le(p, (u32)(v & 0xffffffffu)); - wr_u32_le(p + 4, (u32)((v >> 32) & 0xffffffffu)); -} - void link_reloc_apply(Compiler* c, RelocKind k, u8* P_bytes, u64 S, i64 A, u64 P) { diff --git a/src/obj/elf.h b/src/obj/elf.h @@ -173,39 +173,26 @@ u32 elf_aarch64_reloc_to (u32 kind /* RelocKind */); u32 elf_aarch64_reloc_from(u32 elf_type); -/* ---- little-endian byte writers/readers ---- */ +/* ---- little-endian byte writers/readers (Writer-based) ---- + * Reads use rd_u*_le from core/bytes.h directly; only writes need the + * Writer-bridging wrappers below. */ + +#include "core/bytes.h" + static inline void elf_wr_u8 (Writer* w, u32 v) { u8 b = (u8)v; cfree_writer_write(w, &b, 1); } static inline void elf_wr_u16(Writer* w, u32 v) -{ - u8 b[2] = { (u8)(v), (u8)(v >> 8) }; - cfree_writer_write(w, b, 2); -} +{ u8 b[2]; wr_u16_le(b, (u16)v); cfree_writer_write(w, b, 2); } static inline void elf_wr_u32(Writer* w, u32 v) -{ - u8 b[4] = { (u8)(v), (u8)(v >> 8), (u8)(v >> 16), (u8)(v >> 24) }; - cfree_writer_write(w, b, 4); -} +{ u8 b[4]; wr_u32_le(b, v); cfree_writer_write(w, b, 4); } static inline void elf_wr_u64(Writer* w, u64 v) -{ - u8 b[8] = { - (u8)(v), (u8)(v >> 8), (u8)(v >> 16), (u8)(v >> 24), - (u8)(v >> 32), (u8)(v >> 40), (u8)(v >> 48), (u8)(v >> 56), - }; - cfree_writer_write(w, b, 8); -} - -static inline u16 elf_rd_u16(const u8* p) { return (u16)(p[0] | (p[1] << 8)); } -static inline u32 elf_rd_u32(const u8* p) -{ - return (u32)p[0] | ((u32)p[1] << 8) | ((u32)p[2] << 16) | ((u32)p[3] << 24); -} -static inline u64 elf_rd_u64(const u8* p) -{ - return (u64)elf_rd_u32(p) | ((u64)elf_rd_u32(p + 4) << 32); -} +{ u8 b[8]; wr_u64_le(b, v); cfree_writer_write(w, b, 8); } + +static inline u16 elf_rd_u16(const u8* p) { return rd_u16_le(p); } +static inline u32 elf_rd_u32(const u8* p) { return rd_u32_le(p); } +static inline u64 elf_rd_u64(const u8* p) { return rd_u64_le(p); } #endif /* CFREE_OBJ_ELF_H */ diff --git a/src/obj/elf_emit.c b/src/obj/elf_emit.c @@ -28,6 +28,7 @@ #include "core/arena.h" #include "core/buf.h" #include "core/heap.h" +#include "core/util.h" #include "core/pool.h" #include <string.h> @@ -210,17 +211,6 @@ static u32 strtab_add(Buf* b, const char* s, u32 len) return off; } -static u32 align_up(u32 x, u32 a) -{ - if (a < 2) return x; - return (x + (a - 1)) & ~(a - 1); -} - -static u64 align_up64(u64 x, u64 a) -{ - if (a < 2) return x; - return (x + (a - 1)) & ~(a - 1); -} void emit_elf(Compiler* c, ObjBuilder* ob, Writer* w) { @@ -261,8 +251,7 @@ void emit_elf(Compiler* c, ObjBuilder* ob, Writer* w) memset(&secs[nsecs++], 0, sizeof secs[0]); /* index 0 = SHN_UNDEF */ /* Map obj section id -> ELF section index. */ - u32* obj_to_elf = arena_array(c->scratch, u32, nobjsec); - memset(obj_to_elf, 0, sizeof(u32) * nobjsec); + u32* obj_to_elf = arena_zarray(c->scratch, u32, nobjsec); for (u32 i = 1; i < nobjsec; ++i) { const Section* s = obj_section_get(ob, i); @@ -347,8 +336,7 @@ void emit_elf(Compiler* c, ObjBuilder* ob, Writer* w) * symbols carry one. */ /* Map obj symbol id -> elf symbol index. */ - u32* sym_to_elf = arena_array(c->scratch, u32, nobjsym + 2); - memset(sym_to_elf, 0, sizeof(u32) * (nobjsym + 2)); + u32* sym_to_elf = arena_zarray(c->scratch, u32, nobjsym + 2); /* Two passes over obj symbols: locals, then globals/weak. */ for (int pass = 0; pass < 2; ++pass) { @@ -457,8 +445,7 @@ void emit_elf(Compiler* c, ObjBuilder* ob, Writer* w) u32 size; /* bytes count = nrelocs * 24 */ } RelaPlan; - RelaPlan* rela_plans = arena_array(c->scratch, RelaPlan, nobjsec); - memset(rela_plans, 0, sizeof(RelaPlan) * nobjsec); + RelaPlan* rela_plans = arena_zarray(c->scratch, RelaPlan, nobjsec); u32 nrela_plans = 0; for (u32 si = 1; si < nobjsec; ++si) { @@ -613,11 +600,11 @@ void emit_elf(Compiler* c, ObjBuilder* ob, Writer* w) continue; } u64 a = es->sh_addralign ? es->sh_addralign : 1; - cur = align_up64(cur, a); + cur = ALIGN_UP(cur, a); es->sh_offset = cur; cur += es->sh_size; } - cur = align_up64(cur, 8); + cur = ALIGN_UP(cur, (u64)8); u64 e_shoff = cur; /* ---- pass 6: write Ehdr ----------------------------------------- */ @@ -708,6 +695,4 @@ void emit_elf(Compiler* c, ObjBuilder* ob, Writer* w) elf_wr_u64(w, es->sh_addralign); elf_wr_u64(w, es->sh_entsize); } - - (void)align_up; } diff --git a/src/obj/elf_read.c b/src/obj/elf_read.c @@ -230,8 +230,7 @@ ObjBuilder* read_elf(Compiler* c, const char* name, if (!ob) compiler_panic(c, no_loc(), "read_elf: obj_new failed"); /* elf_to_obj[shndx] -> ObjSecId, OBJ_SEC_NONE for skipped sections. */ - u32* elf_to_obj = arena_array(c->scratch, u32, e_shnum); - memset(elf_to_obj, 0, sizeof(u32) * e_shnum); + u32* elf_to_obj = arena_zarray(c->scratch, u32, e_shnum); /* Pass 1: create obj sections for every non-NULL shdr that carries * load-bearing model state. SYMTAB / STRTAB / RELA / REL are @@ -331,8 +330,7 @@ ObjBuilder* read_elf(Compiler* c, const char* name, u64 strtab_sz = str_sh->sh_size; nsyms = (u32)(sh->sh_size / ELF64_SYM_SIZE); - sym_elf_to_obj = arena_array(c->scratch, u32, nsyms ? nsyms : 1); - memset(sym_elf_to_obj, 0, sizeof(u32) * (nsyms ? nsyms : 1)); + sym_elf_to_obj = arena_zarray(c->scratch, u32, nsyms ? nsyms : 1); const u8* base = data + sh->sh_offset; for (u32 i = 1; i < nsyms; ++i) { /* skip index 0 */ diff --git a/src/obj/obj.c b/src/obj/obj.c @@ -10,6 +10,7 @@ #include "core/heap.h" #include "core/pool.h" +#include "core/vec.h" #include <string.h> @@ -45,79 +46,17 @@ struct ObjSymIter { /* ---- growth helpers ---- */ -#define GROW_AT_LEAST(cap, want) ((cap) ? (cap) : 8) - static int sections_grow(ObjBuilder* ob, u32 want) -{ - u32 new_cap; - Section* p; - if (want <= ob->sections_cap) return 0; - new_cap = GROW_AT_LEAST(ob->sections_cap, want); - while (new_cap < want) new_cap *= 2; - p = (Section*)ob->heap->realloc( - ob->heap, ob->sections, - sizeof(*ob->sections) * ob->sections_cap, - sizeof(*ob->sections) * new_cap, - _Alignof(Section)); - if (!p) return 1; - ob->sections = p; - ob->sections_cap = new_cap; - return 0; -} +{ return VEC_GROW(ob->heap, ob->sections, ob->sections_cap, want); } static int symbols_grow(ObjBuilder* ob, u32 want) -{ - u32 new_cap; - ObjSym* p; - if (want <= ob->symbols_cap) return 0; - new_cap = GROW_AT_LEAST(ob->symbols_cap, want); - while (new_cap < want) new_cap *= 2; - p = (ObjSym*)ob->heap->realloc( - ob->heap, ob->symbols, - sizeof(*ob->symbols) * ob->symbols_cap, - sizeof(*ob->symbols) * new_cap, - _Alignof(ObjSym)); - if (!p) return 1; - ob->symbols = p; - ob->symbols_cap = new_cap; - return 0; -} +{ return VEC_GROW(ob->heap, ob->symbols, ob->symbols_cap, want); } static int relocs_grow(ObjBuilder* ob, u32 want) -{ - u32 new_cap; - Reloc* p; - if (want <= ob->relocs_cap) return 0; - new_cap = GROW_AT_LEAST(ob->relocs_cap, want); - while (new_cap < want) new_cap *= 2; - p = (Reloc*)ob->heap->realloc( - ob->heap, ob->relocs, - sizeof(*ob->relocs) * ob->relocs_cap, - sizeof(*ob->relocs) * new_cap, - _Alignof(Reloc)); - if (!p) return 1; - ob->relocs = p; - ob->relocs_cap = new_cap; - return 0; -} +{ return VEC_GROW(ob->heap, ob->relocs, ob->relocs_cap, want); } static int groups_grow(ObjBuilder* ob, u32 want) -{ - u32 new_cap; - ObjGroup* p; - if (want <= ob->groups_cap) return 0; - new_cap = GROW_AT_LEAST(ob->groups_cap, want); - while (new_cap < want) new_cap *= 2; - p = (ObjGroup*)ob->heap->realloc( - ob->heap, ob->groups, - sizeof(*ob->groups) * ob->groups_cap, - sizeof(*ob->groups) * new_cap, - _Alignof(ObjGroup)); - if (!p) return 1; - ob->groups = p; - ob->groups_cap = new_cap; - return 0; -} +{ return VEC_GROW(ob->heap, ob->groups, ob->groups_cap, want); } /* ---- lifecycle ---- */ diff --git a/src/pp/pp.c b/src/pp/pp.c @@ -73,10 +73,11 @@ typedef struct TokSrc { Sym file_override; } TokSrc; -typedef struct MacroEntry { - Sym key; /* 0 = empty */ - Macro* val; -} MacroEntry; +/* MacroMap = Sym -> Macro*. Generated open-addressed hashmap with + * deletion (#undef). See core/hashmap.h. */ +#include "core/hashmap.h" +static inline u32 macro_hash_(Sym s) { return hash_u32((u32)s); } +HASHMAP_DEFINE(MacroMap, Sym, Macro*, macro_hash_); typedef enum IfState { IF_INCLUDE = 1, /* group active, emit code */ @@ -99,10 +100,8 @@ struct Pp { u32 nsources; u32 sources_cap; - /* Macro table (open-addressed). */ - MacroEntry* mtab; - u32 mtab_cap; - u32 mtab_used; + /* Macro table (open-addressed; key = Sym, value = Macro*). */ + MacroMap mtab; /* Conditional inclusion stack (#if / #ifdef / #ifndef → #endif). */ IfFrame* ifstk; @@ -343,100 +342,15 @@ static HidesetId hs_intersect(Pp* pp, HidesetId a, HidesetId b) * Macro table * ============================================================ */ -static u32 mt_hash(Sym s) -{ - /* xorshift mixer; Syms are dense small integers so a simple mix suffices. */ - u32 x = (u32)s * 2654435761u; - x ^= x >> 16; - return x; -} - -static void mt_grow(Pp* pp, u32 nc) -{ - MacroEntry* old = pp->mtab; - u32 oldc = pp->mtab_cap; - u32 i; - pp->mtab = (MacroEntry*)pp_xrealloc(pp, NULL, 0, - sizeof(MacroEntry) * nc, - _Alignof(MacroEntry)); - pp->mtab_cap = nc; - pp->mtab_used = 0; - for (i = 0; i < nc; ++i) { pp->mtab[i].key = 0; pp->mtab[i].val = NULL; } - for (i = 0; i < oldc; ++i) { - if (old[i].key) { - u32 mask = nc - 1; - u32 h = mt_hash(old[i].key) & mask; - while (pp->mtab[h].key) h = (h + 1) & mask; - pp->mtab[h] = old[i]; - ++pp->mtab_used; - } - } - pp_xfree(pp, old, sizeof(MacroEntry) * oldc); -} - +/* Thin wrappers over the generated MacroMap_* functions; preserved + * because the call sites are tagged "mt_*" throughout this TU. */ static Macro* mt_get(Pp* pp, Sym name) -{ - u32 mask, h; - if (!pp->mtab_cap || name == 0) return NULL; - mask = pp->mtab_cap - 1; - h = mt_hash(name) & mask; - while (pp->mtab[h].key) { - if (pp->mtab[h].key == name) return pp->mtab[h].val; - h = (h + 1) & mask; - } - return NULL; -} +{ Macro** v = MacroMap_get(&pp->mtab, name); return v ? *v : NULL; } static void mt_put(Pp* pp, Sym name, Macro* m) -{ - u32 mask, h; - if (!pp->mtab_cap || (pp->mtab_used + 1) * 2 >= pp->mtab_cap) { - mt_grow(pp, pp->mtab_cap ? pp->mtab_cap * 2 : 32); - } - mask = pp->mtab_cap - 1; - h = mt_hash(name) & mask; - while (pp->mtab[h].key) { - if (pp->mtab[h].key == name) { pp->mtab[h].val = m; return; } - h = (h + 1) & mask; - } - pp->mtab[h].key = name; - pp->mtab[h].val = m; - ++pp->mtab_used; -} - -static void mt_del(Pp* pp, Sym name) -{ - /* Tombstoneless deletion: on remove, rehash the cluster. */ - u32 mask, h; - if (!pp->mtab_cap) return; - mask = pp->mtab_cap - 1; - h = mt_hash(name) & mask; - while (pp->mtab[h].key) { - if (pp->mtab[h].key == name) { - pp->mtab[h].key = 0; - pp->mtab[h].val = NULL; - --pp->mtab_used; - /* Rehash following cluster. */ - h = (h + 1) & mask; - while (pp->mtab[h].key) { - Sym k = pp->mtab[h].key; - Macro* v = pp->mtab[h].val; - u32 nh; - pp->mtab[h].key = 0; - pp->mtab[h].val = NULL; - --pp->mtab_used; - nh = mt_hash(k) & mask; - while (pp->mtab[nh].key) nh = (nh + 1) & mask; - pp->mtab[nh].key = k; - pp->mtab[nh].val = v; - ++pp->mtab_used; - h = (h + 1) & mask; - } - return; - } - h = (h + 1) & mask; - } -} +{ (void)MacroMap_set(&pp->mtab, name, m); } + +static void mt_del(Pp* pp, Sym name) { MacroMap_del(&pp->mtab, name); } /* ============================================================ * Source stack @@ -622,8 +536,7 @@ static void do_define(Pp* pp, const Tok* line, u32 n) def_loc = line[i].loc; ++i; - m = arena_new(&pp->arena, Macro); - memset(m, 0, sizeof(*m)); + m = arena_znew(&pp->arena, Macro); m->name = name; m->def_loc = def_loc; @@ -2794,7 +2707,7 @@ Pp* pp_new(Compiler* c) _Alignof(Hideset*)); pp->hsets[0] = NULL; pp->hsets_n = 1; - mt_grow(pp, 32); + MacroMap_init_cap(&pp->mtab, h, 32u); pp_intern_keywords(pp); compute_date_time(pp); pp_register_static_predefined(pp); @@ -2807,7 +2720,7 @@ void pp_free(Pp* pp) /* Pop / close any remaining lex sources. */ while (pp->nsources) src_pop(pp); pp_xfree(pp, pp->sources, sizeof(TokSrc) * pp->sources_cap); - pp_xfree(pp, pp->mtab, sizeof(MacroEntry) * pp->mtab_cap); + MacroMap_fini(&pp->mtab); pp_xfree(pp, pp->hsets, sizeof(Hideset*) * pp->hsets_cap); pp_xfree(pp, pp->ifstk, sizeof(IfFrame) * pp->ifstk_cap); pp_xfree(pp, pp->inc_dirs,