kit

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

commit 209596b8a0efc2568c32863ca6990825240d26f1
parent b50e883e3b179503f8c2340695356869e6b7e602
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Sat, 13 Jun 2026 11:36:50 -0700

perf(pp): skip no-op arena_reset when xarena is pristine (A5)

(cherry picked from commit 0ba6525dd53dd5f07f075cddb8c5555755f1b96e)

Diffstat:
Minclude/kit/support/arena.h | 2++
Mlang/cpp/pp/pp_expand.c | 8++++++--
Msrc/api/support_arena.c | 4++++
Msrc/core/arena.c | 13++++++++++++-
Msrc/core/arena.h | 1+
5 files changed, 25 insertions(+), 3 deletions(-)

diff --git a/include/kit/support/arena.h b/include/kit/support/arena.h @@ -14,6 +14,8 @@ typedef struct KitArena KitArena; KIT_API KitStatus kit_arena_new(KitHeap*, size_t block_size, KitArena** out); KIT_API void kit_arena_free(KitArena*); KIT_API void kit_arena_reset(KitArena*); +/* True when reset would be a no-op (nothing allocated since last reset). */ +KIT_API int kit_arena_is_empty(const KitArena*); KIT_API void* kit_arena_alloc(KitArena*, size_t size, size_t align); KIT_API void* kit_arena_zalloc(KitArena*, size_t size, size_t align); KIT_API char* kit_arena_strdup(KitArena*, const char* s, size_t len); diff --git a/lang/cpp/pp/pp_expand.c b/lang/cpp/pp/pp_expand.c @@ -959,11 +959,15 @@ static void pp_pull_into(Pp* pp, Tok* out, int skip_nl) { * reset site; it never fires mid-expansion (a SRC_BUF, incl. the * scope_top arg-prescan buffer, is on top then) nor during raw argument * collection (which uses src_next_raw, not this function). Skipped inside - * #if expansion, whose bounded condition keeps its scratch live. */ + * #if expansion, whose bounded condition keeps its scratch live. The reset + * is further skipped when the xarena is already at its baseline (a plain + * lexer run that allocated no scratch): kit_arena_is_empty is the pristine + * guard, so the dead reset on plain-token runs is elided -- byte-identical + * since resetting an already-pristine arena does nothing. */ if (!pp->in_if_expansion && (pp->nsources == 0 || pp->sources[pp->nsources - 1].kind == SRC_LEX)) { - kit_arena_reset(pp->xarena); + if (!kit_arena_is_empty(pp->xarena)) kit_arena_reset(pp->xarena); } /* Fast path: top source is a non-exhausted SRC_BUF (the dominant * macro-replay case). Pull the token, hideset, and kind inline so the diff --git a/src/api/support_arena.c b/src/api/support_arena.c @@ -32,6 +32,10 @@ void kit_arena_reset(KitArena* a) { if (a) arena_reset(&a->inner); } +int kit_arena_is_empty(const KitArena* a) { + return a ? arena_is_empty(&a->inner) : 1; +} + void* kit_arena_alloc(KitArena* a, size_t size, size_t align) { return a ? arena_alloc(&a->inner, size, align) : NULL; } diff --git a/src/core/arena.c b/src/core/arena.c @@ -47,12 +47,23 @@ void arena_fini(Arena* a) { a->end = NULL; } +int arena_is_empty(const Arena* a) { + /* The pristine predicate: reset would be a no-op. Either nothing is + * allocated (head==NULL), or the head is the lone, non-oversize block with + * cur still at its base -- exactly the state arena_reset re-establishes. */ + if (!a->head) return 1; + return a->head->next == NULL && a->head->cap <= a->block_size && + a->cur == a->head->data; +} + void arena_reset(Arena* a) { /* Free every block past the head, then reuse the head as the O(1) reset * baseline -- unless it is an oversize dedicated block (cap > block_size). * The head is the most-recently allocated block, so a one-off large * allocation just before reset would otherwise become a permanently - * inflated baseline; release it and let the next alloc size a fresh one. */ + * inflated baseline; release it and let the next alloc size a fresh one. + * Skip entirely when the arena is already at its reset baseline. */ + if (arena_is_empty(a)) return; if (a->head) { ArenaBlock* b = a->head->next; while (b) { diff --git a/src/core/arena.h b/src/core/arena.h @@ -17,6 +17,7 @@ struct Arena { void arena_init(Arena*, Heap*, size_t block_size); void arena_fini(Arena*); void arena_reset(Arena*); +int arena_is_empty(const 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);