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