kit

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

commit dc7830d966d3836fc25be94cd5cc03ac7e0a1427
parent 0020f1ce0b5092ec7547fea6464babcb19b42836
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Wed, 10 Jun 2026 18:04:18 -0700

perf(cc): zero-churn allocation + buffered IO at -O0

Round 1 made every axis linear; the remaining slowness was a constant
factor the sampler kept surfacing as kernel time: libmalloc churn
(mach_vm_reclaim) at 50-57% of compile, and write() at up to 96% of -E.
Both are now gone, and every compile axis is faster than clang -O0.
Output is byte-identical; ASan/UBSan-clean.

Five fixes, each eliminating a per-input-unit allocation or syscall:

1. type-lowering pool: c_abi_type_info/sizeof/alignof called type_cg_id,
   which spun up and tore down a whole Pool (64KB arena) per query --
   every literal/sizeof/conversion. That one site was the 50%+
   mach_vm_reclaim hotspot (1M 64KB alloc+free pairs for one 128k-stmt
   function). Route the queries through the parser's persistent p->pool
   (matching cg_adapter); builtins now allocate nothing, records memoize.
   Remove the now-dead type_cg_id.

2. buffered output writer (driver/env/posix.c): fdw_write did one write()
   syscall per KitWriter::write, and pp_emit_text writes once per token.
   Add a 64KB buffer with flush-on-seek (object emit seeks to patch
   headers) and large-write passthrough. pp-macro -E: millions -> 148.

3. per-token lexer scratch (lang/cpp/lex/lex.c): the pp-number path
   malloc'd a temp buffer per numeric token for splice removal. Add the
   no-splice fast path it already had for identifiers, plus a grow-once
   high-water Lexer.scratch so even the spliced slow path never allocates
   per token.

4. linkage-name concat (src/api/core.c): kit_cg_c_linkage_name malloc'd a
   temp per declared symbol on Mach-O to prepend '_'; use a 256-byte stack
   buffer, heap only for pathologically long names.

5. shared CFI directive vector (src/arch/mc.c): each CfiFde owned a
   separately heap-allocated directives array (a per-function malloc).
   Use one growable vector on MCImpl; each FDE keeps a (dir_start, ndir)
   range -- single-pass emission keeps them contiguous.

Results (kit @maxN, kit/clang): body-size 1321->305ms (1.30x slower ->
0.36x), global-decl 930->77ms (5.3x slower -> 0.48x), pp-macro -E
22x slower -> 0.76x, fn-count 403->129ms. Mallocs for a 128k-stmt
function: 3.3M -> 880. Per-unit mallocs across axes now <0.07.

Diffstat:
Mdoc/plan/PERF.md | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdriver/env/posix.c | 51+++++++++++++++++++++++++++++++++++++++++++++------
Mlang/c/abi/c_abi.c | 12++++++------
Mlang/c/abi/c_abi.h | 9++++++---
Mlang/c/parse/cg_adapter.c | 10+++++-----
Mlang/c/parse/parse.c | 14+++++++-------
Mlang/c/parse/parse_expr.c | 74+++++++++++++++++++++++++++++++++++++-------------------------------------
Mlang/c/parse/parse_init.c | 50+++++++++++++++++++++++++-------------------------
Mlang/c/parse/parse_stmt.c | 4++--
Mlang/c/parse/parse_type.c | 18+++++++++---------
Mlang/c/type/type.c | 8--------
Mlang/c/type/type.h | 1-
Mlang/cpp/lex/lex.c | 81+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Msrc/api/core.c | 10++++++++--
Msrc/arch/mc.c | 72+++++++++++++++++++++++++++++++++++++++---------------------------------
15 files changed, 317 insertions(+), 164 deletions(-)

diff --git a/doc/plan/PERF.md b/doc/plan/PERF.md @@ -75,6 +75,73 @@ Quick wire-check: `KIT_CC_BENCH_SIZES='8 16 32' KIT_CC_BENCH_SAMPLE=0 make bench ## Findings +### Round 2 — constant factor: zero-churn allocation + buffered IO + +> 2026-06-10, M1 (8-core), clang-built `PROFILE=1` release kit, best-of-3. +> Round 1 made every axis linear; the remaining slowness was a **constant +> factor** dominated by two things the sampler kept surfacing as kernel time: +> `mach_vm_reclaim_*` (libmalloc churn) at 50–57 % of compile, and `write` at +> up to 96 % of `-E`. Both are now gone. **Every compile axis is now faster than +> clang -O0.** + +| axis | kit @maxN before → after | kit/clang before → after | +|------|:------------------------:|:------------------------:| +| body-size | 1321 ms → **305 ms** | 1.30× slower → **0.36× (2.8× faster)** | +| global-decl | 930 ms → **77 ms** | 5.31× slower → **0.48× (2.1× faster)** | +| fn-count | 403 ms → **129 ms** | 0.33× → **0.13× (7.7× faster)** | +| ref-density | 565 ms → **240 ms** | 0.16× → **0.08×** | +| locals-per-fn | 264 ms → **72 ms** | 0.04× → **0.01×** | +| pp-macro `-E` | 132 µs/exp → **3.7 µs/exp** | 22.1× slower → **0.76× (1.3× faster)** | +| pp-include | 436 ms → **141 ms** | 1.76× slower → **0.73× (1.4× faster)** | +| type-decl | 77 ms → **72 ms** | 0.62× → 0.67× | +| symbol-count | (link) 287 → 302 ms | 1.45× (unchanged — pure linker) | + +Allocation is now ~0 per input unit (mallocs scale with arena *blocks*, not +items); IO is at the buffered ideal (output_bytes / 64 KB writes): + +| axis | mallocs/unit before → after | | write() syscalls before → after | +|------|:---:|---|:---:| +| body-size (128k stmt) | ~26 (3.3 M total) → **0.007** | obj 9.2 MB | ~thousands → **4** | +| pp-macro `-E` (128k) | — → **0.15** | out 9.6 MB | **millions → 148** | +| fn / global / ref | 1–2 → **< 0.07** | | | + +**Fixes (all landed, output byte-identical, ASan/UBSan-clean):** + +1. **Throwaway type-lowering pool** (`lang/c`). `c_abi_type_info/sizeof/alignof` + called `type_cg_id`, which spun up and tore down a whole `Pool` (64 KB + arena) **per query** — every integer literal / `sizeof` / arithmetic + conversion. That single site was the 50 %+ `mach_vm_reclaim` hotspot (1 M + 64 KB alloc+free pairs for one 128 k-stmt function). Routed the queries + through the parser's persistent `p->pool` (matching `cg_adapter`), so + builtins allocate nothing and records memoize. Removed `type_cg_id`. +2. **Buffered output writer** (`driver/env/posix.c`). `fdw_write` did one + `write()` syscall per `KitWriter::write` — and `pp_emit_text` writes once + per token. Added a 64 KB buffer with flush-on-seek (object emit seeks to + patch headers) and large-write passthrough. pp-macro `-E` went from millions + of syscalls to 148. +3. **Per-token lexer scratch** (`lang/cpp/lex/lex.c`). The pp-number path + malloc'd a temp buffer per numeric token (splice removal); identifiers + already had a no-splice fast path. Added the same fast path for numbers and + a grow-once, high-water `Lexer.scratch` so even the (rare) spliced-token slow + path never allocates per token. 256 k tiny mallocs → ~0. +4. **Linkage-name concat** (`src/api/core.c`). `kit_cg_c_linkage_name` malloc'd + a temp buffer per declared symbol on Mach-O (to prepend `_`); now a 256-byte + stack buffer, heap only for pathologically long names. +5. **Shared CFI directive vector** (`src/arch/mc.c`). Each `CfiFde` owned a + separately heap-allocated `directives` array (a per-function 128-byte + malloc). Replaced with one growable vector on `MCImpl`; each FDE keeps a + `(dir_start, ndir)` range — single-pass emission keeps them contiguous. + +**Next candidate.** With churn + IO gone, pp-macro `-E` is now CPU-bound on the +token machinery (`pp_next_raw`/`subst_phase2`/`lex_next`); the #2 self-time +frame is `arena_fini` — the one-time teardown freeing the accumulated working +set (hundreds of 64 KB blocks back to libc → `mach_vm_reclaim`). A heap-level +block free-list (recycle arena-sized blocks instead of returning them to +libmalloc) would erase that teardown cost and any residual block churn +systemically. + +### Round 1 — linearity (all superlinear axes fixed) + > 2026-06-10, M1 (8-core), clang-built `PROFILE=1` release kit, best-of-3. > **All superlinear axes have been fixed** — every axis is now LINEAR. The table > shows the exponent before → after the fixes below, and kit's speed vs clang diff --git a/driver/env/posix.c b/driver/env/posix.c @@ -153,18 +153,26 @@ KitExecMem g_execmem_posix; /* page_size set in driver_env_init */ /* ---------------- fd writer ---------------- */ +/* Output is buffered: object emit and `-E` text both drive the writer in many + * tiny chunks (a token, a 16-byte nlist entry), and one write() syscall per + * chunk dominated compile/link wall time. The buffer coalesces them into + * page-sized flushes. Invariant: the buffer holds a contiguous run ending at + * the logical position `pos`, so the fd's real offset is always pos-buf_len; + * every seek flushes first to keep that true. */ +#define FDW_BUF_CAP 65536u + typedef struct DriverFdWriter { KitWriter base; /* must be first; libkit reads via this */ KitHeap* heap; int fd; KitStatus status; - uint64_t pos; + uint64_t pos; /* logical position of the next byte (incl. buffered) */ + size_t buf_len; /* bytes buffered but not yet written to fd */ + unsigned char buf[FDW_BUF_CAP]; } DriverFdWriter; -static KitStatus fdw_write(KitWriter* w, const void* data, size_t n) { - DriverFdWriter* fw = (DriverFdWriter*)w; - const unsigned char* p = (const unsigned char*)data; - if (fw->status != KIT_OK) return fw->status; +/* Drain `data`/`n` straight to the fd (no buffering, no pos accounting). */ +static KitStatus fdw_raw(DriverFdWriter* fw, const unsigned char* p, size_t n) { while (n > 0) { ssize_t k = write(fw->fd, p, n); if (k < 0) { @@ -173,14 +181,43 @@ static KitStatus fdw_write(KitWriter* w, const void* data, size_t n) { } p += (size_t)k; n -= (size_t)k; - fw->pos += (uint64_t)k; } return KIT_OK; } +static KitStatus fdw_flush(DriverFdWriter* fw) { + size_t n = fw->buf_len; + if (n == 0) return fw->status; + fw->buf_len = 0; + return fdw_raw(fw, fw->buf, n); +} + +static KitStatus fdw_write(KitWriter* w, const void* data, size_t n) { + DriverFdWriter* fw = (DriverFdWriter*)w; + const unsigned char* p = (const unsigned char*)data; + if (fw->status != KIT_OK) return fw->status; + if (n == 0) return KIT_OK; + /* Large writes bypass the buffer: flush what is pending, then stream the + * payload directly so a multi-MB section never round-trips through buf. */ + if (n >= FDW_BUF_CAP) { + if (fdw_flush(fw) != KIT_OK) return fw->status; + if (fdw_raw(fw, p, n) != KIT_OK) return fw->status; + fw->pos += (uint64_t)n; + return KIT_OK; + } + if (fw->buf_len + n > FDW_BUF_CAP) { + if (fdw_flush(fw) != KIT_OK) return fw->status; + } + memcpy(fw->buf + fw->buf_len, p, n); + fw->buf_len += n; + fw->pos += (uint64_t)n; + return KIT_OK; +} + static KitStatus fdw_seek(KitWriter* w, uint64_t off) { DriverFdWriter* fw = (DriverFdWriter*)w; if (fw->status != KIT_OK) return fw->status; + if (fdw_flush(fw) != KIT_OK) return fw->status; if (lseek(fw->fd, (off_t)off, SEEK_SET) < 0) { fw->status = KIT_IO; return KIT_IO; @@ -196,6 +233,7 @@ static KitStatus fdw_status(KitWriter* w) { static void fdw_close(KitWriter* w) { DriverFdWriter* fw = (DriverFdWriter*)w; + fdw_flush(fw); if (fw->fd >= 0) close(fw->fd); fw->heap->free(fw->heap, fw, sizeof(*fw)); } @@ -213,6 +251,7 @@ static KitWriter* driver_writer_fd(KitHeap* h, int fd) { fw->fd = fd; fw->status = KIT_OK; fw->pos = 0; + fw->buf_len = 0; return &fw->base; } diff --git a/lang/c/abi/c_abi.c b/lang/c/abi/c_abi.c @@ -2,8 +2,8 @@ #include <string.h> -ABITypeInfo c_abi_type_info(KitCompiler* a, const Type* t) { - KitCgTypeId id = type_cg_id(a, t); +ABITypeInfo c_abi_type_info(KitCompiler* a, Pool* p, const Type* t) { + KitCgTypeId id = type_cg_id_in_pool(a, p, t); KitCgTypeKind kind = kit_cg_type_kind(a, id); ABITypeInfo r; memset(&r, 0, sizeof(r)); @@ -34,12 +34,12 @@ ABITypeInfo c_abi_type_info(KitCompiler* a, const Type* t) { return r; } -u32 c_abi_sizeof(KitCompiler* a, const Type* t) { - return c_abi_type_info(a, t).size; +u32 c_abi_sizeof(KitCompiler* a, Pool* p, const Type* t) { + return c_abi_type_info(a, p, t).size; } -u32 c_abi_alignof(KitCompiler* a, const Type* t) { - return c_abi_type_info(a, t).align; +u32 c_abi_alignof(KitCompiler* a, Pool* p, const Type* t) { + return c_abi_type_info(a, p, t).align; } const ABIRecordLayout* c_abi_record_layout(KitCompiler* a, Pool* p, diff --git a/lang/c/abi/c_abi.h b/lang/c/abi/c_abi.h @@ -44,9 +44,12 @@ typedef struct ABIFuncInfo { u16 nparams; } ABIFuncInfo; -ABITypeInfo c_abi_type_info(KitCompiler*, const Type*); -u32 c_abi_sizeof(KitCompiler*, const Type*); -u32 c_abi_alignof(KitCompiler*, const Type*); +/* Scalar/size/align queries lower the type to a CG id through the caller's + * Pool so the lowering scratch (and record-layout memo) is reused for the + * whole translation unit -- never a throwaway per-query arena. */ +ABITypeInfo c_abi_type_info(KitCompiler*, Pool*, const Type*); +u32 c_abi_sizeof(KitCompiler*, Pool*, const Type*); +u32 c_abi_alignof(KitCompiler*, Pool*, const Type*); const ABIRecordLayout* c_abi_record_layout(KitCompiler*, Pool*, const Type*); const ABIFuncInfo* c_abi_func_info(KitCompiler*, Pool*, const Type*); diff --git a/lang/c/parse/cg_adapter.c b/lang/c/parse/cg_adapter.c @@ -658,8 +658,8 @@ void pcg_store(Parser* p) { int trivial = pcg_lv_is_trivial_local(lv); memset(&fsd, 0, sizeof fsd); fsd.type = rv_ty; - fsd.size = c_abi_sizeof(p->abi, rv_ty); - fsd.align = c_abi_alignof(p->abi, rv_ty); + fsd.size = c_abi_sizeof(p->abi, p->pool, rv_ty); + fsd.align = c_abi_alignof(p->abi, p->pool, rv_ty); fsd.kind = FS_LOCAL; tmp = pcg_local(p, &fsd); kit_cg_push_local(p->cg, tmp); /* [base.., value, &tmp] */ @@ -942,7 +942,7 @@ void pcg_inc_dec(Parser* p, BinOp op, int post) { const Type* pointee = ty->ptr.pointee; if (pointee && pointee->kind == TY_VOID) perr(p, "pointer arithmetic on void pointer"); - step = c_abi_sizeof(p->abi, pointee); + step = c_abi_sizeof(p->abi, p->pool, pointee); step_ty = c_abi_ptrdiff_type(p->abi, p->pool); } /* Materialize the lvalue to a single destination pointer so its address can @@ -955,8 +955,8 @@ void pcg_inc_dec(Parser* p, BinOp op, int post) { KitCgMemAccess r_access = pcg_mem(p, result_ty); memset(&fsd, 0, sizeof fsd); fsd.type = result_ty; - fsd.size = c_abi_sizeof(p->abi, result_ty); - fsd.align = c_abi_alignof(p->abi, result_ty); + fsd.size = c_abi_sizeof(p->abi, p->pool, result_ty); + fsd.align = c_abi_alignof(p->abi, p->pool, result_ty); fsd.kind = FS_LOCAL; tmp = pcg_local(p, &fsd); kit_cg_dup(p->cg); /* [ptr, ptr] */ diff --git a/lang/c/parse/parse.c b/lang/c/parse/parse.c @@ -510,12 +510,12 @@ FrameSlot make_local_aligned(Parser* p, Sym name, const Type* type, SrcLoc loc, FrameSlotDesc fsd; FrameSlot s; SymEntry* e; - u32 nat = c_abi_alignof(p->abi, type); + u32 nat = c_abi_alignof(p->abi, p->pool, type); memset(&fsd, 0, sizeof fsd); fsd.type = type; fsd.name = name; fsd.loc = loc; - fsd.size = c_abi_sizeof(p->abi, type); + fsd.size = c_abi_sizeof(p->abi, p->pool, type); fsd.align = (align_override > nat) ? align_override : nat; fsd.kind = FS_LOCAL; fsd.flags = FSF_NONE; @@ -535,8 +535,8 @@ static FrameSlot make_vla_size_slot(Parser* p) { FrameSlotDesc fsd; memset(&fsd, 0, sizeof fsd); fsd.type = ty_size_t(p); - fsd.size = c_abi_sizeof(p->abi, fsd.type); - fsd.align = c_abi_alignof(p->abi, fsd.type); + fsd.size = c_abi_sizeof(p->abi, p->pool, fsd.type); + fsd.align = c_abi_alignof(p->abi, p->pool, fsd.type); fsd.kind = FS_LOCAL; return pcg_local(p, &fsd); } @@ -569,7 +569,7 @@ static int build_vla_size(Parser* p, const Type* ty, u32* count_idx, VLABound** bounds, FrameSlot* out_slot, u32* out_static_size, SrcLoc loc) { if (!ty || ty->kind != TY_ARRAY) { - *out_static_size = c_abi_sizeof(p->abi, ty); + *out_static_size = c_abi_sizeof(p->abi, p->pool, ty); *out_slot = FRAME_SLOT_NONE; return 0; } @@ -1272,8 +1272,8 @@ static void parse_function_body(Parser* p, ObjSymId fsym, const Type* fn_ty, fsd.type = infos[i].type; fsd.name = infos[i].name; fsd.loc = infos[i].loc; - fsd.size = c_abi_sizeof(p->abi, infos[i].type); - fsd.align = c_abi_alignof(p->abi, infos[i].type); + fsd.size = c_abi_sizeof(p->abi, p->pool, infos[i].type); + fsd.align = c_abi_alignof(p->abi, p->pool, infos[i].type); fsd.kind = FS_PARAM; fsd.flags = FSF_NONE; s = pcg_param_slot(p, i, &fsd); diff --git a/lang/c/parse/parse_expr.c b/lang/c/parse/parse_expr.c @@ -424,7 +424,7 @@ u8* decode_string_literal(Parser* p, const Tok* t, size_t* nlen_out) { else if (t->flags & (TF_STR_WIDE | TF_STR_U16 | TF_STR_U32)) i = 1; elem_ty = string_literal_elem_type(p, t); - elem_size = c_abi_sizeof(p->abi, elem_ty); + elem_size = c_abi_sizeof(p->abi, p->pool, elem_ty); if (i >= len || s[i] != '"') perr(p, "malformed string literal"); i++; buf = (u8*)h->alloc(h, (len + 1u) * elem_size, 1); @@ -456,10 +456,10 @@ KitCgSym emit_string_to_rodata(Parser* p, const u8* bytes, size_t n) { KitCgSym emit_string_literal_to_rodata(Parser* p, const u8* bytes, size_t nbytes, const Type* elem_ty) { - u32 elem_size = c_abi_sizeof(p->abi, elem_ty); + u32 elem_size = c_abi_sizeof(p->abi, p->pool, elem_ty); u32 count = elem_size ? (u32)(nbytes / elem_size) : 0; const Type* arr_ty = type_array(p->pool, elem_ty, count, 0); - return kit_cg_const_data(p->cg, bytes, nbytes, c_abi_alignof(p->abi, elem_ty), + return kit_cg_const_data(p->cg, bytes, nbytes, c_abi_alignof(p->abi, p->pool, elem_ty), pcg_tid(p, arr_ty)); } @@ -474,7 +474,7 @@ static const Type* common_fp_type(Parser* p, const Type* a, const Type* b); static void coerce_fp_cmp_operands(Parser* p, const Type* common); static u32 cint_bits(Parser* p, const Type* ty) { - u32 sz = ty ? c_abi_sizeof(p->abi, ty) : 8u; + u32 sz = ty ? c_abi_sizeof(p->abi, p->pool, ty) : 8u; if (ty && (ty->kind == TY_INT128 || ty->kind == TY_UINT128)) return 128; if (sz >= 8) return 64; return sz * 8u; @@ -482,7 +482,7 @@ static u32 cint_bits(Parser* p, const Type* ty) { static int cint_signed(Parser* p, const Type* ty) { if (!ty) return 1; - return c_abi_type_info(p->abi, ty).signed_ != 0; + return c_abi_type_info(p->abi, p->pool, ty).signed_ != 0; } static void cint_mask_to_bits(CConstInt* v, u32 bits) { @@ -892,7 +892,7 @@ static CConstInt cexpr_unary(Parser* p, SrcLoc loc) { const Type* t = parse_type_name(p); expect_punct(p, ')', "')' after sizeof type-name"); require_sizeof_type(p, t); - return cint_make_u64(p, ty_size_t(p), c_abi_sizeof(p->abi, t)); + return cint_make_u64(p, ty_size_t(p), c_abi_sizeof(p->abi, p->pool, t)); } } } @@ -901,7 +901,7 @@ static CConstInt cexpr_unary(Parser* p, SrcLoc loc) { const Type* ty = pcg_top_type(p); if (pcg_top_is_bitfield(p)) perr(p, "sizeof bit-field"); require_sizeof_type(p, ty); - i64 sz = (i64)c_abi_sizeof(p->abi, ty); + i64 sz = (i64)c_abi_sizeof(p->abi, p->pool, ty); pcg_drop(p); return cint_make_u64(p, ty_size_t(p), (u64)sz); } @@ -914,14 +914,14 @@ static CConstInt cexpr_unary(Parser* p, SrcLoc loc) { { const Type* t = parse_type_name(p); expect_punct(p, ')', "')' after _Alignof type-name"); - return cint_make_u64(p, ty_size_t(p), c_abi_alignof(p->abi, t)); + return cint_make_u64(p, ty_size_t(p), c_abi_alignof(p->abi, p->pool, t)); } } } parse_unary(p); { const Type* ty = pcg_top_type(p); - i64 al = (i64)c_abi_alignof(p->abi, ty); + i64 al = (i64)c_abi_alignof(p->abi, p->pool, ty); pcg_drop(p); return cint_make_u64(p, ty_size_t(p), (u64)al); } @@ -1103,7 +1103,7 @@ static const Type* atomic_lock_free_type_for_size(Parser* p, i64 size) { default: return NULL; } - return c_abi_sizeof(p->abi, ty) == (u32)size ? ty : NULL; + return c_abi_sizeof(p->abi, p->pool, ty) == (u32)size ? ty : NULL; } static int atomic_lock_free_for_const_size(Parser* p, i64 size) { @@ -1331,7 +1331,7 @@ static const Type* offsetof_designator(Parser* p, const Type* base, u32* off) { if (cur->kind != TY_ARRAY) { perr(p, "__builtin_offsetof '[' on non-array"); } - *off += (u32)((i64)c_abi_sizeof(p->abi, cur->arr.elem) * idx); + *off += (u32)((i64)c_abi_sizeof(p->abi, p->pool, cur->arr.elem) * idx); cur = cur->arr.elem; continue; } @@ -1387,8 +1387,8 @@ static FrameSlot builtin_tmp_slot(Parser* p, const Type* ty) { FrameSlotDesc fsd; memset(&fsd, 0, sizeof fsd); fsd.type = ty; - fsd.size = c_abi_sizeof(p->abi, ty); - fsd.align = c_abi_alignof(p->abi, ty); + fsd.size = c_abi_sizeof(p->abi, p->pool, ty); + fsd.align = c_abi_alignof(p->abi, p->pool, ty); fsd.kind = FS_LOCAL; return pcg_local(p, &fsd); } @@ -1956,8 +1956,8 @@ static int try_parse_builtin_call(Parser* p) { FrameSlotDesc fsd; memset(&fsd, 0, sizeof fsd); fsd.type = eptr_ty; - fsd.size = c_abi_sizeof(p->abi, eptr_ty); - fsd.align = c_abi_alignof(p->abi, eptr_ty); + fsd.size = c_abi_sizeof(p->abi, p->pool, eptr_ty); + fsd.align = c_abi_alignof(p->abi, p->pool, eptr_ty); fsd.kind = FS_LOCAL; FrameSlot eslot = pcg_local(p, &fsd); pcg_push_local_typed(p, eslot, eptr_ty); @@ -1990,8 +1990,8 @@ static int try_parse_builtin_call(Parser* p) { FrameSlotDesc okd; memset(&okd, 0, sizeof okd); okd.type = ok_ty; - okd.size = c_abi_sizeof(p->abi, ok_ty); - okd.align = c_abi_alignof(p->abi, ok_ty); + okd.size = c_abi_sizeof(p->abi, p->pool, ok_ty); + okd.align = c_abi_alignof(p->abi, p->pool, ok_ty); okd.kind = FS_LOCAL; FrameSlot okslot = pcg_local(p, &okd); pcg_push_local_typed(p, okslot, ok_ty); @@ -2002,8 +2002,8 @@ static int try_parse_builtin_call(Parser* p) { FrameSlotDesc pd; memset(&pd, 0, sizeof pd); pd.type = val_ty; - pd.size = c_abi_sizeof(p->abi, val_ty); - pd.align = c_abi_alignof(p->abi, val_ty); + pd.size = c_abi_sizeof(p->abi, p->pool, val_ty); + pd.align = c_abi_alignof(p->abi, p->pool, val_ty); pd.kind = FS_LOCAL; FrameSlot pslot = pcg_local(p, &pd); pcg_push_local_typed(p, pslot, val_ty); @@ -2177,7 +2177,7 @@ static void parse_primary(Parser* p) { size_t n = 0; u8* bytes = decode_string_literal(p, &t, &n); const Type* elem_ty = string_literal_elem_type(p, &t); - u32 elem_size = c_abi_sizeof(p->abi, elem_ty); + u32 elem_size = c_abi_sizeof(p->abi, p->pool, elem_ty); ObjSymId sym = emit_string_literal_to_rodata(p, bytes, n, elem_ty); kit_compiler_context(p->c)->heap->free(kit_compiler_context(p->c)->heap, bytes, 0); @@ -2356,7 +2356,7 @@ static void parse_postfix(Parser* p) { p->last_pushed_vla_slot = elem_vla_slot; p->last_pushed_vla_bounds = vla_bounds; } else { - u32 elem_size = c_abi_sizeof(p->abi, elem); + u32 elem_size = c_abi_sizeof(p->abi, p->pool, elem); pcg_lv_subscript(p, elem_size, elem); } } @@ -2440,8 +2440,8 @@ void parse_unary(Parser* p) { } memset(&fsd, 0, sizeof fsd); fsd.type = lit_ty; - fsd.size = c_abi_sizeof(p->abi, lit_ty); - fsd.align = c_abi_alignof(p->abi, lit_ty); + fsd.size = c_abi_sizeof(p->abi, p->pool, lit_ty); + fsd.align = c_abi_alignof(p->abi, p->pool, lit_ty); fsd.kind = FS_LOCAL; fsd.flags = FSF_NONE; slot = pcg_local(p, &fsd); @@ -2594,7 +2594,7 @@ void parse_unary(Parser* p) { pcg_load(p); } else { require_sizeof_type(p, ty); - pcg_push_int(p, (i64)c_abi_sizeof(p->abi, ty), ty_size_t(p)); + pcg_push_int(p, (i64)c_abi_sizeof(p->abi, p->pool, ty), ty_size_t(p)); } return; } @@ -2767,7 +2767,7 @@ void parse_unary(Parser* p) { pcg_drop(p); } expect_punct(p, ')', "')'"); - pcg_push_int(p, (i64)c_abi_alignof(p->abi, ty), ty_size_t(p)); + pcg_push_int(p, (i64)c_abi_alignof(p->abi, p->pool, ty), ty_size_t(p)); return; } parse_postfix(p); @@ -2919,7 +2919,7 @@ static void emit_add_or_sub(Parser* p, BinOp bop) { if (l_is_ptr && type_is_int(rt)) { if (lt->ptr.pointee && lt->ptr.pointee->kind == TY_VOID) perr(p, "pointer arithmetic on void pointer"); - u32 esz = c_abi_sizeof(p->abi, lt->ptr.pointee); + u32 esz = c_abi_sizeof(p->abi, p->pool, lt->ptr.pointee); scale_pointer_index(p, esz); pcg_binop(p, BO_IADD); return; @@ -2928,7 +2928,7 @@ static void emit_add_or_sub(Parser* p, BinOp bop) { if (rt->ptr.pointee && rt->ptr.pointee->kind == TY_VOID) perr(p, "pointer arithmetic on void pointer"); pcg_swap(p); - u32 esz = c_abi_sizeof(p->abi, rt->ptr.pointee); + u32 esz = c_abi_sizeof(p->abi, p->pool, rt->ptr.pointee); scale_pointer_index(p, esz); pcg_binop(p, BO_IADD); return; @@ -2937,7 +2937,7 @@ static void emit_add_or_sub(Parser* p, BinOp bop) { if (l_is_ptr && type_is_int(rt)) { if (lt->ptr.pointee && lt->ptr.pointee->kind == TY_VOID) perr(p, "pointer arithmetic on void pointer"); - u32 esz = c_abi_sizeof(p->abi, lt->ptr.pointee); + u32 esz = c_abi_sizeof(p->abi, p->pool, lt->ptr.pointee); scale_pointer_index(p, esz); pcg_binop(p, BO_ISUB); return; @@ -2946,7 +2946,7 @@ static void emit_add_or_sub(Parser* p, BinOp bop) { if (!pointer_pointees_compatible(p, lt, rt)) { perr(p, "subtraction of incompatible pointer types"); } - u32 esz = c_abi_sizeof(p->abi, lt->ptr.pointee); + u32 esz = c_abi_sizeof(p->abi, p->pool, lt->ptr.pointee); pcg_binop(p, BO_ISUB); if (esz != 1) { pcg_push_int(p, (i64)esz, ty_size_t(p)); @@ -3011,7 +3011,7 @@ static void parse_shift(Parser* p) { const Type* lp = cint_promote_type(p, lt); if (!type_is_int(lt)) perr(p, "shift operator requires integer operands"); if (pcg_top_type(p) != lp) pcg_convert(p, lp); - if (bop == BO_SHR_S && !c_abi_type_info(p->abi, lp).signed_) + if (bop == BO_SHR_S && !c_abi_type_info(p->abi, p->pool, lp).signed_) bop = BO_SHR_U; } parse_add(p); @@ -3194,8 +3194,8 @@ static FrameSlot ll_tmp_slot(Parser* p, const Type* ty) { FrameSlotDesc fsd; memset(&fsd, 0, sizeof fsd); fsd.type = ty; - fsd.size = c_abi_sizeof(p->abi, ty); - fsd.align = c_abi_alignof(p->abi, ty); + fsd.size = c_abi_sizeof(p->abi, p->pool, ty); + fsd.align = c_abi_alignof(p->abi, p->pool, ty); fsd.kind = FS_LOCAL; fsd.flags = FSF_NONE; return pcg_local(p, &fsd); @@ -3323,8 +3323,8 @@ static void parse_ternary(Parser* p) { then_null ? type_ptr(p->pool, type_prim(p->pool, TY_VOID)) : result_ty; memset(&fsd, 0, sizeof fsd); fsd.type = then_store_ty; - fsd.size = c_abi_sizeof(p->abi, then_store_ty); - fsd.align = c_abi_alignof(p->abi, then_store_ty); + fsd.size = c_abi_sizeof(p->abi, p->pool, then_store_ty); + fsd.align = c_abi_alignof(p->abi, p->pool, then_store_ty); fsd.kind = FS_LOCAL; fsd.flags = FSF_NONE; tmp = pcg_local(p, &fsd); @@ -3365,8 +3365,8 @@ static void parse_ternary(Parser* p) { FrameSlot ctmp; memset(&cfsd, 0, sizeof cfsd); cfsd.type = common; - cfsd.size = c_abi_sizeof(p->abi, common); - cfsd.align = c_abi_alignof(p->abi, common); + cfsd.size = c_abi_sizeof(p->abi, p->pool, common); + cfsd.align = c_abi_alignof(p->abi, p->pool, common); cfsd.kind = FS_LOCAL; cfsd.flags = FSF_NONE; ctmp = pcg_local(p, &cfsd); @@ -3429,7 +3429,7 @@ void parse_assign_expr(Parser* p) { } advance(p); const Type* lhs = pcg_top_type(p); - if (compound == BO_SHR_S && !c_abi_type_info(p->abi, lhs).signed_) + if (compound == BO_SHR_S && !c_abi_type_info(p->abi, p->pool, lhs).signed_) compound = BO_SHR_U; { if (lhs && (lhs->qual & Q_CONST)) { diff --git a/lang/c/parse/parse_init.c b/lang/c/parse/parse_init.c @@ -181,7 +181,7 @@ void zero_object_bytes_at(Parser* p, FrameSlot slot, const Type* arr_ty, push_subobject_lv(p, slot, arr_ty, offset, ty); pcg_addr(p); if (pcg_emit_enabled(p)) { - kit_cg_memset(p->cg, 0, c_abi_sizeof(p->abi, ty), access); + kit_cg_memset(p->cg, 0, c_abi_sizeof(p->abi, p->pool, ty), access); } pcg_drop_type(p); } @@ -241,7 +241,7 @@ static void emit_walk_copy(Parser* p, FrameSlot dst_slot, return; } if (ty->kind == TY_ARRAY) { - u32 esz = c_abi_sizeof(p->abi, ty->arr.elem); + u32 esz = c_abi_sizeof(p->abi, p->pool, ty->arr.elem); for (u32 i = 0; i < ty->arr.count; ++i) { emit_walk_copy(p, dst_slot, dst_arr_ty, dst_off + i * esz, src_ptr_slot, src_ptr_ty, src_off + i * esz, ty->arr.elem); @@ -249,7 +249,7 @@ static void emit_walk_copy(Parser* p, FrameSlot dst_slot, return; } if (ty->kind == TY_UNION) { - u32 sz = c_abi_sizeof(p->abi, ty); + u32 sz = c_abi_sizeof(p->abi, p->pool, ty); const Type* uchar_ty = type_prim(p->pool, TY_UCHAR); for (u32 i = 0; i < sz; ++i) { emit_copy_leaf(p, dst_slot, dst_arr_ty, dst_off + i, src_ptr_slot, @@ -273,8 +273,8 @@ void emit_struct_copy_into_slot(Parser* p, FrameSlot dst_slot, pcg_addr(p); memset(&fsd, 0, sizeof fsd); fsd.type = ptr_ty; - fsd.size = c_abi_sizeof(p->abi, ptr_ty); - fsd.align = c_abi_alignof(p->abi, ptr_ty); + fsd.size = c_abi_sizeof(p->abi, p->pool, ptr_ty); + fsd.align = c_abi_alignof(p->abi, p->pool, ptr_ty); fsd.kind = FS_LOCAL; fsd.flags = FSF_NONE; src_ptr_slot = pcg_local(p, &fsd); @@ -289,7 +289,7 @@ void emit_struct_copy_into_slot(Parser* p, FrameSlot dst_slot, void zero_init_at(Parser* p, FrameSlot slot, const Type* arr_ty, u32 offset, const Type* ty) { if (ty->kind == TY_ARRAY) { - u32 esz = c_abi_sizeof(p->abi, ty->arr.elem); + u32 esz = c_abi_sizeof(p->abi, p->pool, ty->arr.elem); for (u32 i = 0; i < ty->arr.count; ++i) { zero_init_at(p, slot, arr_ty, offset + i * esz, ty->arr.elem); } @@ -358,7 +358,7 @@ static void init_string_at(Parser* p, FrameSlot slot, const Type* arr_ty, u32 offset, const Type* elem_ty, u32 count) { size_t n = 0; u8* bytes = peek_string_bytes(p, &n); - u32 elem_size = c_abi_sizeof(p->abi, elem_ty); + u32 elem_size = c_abi_sizeof(p->abi, p->pool, elem_ty); size_t elems = elem_size ? n / elem_size : 0; size_t copy = elems; size_t i; @@ -407,7 +407,7 @@ static void parse_designator_chain(Parser* p, const Type* outer_ty, if (idx < 0 || (u32)idx >= cur_ty->arr.count) { perr(p, "array designator index out of range"); } - esz = c_abi_sizeof(p->abi, cur_ty->arr.elem); + esz = c_abi_sizeof(p->abi, p->pool, cur_ty->arr.elem); cur_off += (u32)idx * esz; cur_ty = cur_ty->arr.elem; cont.parent_ty = parent_ty; @@ -499,7 +499,7 @@ static int designator_continues_inside(Parser* p, const Type* outer_ty, if (cont->parent_ty == outer_ty && cont->parent_offset == outer_offset) return 0; if (!aggregate_has_index(cont->parent_ty, cont->next_index)) return 0; - top_size = c_abi_sizeof(p->abi, top_ty); + top_size = c_abi_sizeof(p->abi, p->pool, top_ty); return cont->parent_offset >= top_offset && cont->parent_offset - top_offset <= top_size; } @@ -602,7 +602,7 @@ static void init_aggregate_remainder(Parser* p, FrameSlot slot, const Type* arr_ty, u32 offset, const Type* ty, u32 start_index) { if (ty->kind == TY_ARRAY) { - u32 esz = c_abi_sizeof(p->abi, ty->arr.elem); + u32 esz = c_abi_sizeof(p->abi, p->pool, ty->arr.elem); u32 i; for (i = start_index; i < ty->arr.count; ++i) { init_at(p, slot, arr_ty, offset + i * esz, ty->arr.elem); @@ -648,7 +648,7 @@ void init_at(Parser* p, FrameSlot slot, const Type* arr_ty, u32 offset, const Type* ty) { if (ty->kind == TY_ARRAY) { const Type* elem_ty = ty->arr.elem; - u32 esz = c_abi_sizeof(p->abi, elem_ty); + u32 esz = c_abi_sizeof(p->abi, p->pool, elem_ty); if (p->cur.kind == TOK_STR && string_literal_initializes_array(p, elem_ty, &p->cur)) { init_string_at(p, slot, arr_ty, offset, elem_ty, ty->arr.count); @@ -943,7 +943,7 @@ static void parse_static_string_at(Parser* p, u8* buf, u32 buflen, u32 offset, const Type* elem_ty, u32 count) { size_t n = 0; u8* bytes = peek_string_bytes(p, &n); - u32 elem_size = c_abi_sizeof(p->abi, elem_ty); + u32 elem_size = c_abi_sizeof(p->abi, p->pool, elem_ty); size_t elems = elem_size ? n / elem_size : 0; size_t copy = elems; size_t copy_bytes; @@ -1091,7 +1091,7 @@ static CStaticConst parse_static_compound_literal_after_type(Parser* p, } static CConstInt int_bits_for_type(Parser* p, CConstInt v, const Type* ty) { - u32 sz = c_abi_sizeof(p->abi, ty); + u32 sz = c_abi_sizeof(p->abi, p->pool, ty); v.type = ty; if (sz < 8u) { u32 bits = sz * 8u; @@ -1116,12 +1116,12 @@ static void check_static_integer_initializer_range(Parser* p, const Type* ty, u32 bits; if (!dst || !type_is_int(dst) || dst->kind == TY_BOOL) return; if (dst->kind == TY_CHAR) return; - if (!c_abi_type_info(p->abi, dst).signed_) return; - bits = c_abi_sizeof(p->abi, dst) * 8u; + if (!c_abi_type_info(p->abi, p->pool, dst).signed_) return; + bits = c_abi_sizeof(p->abi, p->pool, dst) * 8u; if (bits < 64u) { i64 minv = -(1ll << (bits - 1u)); i64 maxv = (1ll << (bits - 1u)) - 1ll; - if (c_abi_type_info(p->abi, v.type).signed_) { + if (c_abi_type_info(p->abi, p->pool, v.type).signed_) { i64 sv = const_int_as_i64(p, v); if (sv < minv || sv > maxv) { perr(p, "initializer value overflows destination type"); @@ -1228,7 +1228,7 @@ static int try_parse_static_address_const(Parser* p, CStaticConst* out) { expect_punct(p, ']', "']' after array-subscript constant"); if (tgt_ty && tgt_ty->kind == TY_ARRAY) { byte_addend += - element_addend * (i64)c_abi_sizeof(p->abi, tgt_ty->arr.elem); + element_addend * (i64)c_abi_sizeof(p->abi, p->pool, tgt_ty->arr.elem); } else { byte_addend += element_addend; } @@ -1242,11 +1242,11 @@ static int try_parse_static_address_const(Parser* p, CStaticConst* out) { v = eval_const_int(p, cloc); if (neg) v = -v; if (tgt_ty && tgt_ty->kind == TY_ARRAY) { - byte_addend += v * (i64)c_abi_sizeof(p->abi, tgt_ty->arr.elem); + byte_addend += v * (i64)c_abi_sizeof(p->abi, p->pool, tgt_ty->arr.elem); } else if (tgt_ty && tgt_ty->kind == TY_PTR) { - byte_addend += v * (i64)c_abi_sizeof(p->abi, tgt_ty->ptr.pointee); + byte_addend += v * (i64)c_abi_sizeof(p->abi, p->pool, tgt_ty->ptr.pointee); } else if (saw_amp) { - byte_addend += v * (i64)c_abi_sizeof(p->abi, tgt_ty); + byte_addend += v * (i64)c_abi_sizeof(p->abi, p->pool, tgt_ty); } else { byte_addend += v; } @@ -1381,7 +1381,7 @@ static void parse_static_aggregate_remainder(Parser* p, u8* buf, u32 buflen, u32 start_index) { if (ty->kind == TY_ARRAY) { const Type* elem = ty->arr.elem; - u32 esz = c_abi_sizeof(p->abi, elem); + u32 esz = c_abi_sizeof(p->abi, p->pool, elem); u32 i; for (i = start_index; i < ty->arr.count; ++i) { parse_static_init_at(p, buf, buflen, offset + i * esz, elem); @@ -1440,7 +1440,7 @@ void parse_static_init_at(Parser* p, u8* buf, u32 buflen, u32 offset, } if (ty->kind == TY_ARRAY) { const Type* elem = ty->arr.elem; - u32 esz = c_abi_sizeof(p->abi, elem); + u32 esz = c_abi_sizeof(p->abi, p->pool, elem); u32 i = 0; int had_brace; if (p->cur.kind == TOK_STR && @@ -1580,7 +1580,7 @@ void parse_static_init_at(Parser* p, u8* buf, u32 buflen, u32 offset, { int had_brace = accept_punct(p, '{'); SrcLoc cloc = tok_loc_init(&p->cur); - u32 sz = c_abi_sizeof(p->abi, ty); + u32 sz = c_abi_sizeof(p->abi, p->pool, ty); CStaticConst cv; if (offset + sz > buflen) perr(p, "initializer overflows object"); if (try_parse_static_float(p, buf + offset, sz, ty)) { @@ -1661,8 +1661,8 @@ static void emit_static_data(Parser* p, const u8* buf, u32 size) { void define_static_object(Parser* p, ObjSymId sym, ObjSecId section_id, const Type* var_ty, u16 quals, int has_init, SrcLoc loc, u32 align_override) { - u32 size = c_abi_sizeof(p->abi, var_ty); - u32 align = c_abi_alignof(p->abi, var_ty); + u32 size = c_abi_sizeof(p->abi, p->pool, var_ty); + u32 align = c_abi_alignof(p->abi, p->pool, var_ty); KitCgDataDefAttrs attrs; if (align_override > align) align = align_override; u8* buf = NULL; diff --git a/lang/c/parse/parse_stmt.c b/lang/c/parse/parse_stmt.c @@ -457,8 +457,8 @@ static void parse_switch_stmt(Parser* p) { memset(&ctx, 0, sizeof ctx); memset(&fsd, 0, sizeof fsd); fsd.type = vty; - fsd.size = c_abi_sizeof(p->abi, vty); - fsd.align = c_abi_alignof(p->abi, vty); + fsd.size = c_abi_sizeof(p->abi, p->pool, vty); + fsd.align = c_abi_alignof(p->abi, p->pool, vty); fsd.kind = FS_LOCAL; ctx.value_slot = pcg_local(p, &fsd); ctx.value_type = vty; diff --git a/lang/c/parse/parse_type.c b/lang/c/parse/parse_type.c @@ -99,7 +99,7 @@ static const Type* attrs_apply_type_mode(Parser* p, const Type* base, if (attr_sym_canon_eq(p, a->v.sym, "TI")) { const Type* u = type_unqual(p->pool, base); int is_unsigned = - u && type_is_int(u) && c_abi_type_info(p->abi, u).signed_ == 0; + u && type_is_int(u) && c_abi_type_info(p->abi, p->pool, u).signed_ == 0; if (!target_has_int128(p)) { perr(p, "__int128 is not supported on the target architecture"); } @@ -513,7 +513,7 @@ void validate_decl_type_constraints(Parser* p, const DeclSpecs* specs, if (u->kind == TY_VOID || u->kind == TY_FUNC) { perr(p, "_Alignas requires an object type"); } - natural = c_abi_alignof(p->abi, ty); + natural = c_abi_alignof(p->abi, p->pool, ty); if (specs->align < natural) { perr(p, "_Alignas cannot weaken natural alignment"); } @@ -780,7 +780,7 @@ int parse_decl_specs(Parser* p, DeclSpecs* out) { expect_punct(p, '(', "'(' after _Alignas"); if (starts_type_name(p, &p->cur)) { const Type* tn = parse_type_name(p); - a = c_abi_alignof(p->abi, tn); + a = c_abi_alignof(p->abi, p->pool, tn); } else { i64 v = eval_const_int(p, tok_loc(&p->cur)); if (v < 0) perr(p, "_Alignas requires a non-negative alignment"); @@ -982,7 +982,7 @@ static void parse_member_decls(Parser* p, TypeRecordBuilder* b) { if (!type_is_int(specs.type)) perr(p, "bit-field has non-integer type"); i64 w = eval_const_int(p, mloc); if (w < 0) perr(p, "negative bit-field width"); - if (w > (i64)c_abi_sizeof(p->abi, specs.type) * 8) { + if (w > (i64)c_abi_sizeof(p->abi, p->pool, specs.type) * 8) { perr(p, "bit-field width exceeds its type width"); } f.name = 0; @@ -1006,7 +1006,7 @@ static void parse_member_decls(Parser* p, TypeRecordBuilder* b) { if (w < 0) perr(p, "negative bit-field width"); if (w == 0 && mname != 0) perr(p, "zero-width bit-field must be unnamed"); - if (w > (i64)c_abi_sizeof(p->abi, mty) * 8) { + if (w > (i64)c_abi_sizeof(p->abi, p->pool, mty) * 8) { perr(p, "bit-field width exceeds its type width"); } f.name = mname; @@ -1421,8 +1421,8 @@ int parse_decl_suffix(Parser* p, DeclSuffix* out) { out->vla = 1; memset(&fsd, 0, sizeof fsd); fsd.type = ty_size_t(p); - fsd.size = c_abi_sizeof(p->abi, fsd.type); - fsd.align = c_abi_alignof(p->abi, fsd.type); + fsd.size = c_abi_sizeof(p->abi, p->pool, fsd.type); + fsd.align = c_abi_alignof(p->abi, p->pool, fsd.type); fsd.kind = FS_LOCAL; out->vla_count_slot = pcg_local(p, &fsd); parse_assign_expr(p); @@ -1704,7 +1704,7 @@ const Type* complete_incomplete_array(Parser* p, const Type* ty) { Tok t = p->cur; size_t n = 0; u8* bytes = decode_string_literal(p, &t, &n); - u32 elem_size = c_abi_sizeof(p->abi, elem); + u32 elem_size = c_abi_sizeof(p->abi, p->pool, elem); kit_compiler_context(p->c)->heap->free(kit_compiler_context(p->c)->heap, bytes, 0); return type_array(p->pool, elem, elem_size ? (u32)(n / elem_size) : 0, @@ -1719,7 +1719,7 @@ const Type* complete_incomplete_array(Parser* p, const Type* ty) { Tok t = p->replay[1]; size_t n = 0; u8* bytes = decode_string_literal(p, &t, &n); - u32 elem_size = c_abi_sizeof(p->abi, elem); + u32 elem_size = c_abi_sizeof(p->abi, p->pool, elem); kit_compiler_context(p->c)->heap->free(kit_compiler_context(p->c)->heap, bytes, 0); cnt = elem_size ? (u32)(n / elem_size) : 0; diff --git a/lang/c/type/type.c b/lang/c/type/type.c @@ -717,11 +717,3 @@ KitCgTypeId type_cg_id_in_pool(KitCompiler* c, Pool* p, const Type* t) { return type_cg_lower(&l, t, TYPE_CG_VALUE); } -KitCgTypeId type_cg_id(KitCompiler* c, const Type* t) { - KitCgTypeId id; - Pool* p = c_pool_new(c); - if (!p) return KIT_CG_TYPE_NONE; - id = type_cg_id_in_pool(c, p, t); - c_pool_free(p); - return id; -} diff --git a/lang/c/type/type.h b/lang/c/type/type.h @@ -182,6 +182,5 @@ u32 type_kind_int_rank(TypeKind); TypeKind type_kind_unsigned_variant(TypeKind); KitCgTypeId type_cg_id_in_pool(KitCompiler*, Pool*, const Type*); -KitCgTypeId type_cg_id(KitCompiler*, const Type*); #endif diff --git a/lang/cpp/lex/lex.c b/lang/cpp/lex/lex.c @@ -32,8 +32,30 @@ struct Lexer { * 0 = none, 1 = saw pp-hash, 2 = saw `#include`/etc and the next * token may be a header-name. */ u8 dstate; + /* Grow-once scratch reused for per-token splice removal: a (rare) token + * containing a `\<newline>` packs its bytes here instead of a fresh heap + * block, so capacity sticks at the high-water mark and steady-state lexing + * never allocates per token. Contents are transient -- valid only until the + * next token. */ + char* scratch; + size_t scratch_cap; }; +/* At least `need` bytes of reusable scratch (see Lexer.scratch), growing + * geometrically and retaining capacity. NULL only on OOM. */ +static char* lex_scratch(Lexer* l, size_t need) { + if (need > l->scratch_cap) { + size_t cap = l->scratch_cap ? l->scratch_cap : 64; + char* p; + while (cap < need) cap *= 2; + p = (char*)l->heap->realloc(l->heap, l->scratch, l->scratch_cap, cap, 1); + if (!p) return NULL; + l->scratch = p; + l->scratch_cap = cap; + } + return l->scratch; +} + /* §5.1.1.2 translation phase 2: splice physical lines joined by * backslash-newline. Advance past any splice sequence at l->pos so the * cursor never rests on the leading backslash of a splice. */ @@ -148,6 +170,7 @@ Lexer* lex_open_mem(Compiler* c, const char* name, const char* src, void lex_close(Lexer* l) { if (!l) return; + if (l->scratch) l->heap->free(l->heap, l->scratch, l->scratch_cap); c_pool_free(l->pool); l->heap->free(l->heap, l, sizeof(*l)); } @@ -177,7 +200,6 @@ static Sym intern_spliced(Lexer* l, size_t start, size_t end) { int has_splice = 0; char* buf; size_t k; - Sym sym; for (i = start; i + 1 < end; ++i) { if (l->src[i] == '\\' && l->src[i + 1] == '\n') { @@ -189,7 +211,8 @@ static Sym intern_spliced(Lexer* l, size_t start, size_t end) { return kit_sym_intern(l->pool->c, (KitSlice){.s = l->src + start, .len = end - start}); - buf = (char*)l->heap->alloc(l->heap, end - start, 1); + buf = lex_scratch(l, end - start); + if (!buf) return 0; k = 0; for (i = start; i < end;) { if (i + 1 < end && l->src[i] == '\\' && l->src[i + 1] == '\n') { @@ -198,9 +221,7 @@ static Sym intern_spliced(Lexer* l, size_t start, size_t end) { } buf[k++] = l->src[i++]; } - sym = kit_sym_intern(l->pool->c, (KitSlice){.s = buf, .len = k}); - l->heap->free(l->heap, buf, end - start); - return sym; + return kit_sym_intern(l->pool->c, (KitSlice){.s = buf, .len = k}); } /* §6.4.7 header-name lookahead: in include-directive context, a `<` or `"` @@ -479,21 +500,42 @@ Tok lex_next(Lexer* l) { /* pp-number (§6.4.8), then classified to TOK_NUM / TOK_FLT. */ if (is_digit(ch) || (ch == '.' && is_digit(peek(l, 1)))) { size_t plen; - char* pbuf; + const char* text; size_t i, k; + int has_splice = 0; scan_pp_number(l); - /* Classify on the post-splice text (the spelling we'll intern). */ plen = l->pos - start; - pbuf = (char*)l->heap->alloc(l->heap, plen ? plen : 1, 1); - k = 0; - for (i = start; i < l->pos;) { - if (i + 1 < l->pos && l->src[i] == '\\' && l->src[i + 1] == '\n') { - i += 2; - continue; + /* The interned spelling is the post-splice text. The common case has no + * `\<newline>` inside the number, so its bytes are already contiguous in + * the source -- classify and intern straight from there with no copy. A + * splice packs into the lexer's reused scratch, never a per-token alloc. */ + for (i = start; i + 1 < l->pos; ++i) { + if (l->src[i] == '\\' && l->src[i + 1] == '\n') { + has_splice = 1; + break; + } + } + if (!has_splice) { + text = l->src + start; + k = plen; + } else { + char* sb = lex_scratch(l, plen ? plen : 1); + if (sb) { + k = 0; + for (i = start; i < l->pos;) { + if (i + 1 < l->pos && l->src[i] == '\\' && l->src[i + 1] == '\n') { + i += 2; + continue; + } + sb[k++] = l->src[i++]; + } + text = sb; + } else { + text = l->src + start; /* OOM: fall back to the raw source bytes */ + k = plen; } - pbuf[k++] = l->src[i++]; } - t.kind = (u16)(pp_number_is_float(pbuf, k) ? TOK_FLT : TOK_NUM); + t.kind = (u16)(pp_number_is_float(text, k) ? TOK_FLT : TOK_NUM); /* Suffix flags for §6.4.4.1 / §6.4.4.2. The parser dispatches on * TF_INT_U/L/LL and TF_FLT_F/L to pick a TY_* tag for the literal, * so missing flags would silently coerce `42U`/`42.0f` to plain @@ -501,7 +543,7 @@ Tok lex_next(Lexer* l) { if (t.kind == TOK_FLT) { size_t j = k; while (j > 0) { - char c = pbuf[j - 1]; + char c = text[j - 1]; if (c == 'f' || c == 'F') { t.flags |= TF_FLT_F; --j; @@ -517,14 +559,14 @@ Tok lex_next(Lexer* l) { } else { size_t j = k; while (j > 0) { - char c = pbuf[j - 1]; + char c = text[j - 1]; if (c == 'u' || c == 'U') { t.flags |= TF_INT_U; --j; continue; } if (c == 'l' || c == 'L') { - if (j >= 2 && (pbuf[j - 2] == 'l' || pbuf[j - 2] == 'L')) { + if (j >= 2 && (text[j - 2] == 'l' || text[j - 2] == 'L')) { t.flags |= TF_INT_LL; j -= 2; } else { @@ -536,8 +578,7 @@ Tok lex_next(Lexer* l) { break; } } - t.spelling = kit_sym_intern(l->pool->c, (KitSlice){.s = pbuf, .len = k}); - l->heap->free(l->heap, pbuf, plen ? plen : 1); + t.spelling = kit_sym_intern(l->pool->c, (KitSlice){.s = text, .len = k}); l->dstate = 0; return t; } diff --git a/src/api/core.c b/src/api/core.c @@ -231,6 +231,11 @@ KitSym kit_cg_c_linkage_name(KitCompiler* c, KitSym source_name) { const char* name; size_t len; char* buf; + char stackbuf[256]; /* C linkage names are short identifiers; this covers + * all but pathological cases without touching the heap. + * Called once per declared symbol, so a per-call malloc + * here scales with declaration count -- avoid it. */ + int heaped; KitSym out; Heap* h; Slice nslice; @@ -243,13 +248,14 @@ KitSym kit_cg_c_linkage_name(KitCompiler* c, KitSym source_name) { if (c->target.obj != KIT_OBJ_MACHO) return source_name; h = c->ctx->heap; - buf = (char*)h->alloc(h, len + 2u, 1); + heaped = len + 2u > sizeof stackbuf; + buf = heaped ? (char*)h->alloc(h, len + 2u, 1) : stackbuf; if (!buf) return 0; buf[0] = '_'; if (len) memcpy(buf + 1, name, len); buf[len + 1u] = '\0'; out = pool_intern_slice(c->global, (Slice){.s = buf, .len = (u32)(len + 1u)}); - h->free(h, buf, len + 2u); + if (heaped) h->free(h, buf, len + 2u); return out; } diff --git a/src/arch/mc.c b/src/arch/mc.c @@ -104,9 +104,8 @@ typedef struct CfiFde { u32 func_section; u32 func_start; u32 func_end; - CfiDirective* directives; + u32 dir_start; /* index of this FDE's first directive in MCImpl.dirs */ u32 ndir; - u32 dir_cap; } CfiFde; typedef struct MCImpl { @@ -122,6 +121,13 @@ typedef struct MCImpl { u32 nfdes; u32 fdes_cap; i32 cur_fde; + /* All FDEs' CFI directives share one growable vector; each FDE owns the + * contiguous range [dir_start, dir_start+ndir). Single-pass emission keeps a + * function's directives contiguous, so a function never costs its own heap + * block -- the vector doubles a handful of times for the whole object. */ + CfiDirective* dirs; + u32 ndirs; + u32 dirs_cap; u8 eh_frame_emitted; u8 has_pc_override; u8 pad_cfi[2]; @@ -393,17 +399,19 @@ static void fde_push(MCImpl* mc, u8 kind, u32 reg, i32 imm) { "MCEmitter: CFI directive in wrong section"); } heap = mc->base.c->ctx->heap; - if (fde->ndir == fde->dir_cap) { - u32 new_cap = fde->dir_cap ? fde->dir_cap * 2u : 8u; + /* The current FDE is always the last one, so its directives sit at the tail + * of the shared vector (dir_start + ndir == ndirs); append there. */ + if (mc->ndirs == mc->dirs_cap) { + u32 new_cap = mc->dirs_cap ? mc->dirs_cap * 2u : 64u; CfiDirective* nbuf = (CfiDirective*)heap->alloc( heap, sizeof(CfiDirective) * new_cap, _Alignof(CfiDirective)); if (!nbuf) compiler_panic(mc->base.c, mc->base.loc, "MCEmitter: CFI OOM"); - if (fde->directives) { - memcpy(nbuf, fde->directives, sizeof(CfiDirective) * fde->ndir); - heap->free(heap, fde->directives, sizeof(CfiDirective) * fde->dir_cap); + if (mc->dirs) { + memcpy(nbuf, mc->dirs, sizeof(CfiDirective) * mc->ndirs); + heap->free(heap, mc->dirs, sizeof(CfiDirective) * mc->dirs_cap); } - fde->directives = nbuf; - fde->dir_cap = new_cap; + mc->dirs = nbuf; + mc->dirs_cap = new_cap; } if (mc->has_pc_override) { /* Sticky until cfi_endproc: every directive in a func_end prologue batch @@ -415,7 +423,8 @@ static void fde_push(MCImpl* mc, u8 kind, u32 reg, i32 imm) { } else { pc_off = obj_pos(mc->base.obj, mc->base.section_id) - fde->func_start; } - d = &fde->directives[fde->ndir++]; + d = &mc->dirs[mc->ndirs++]; + fde->ndir++; d->pc_offset = pc_off; d->kind = kind; d->reg = reg; @@ -453,9 +462,8 @@ static void m_cfi_startproc(MCEmitter* m) { fde->func_section = m->section_id; fde->func_start = obj_pos(m->obj, m->section_id); fde->func_end = fde->func_start; - fde->directives = NULL; + fde->dir_start = mc->ndirs; fde->ndir = 0; - fde->dir_cap = 0; } } @@ -535,6 +543,9 @@ MCEmitter* mc_new(Compiler* c, ObjBuilder* o) { mc->nfdes = 0; mc->fdes_cap = 0; mc->cur_fde = -1; + mc->dirs = NULL; + mc->ndirs = 0; + mc->dirs_cap = 0; mc->eh_frame_emitted = 0; mc->has_pc_override = 0; mc->pc_override = 0; @@ -546,18 +557,17 @@ MCEmitter* mc_new(Compiler* c, ObjBuilder* o) { void mc_free(MCEmitter* m) { MCImpl* mc; Heap* heap; - u32 i; if (!m) return; mc = impl_of(m); /* Release any CFI directive buffers when the caller never invoked * mc_emit_eh_frame (e.g. test harness or early teardown). */ if (!mc->eh_frame_emitted && mc->fdes) { heap = m->c->ctx->heap; - for (i = 0; i < mc->nfdes; ++i) { - if (mc->fdes[i].directives) { - heap->free(heap, mc->fdes[i].directives, - sizeof(CfiDirective) * mc->fdes[i].dir_cap); - } + if (mc->dirs) { + heap->free(heap, mc->dirs, sizeof(CfiDirective) * mc->dirs_cap); + mc->dirs = NULL; + mc->dirs_cap = 0; + mc->ndirs = 0; } heap->free(heap, mc->fdes, sizeof(CfiFde) * mc->fdes_cap); mc->fdes = NULL; @@ -711,13 +721,11 @@ void mc_emit_eh_frame(MCEmitter* m) { * freebsd/wasi), which is unaffected and byte-identical. */ if (!m->c->target.emits_eh_frame) { heap = m->c->ctx->heap; - for (i = 0; i < mc->nfdes; ++i) { - if (mc->fdes[i].directives) { - heap->free(heap, mc->fdes[i].directives, - sizeof(CfiDirective) * mc->fdes[i].dir_cap); - mc->fdes[i].directives = NULL; - mc->fdes[i].dir_cap = 0; - } + if (mc->dirs) { + heap->free(heap, mc->dirs, sizeof(CfiDirective) * mc->dirs_cap); + mc->dirs = NULL; + mc->dirs_cap = 0; + mc->ndirs = 0; } if (mc->fdes) { heap->free(heap, mc->fdes, sizeof(CfiFde) * mc->fdes_cap); @@ -794,7 +802,7 @@ void mc_emit_eh_frame(MCEmitter* m) { buf_u32le(&body, fde->func_end - fde->func_start); /* range */ buf_uleb(&body, 0); /* aug_data_len = 0 */ for (j = 0; j < fde->ndir; ++j) { - encode_cfi_directive(&body, &fde->directives[j], &cur_loc, + encode_cfi_directive(&body, &mc->dirs[fde->dir_start + j], &cur_loc, arch->cfi_code_align_factor, arch->cfi_data_align_factor); } @@ -849,13 +857,11 @@ void mc_emit_eh_frame(MCEmitter* m) { buf_fini(&body); - for (i = 0; i < mc->nfdes; ++i) { - if (mc->fdes[i].directives) { - heap->free(heap, mc->fdes[i].directives, - sizeof(CfiDirective) * mc->fdes[i].dir_cap); - mc->fdes[i].directives = NULL; - mc->fdes[i].dir_cap = 0; - } + if (mc->dirs) { + heap->free(heap, mc->dirs, sizeof(CfiDirective) * mc->dirs_cap); + mc->dirs = NULL; + mc->dirs_cap = 0; + mc->ndirs = 0; } if (mc->fdes) { heap->free(heap, mc->fdes, sizeof(CfiFde) * mc->fdes_cap);