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:
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,