kit

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

commit 9bd45ea57728ff2c56b443b35beb4f1db8df27f2
parent 9a060c6c4f6e3d811e3d5d0a493a73625571062d
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Sat, 13 Jun 2026 11:47:49 -0700

perf(abi): fold c_abi_type_info into the per-id memo, route signed_ to type_is_signed_integer (B2)

(cherry picked from commit 19487cd1516bc54a6632ef4c958cecd801053629)

Diffstat:
Mlang/c/abi/c_abi.c | 76++++++++--------------------------------------------------------------------
Mlang/c/abi/c_abi.h | 25++++---------------------
Mlang/c/parse/parse_expr.c | 8+++++---
Mlang/c/parse/parse_init.c | 4++--
Mlang/c/parse/parse_type.c | 2+-
5 files changed, 20 insertions(+), 95 deletions(-)

diff --git a/lang/c/abi/c_abi.c b/lang/c/abi/c_abi.c @@ -2,84 +2,24 @@ #include <string.h> -/* Type -> ABITypeInfo memo. ABI facts (size/align/scalar kind/signedness) are a - * pure function of the C type + the fixed target, so once computed they never - * change — except a struct/union still incomplete, which we therefore never - * cache. The Pool is 1:1 with a compiler, so a plain Type* key suffices. - * Arena-backed (the pool arena-heap), in the pool's abi_cache slot, reclaimed - * wholesale at teardown. Collapses the per-query type_cg lowering + cg-API - * round-trips — a hot path: every sizeof/alignof/scalar probe — to one lookup. */ +/* Size/align of a C type are a pure function of the type + the fixed target. + * Both ride the per-id api_type_layout memo (and the cg_id cached on the Type + * node by type_cg_id_in_pool), so a direct kit_cg_type_size/align off the + * lowered id is already memoized — no second Type*-keyed cache is needed. + * Record layouts (the O(M accesses x N fields) cost) are still memoized on the + * Type node by c_abi_record_layout below. */ static int abi_info_cacheable(const Type* t) { /* Incomplete records have no stable layout yet; everything else (scalars, * pointers, arrays, functions, enums, complete records) is fixed. */ return !((t->kind == TY_STRUCT || t->kind == TY_UNION) && t->rec.incomplete); } -static inline u32 abi_type_ptr_hash(const Type* t) { - return kit_hash_u64((uint64_t)(uintptr_t)t); -} - -KIT_HASHMAP_DEFINE(AbiInfoMap, const Type*, ABITypeInfo, abi_type_ptr_hash); - -static AbiInfoMap* abi_cache_get(Pool* p) { - AbiInfoMap* m = (AbiInfoMap*)p->abi_cache; - if (m) return m; - m = arena_new(p->arena, AbiInfoMap); - if (!m) return NULL; - AbiInfoMap_init_cap(m, &p->arena_heap, 0); /* lazy: first insert allocates */ - p->abi_cache = m; - return m; -} - -ABITypeInfo c_abi_type_info(KitCompiler* a, Pool* p, const Type* t) { - AbiInfoMap* cache = NULL; - KitCgTypeId id; - KitCgTypeKind kind; - ABITypeInfo r; - if (abi_info_cacheable(t)) { - cache = abi_cache_get(p); - if (cache) { - ABITypeInfo* hit = AbiInfoMap_get(cache, t); - if (hit) return *hit; - } - } - id = type_cg_id_in_pool(a, p, t); - kind = kit_cg_type_kind(a, id); - memset(&r, 0, sizeof(r)); - r.size = (u32)kit_cg_type_size(a, id); - r.align = kit_cg_type_align(a, id); - switch (kind) { - case KIT_CG_TYPE_VOID: - r.scalar_kind = ABI_SC_VOID; - break; - case KIT_CG_TYPE_BOOL: - r.scalar_kind = ABI_SC_BOOL; - break; - case KIT_CG_TYPE_INT: - case KIT_CG_TYPE_ENUM: - r.scalar_kind = ABI_SC_INT; - break; - case KIT_CG_TYPE_FLOAT: - r.scalar_kind = ABI_SC_FLOAT; - break; - case KIT_CG_TYPE_PTR: - r.scalar_kind = ABI_SC_PTR; - break; - default: - r.scalar_kind = ABI_SC_VOID; - break; - } - r.signed_ = type_is_signed_integer(t); - if (cache) AbiInfoMap_set(cache, t, r); - return r; -} - u32 c_abi_sizeof(KitCompiler* a, Pool* p, const Type* t) { - return c_abi_type_info(a, p, t).size; + return (u32)kit_cg_type_size(a, type_cg_id_in_pool(a, p, t)); } u32 c_abi_alignof(KitCompiler* a, Pool* p, const Type* t) { - return c_abi_type_info(a, p, t).align; + return kit_cg_type_align(a, type_cg_id_in_pool(a, p, t)); } 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 @@ -4,23 +4,6 @@ #include "c_support.h" #include "type/type.h" -typedef enum ABIScalarKind { - ABI_SC_VOID, - ABI_SC_BOOL, - ABI_SC_INT, - ABI_SC_FLOAT, - ABI_SC_PTR, -} ABIScalarKind; - -typedef struct ABITypeInfo { - u32 size; - u32 align; - u8 scalar_kind; - u8 signed_; - u8 atomic; - u8 pad; -} ABITypeInfo; - typedef struct ABIFieldLayout { u32 offset; u16 bit_offset; @@ -44,10 +27,10 @@ typedef struct ABIFuncInfo { u16 nparams; } ABIFuncInfo; -/* 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*); +/* 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. The lowered id's size/align ride + * the per-id api_type_layout memo, so these need no extra Type*-keyed cache. */ 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*); diff --git a/lang/c/parse/parse_expr.c b/lang/c/parse/parse_expr.c @@ -477,8 +477,10 @@ static u32 cint_bits(Parser* p, const Type* ty) { } static int cint_signed(Parser* p, const Type* ty) { + /* signed_ is a pure Type predicate (no ABI lowering); read it directly. */ + (void)p; if (!ty) return 1; - return c_abi_type_info(p->abi, p->pool, ty).signed_ != 0; + return type_is_signed_integer(ty) != 0; } static void cint_mask_to_bits(CConstInt* v, u32 bits) { @@ -3066,7 +3068,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, p->pool, lp).signed_) + if (bop == BO_SHR_S && !type_is_signed_integer(lp)) bop = BO_SHR_U; } parse_add(p); @@ -3480,7 +3482,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, p->pool, lhs).signed_) + if (compound == BO_SHR_S && !type_is_signed_integer(lhs)) 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 @@ -1101,12 +1101,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, p->pool, dst).signed_) return; + if (!type_is_signed_integer(dst)) 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, p->pool, v.type).signed_) { + if (type_is_signed_integer(v.type)) { i64 sv = const_int_as_i64(p, v); if (sv < minv || sv > maxv) { perr(p, "initializer value overflows destination type"); 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, p->pool, u).signed_ == 0; + u && type_is_int(u) && type_is_signed_integer(u) == 0; if (!target_has_int128(p)) { perr(p, "__int128 is not supported on the target architecture"); }