commit fb17391cb69f024c19807c67bda922243b50d655
parent 6d3a2b1b89cd31db1855b9a57a11abeb1602c9d1
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Sat, 13 Jun 2026 10:00:59 -0700
perf(cg): -O0 memoize builtin predicate bitsets (kill api_type_pred reclassify)
api_type_pred's builtin fast path re-derived the predicate bitset on EVERY
call via cg_type_get + api_pred_bits_for_kind. Builtins (int/char/long/
double/...) are the most-frequent operand types, so this reclassify dominated
the 66.8M-Ir api_type_pred hub the prior wave's O(1) descriptor surfaced.
Precompute a builtin_pred[] bitset at builtin init (mirroring builtin_class[]),
and have the predicate query read it with one indexed load. Builtins are never
aliases, so the bitset is the exact, final value; semantics are bit-identical
to the old api_pred_bits_for_kind(cg_type_get(...)) path.
Gate: full byte-identity gate PASS; test-cg-api 211/0.
Diffstat:
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/src/cg/type.c b/src/cg/type.c
@@ -106,6 +106,11 @@ typedef struct CgApiState {
/* Packed API_TYPE_CLASS_* per builtin, precomputed at init so the hottest
* operand types (i32/i64/f64/ptr/...) classify with a single indexed load. */
u8 builtin_class[KIT_CG_BUILTIN_COUNT];
+ /* Packed API_PRED_* bitset per builtin, precomputed at init so the (very hot)
+ * cg_type_is_* predicate queries on the most-frequent operand types resolve
+ * with a single indexed load instead of a cg_type_get + per-kind re-classify
+ * on every call. Builtins are never aliases, so the bitset is exact. */
+ u8 builtin_pred[KIT_CG_BUILTIN_COUNT];
u8 builtins_init;
u8 pad[3];
} CgApiState;
@@ -218,6 +223,7 @@ static void builtin_cg_type_init(Compiler* c, CgType* out, KitCgBuiltinType t) {
static CgApiState* cg_api_get(Compiler* c);
static CgApiType* api_type_from_id(Compiler* c, KitCgTypeId id);
+static u8 api_pred_bits_for_kind(const CgType* ty);
/* ---- codegen scalar type-property predicates ----
* Thin queries over the resolved CgType / target ABI; pure functions of
@@ -309,6 +315,10 @@ static void cg_api_init_builtins(Compiler* c, CgApiState* s) {
* builtin, so it is ready). */
for (u32 i = 0; i < KIT_CG_BUILTIN_COUNT; ++i) {
s->builtin_class[i] = api_compute_type_class(c, builtin_id((KitCgBuiltinType)i));
+ /* Builtins are never aliases, so the kind-derived predicate bitset is the
+ * exact, final value — classify the node once here, then every predicate
+ * query is one indexed load (see api_type_pred). */
+ s->builtin_pred[i] = api_pred_bits_for_kind(&s->builtins[i]);
}
s->builtins_init = 1;
}
@@ -444,7 +454,11 @@ static int api_type_pred(Compiler* c, KitCgTypeId id, u8 mask) {
CgApiType* e;
if (id == KIT_CG_TYPE_NONE) return 0;
if ((id >> CG_API_TYPE_SEG_SHIFT) == CG_API_TYPE_BUILTIN_SEG) {
- /* Builtins are never aliases; classify the node directly. */
+ /* Builtins are never aliases; their predicate bitset is precomputed at
+ * init, so this is one indexed load (no cg_type_get + re-classify). */
+ u32 off = id & CG_API_TYPE_SEG_MASK;
+ CgApiState* s = c->cg_api ? (CgApiState*)c->cg_api : cg_api_get(c);
+ if (s && off < KIT_CG_BUILTIN_COUNT) return (s->builtin_pred[off] & mask) != 0;
return (api_pred_bits_for_kind(cg_type_get(c, id)) & mask) != 0;
}
e = api_type_from_id(c, id);