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