commit 407e8ac9402c41d518db4ec5f084baea375f33ae
parent 5ff33ea3ab65aa517e6dbb0e50f6c9734d18fe1d
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Sat, 13 Jun 2026 11:24:26 -0700
perf(type): field-init TypeCgLower + stamp builtin cg_id on the node (B3)
(cherry picked from commit 5a8f494350bb173399522ad2d59b7446f7aa907f)
Diffstat:
1 file changed, 15 insertions(+), 6 deletions(-)
diff --git a/lang/c/type/type.c b/lang/c/type/type.c
@@ -840,15 +840,22 @@ static KitCgTypeId type_cg_lower(TypeCgLower* l, const Type* t,
/* Only VALUE-mode lowering is memoized on the node: TYPE_CG_RECORD_FIELD
* collapses pointers to void*, so the same Type* lowers differently there
* (and that mode is used only transiently for record fields / func params).
- * Check the cache BEFORE the builtin switch: a primitive node never carries a
- * cg_id (it returns from the switch below before the cache-store), so this
- * hoist only short-circuits already-lowered non-builtin types and never
- * mis-returns a primitive's id — byte-identical, but skips the switch. */
+ * Check the cache BEFORE the builtin switch: a cached cg_id (whether a builtin
+ * id stamped below or a non-builtin lowering) always equals what the switch
+ * would recompute, so this hoist only short-circuits already-lowered types —
+ * byte-identical, but skips the recompute. */
cacheable = (mode == TYPE_CG_VALUE);
if (cacheable && t->cg_id != KIT_CG_TYPE_NONE)
return t->cg_id; /* cached => was incomplete-free => still stable */
id = type_cg_builtin(l->c, (TypeKind)t->kind);
- if (id != KIT_CG_TYPE_NONE) return id;
+ if (id != KIT_CG_TYPE_NONE) {
+ /* type_cg_builtin is a pure function of t->kind (and the fixed target), so
+ * a builtin id is mode-independent and incomplete-free: stamp it on the node
+ * so the next VALUE-mode crossing short-circuits above. A builtin id is
+ * never 0 (builtin_id(VOID)=64), so it can't alias the NONE sentinel. */
+ if (cacheable) ((Type*)t)->cg_id = id;
+ return id;
+ }
/* Scope saw_incomplete to this subtree so the post-recursion check reflects
* only whether *this* node's lowering touched an incomplete record. */
outer_incomplete = l->saw_incomplete;
@@ -916,10 +923,12 @@ static KitCgTypeId type_cg_lower(TypeCgLower* l, const Type* t,
KitCgTypeId type_cg_id_in_pool(KitCompiler* c, Pool* p, const Type* t) {
TypeCgLower l;
if (!p) return KIT_CG_TYPE_NONE;
- memset(&l, 0, sizeof l);
+ /* TypeCgLower has exactly these four fields; explicit init writes the same
+ * values the memset+assignments did, skipping a per-crossing struct clear. */
l.c = c;
l.p = p;
l.cache = cache_get(p);
+ l.saw_incomplete = 0;
return type_cg_lower(&l, t, TYPE_CG_VALUE);
}