commit ac1e312614a908ba4f64f796ebb3fb3a37684ec9
parent 244f103980455c31f5f0ef75d128bd5fad50bc0a
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Mon, 15 Jun 2026 13:42:37 -0700
cg: fold the type identity memo into the KitCgTypeInfo prefix
Now that the public KitCgTypeInfo is CgApiType's first member, route
kit_cg_type_info's identity fill through kit_cg_type_view and collapse the
duplicated per-entry memos:
- api_unalias_type and kit_cg_type_view share one api_type_info_fill helper
(a single alias/source-base chain walk). The alias-resolved terminal id is
the prefix's info.storage_id, gated by info.id != 0, so the separate
unaliased/unalias_filled cache is removed.
- kit_cg_type_info copies identity from the view (*out = *v) and computes only
the completion/sizing flags and ABI layout per call. Byte-identical output;
the SOURCE_BASE NOMINAL case is preserved by the shared fill helper.
- Drop the write-only CgApiTypeKind field and enum (dead since the structural
hashset dedup: the eq/hash callbacks never read it and the array/func sets
are disjoint by construction).
test-cg-api / test-opt / test-parse green.
Diffstat:
| M | src/cg/type.c | | | 139 | +++++++++++++++++++++++++++++++++---------------------------------------------- |
1 file changed, 58 insertions(+), 81 deletions(-)
diff --git a/src/cg/type.c b/src/cg/type.c
@@ -3,16 +3,6 @@
#include "core/hashmap.h"
#include "obj/obj.h"
-typedef enum CgApiTypeKind {
- CG_API_TYPE_PTR,
- CG_API_TYPE_ARRAY,
- CG_API_TYPE_ALIAS,
- CG_API_TYPE_RECORD,
- CG_API_TYPE_ENUM,
- CG_API_TYPE_FUNC,
- CG_API_TYPE_SOURCE_BASE,
-} CgApiTypeKind;
-
typedef struct CgApiType {
/* Public identity view (kit/cg.h). Kept as the FIRST member so a pointer to
* the entry is a valid `const KitCgTypeInfo*` and kit_cg_type_view can hand
@@ -34,7 +24,6 @@ typedef struct CgApiType {
const KitCgFuncParam* params;
KitCgFuncResult result; /* void builtin means no value */
KitCgCallConv call_conv;
- u8 kind;
u8 abi_variadic;
/* Lazily-filled packed API_TYPE_CLASS_* memo (0 == not yet computed; the
* VALID bit distinguishes a computed all-zero class from unfilled). Reuses
@@ -47,17 +36,14 @@ typedef struct CgApiType {
u8 abi_atomic;
/* Lazily-filled flat predicate descriptor (reuses the former pad[3]). Extends
* the cached_class mechanism: pred_bits is a bitset of API_PRED_* computed on
- * the UNALIASED type, pred_valid distinguishes a computed all-zero set from
- * unfilled, and `unaliased` caches api_unalias_type's terminal id (valid once
- * unalias_filled is set). Predicates and api_unalias_type then become one
- * descriptor load instead of a recursive alias walk + per-kind re-decode. */
+ * the UNALIASED type, and pred_valid distinguishes a computed all-zero set
+ * from unfilled. The alias-resolved terminal id that api_unalias_type returns
+ * is the prefix's info.storage_id (filled, with the rest of the identity, when
+ * info.id != 0) — there is no separate unaliased/unalias_filled cache. */
u8 pred_bits;
u8 pred_valid;
- u8 unalias_filled;
u32 abi_size;
u32 abi_align;
- /* Terminal id of this entry's alias chain (set when unalias_filled). */
- KitCgTypeId unaliased;
/* This entry's own type id (set by type_alloc). Lets a structural hashset of
* CgApiType* recover the id without maintaining a reverse index. */
KitCgTypeId self_id;
@@ -573,7 +559,6 @@ static KitCgTypeId find_array_type_id(Compiler* c, KitCgTypeId elem,
if (!c || !c->cg_api) return KIT_CG_TYPE_NONE;
s = (CgApiState*)c->cg_api;
memset(&probe, 0, sizeof probe);
- probe.kind = CG_API_TYPE_ARRAY;
probe.base = elem;
probe.array_count = count;
hit = CgArraySet_find(&s->array_index, &probe);
@@ -606,8 +591,8 @@ static int cg_array_eq(CgApiType* const a, CgApiType* const b) {
}
/* Structural hash/eq over a function type's identity — the same fields the old
- * linear scan compared. Only CG_API_TYPE_FUNC entries enter the set, so neither
- * callback re-checks kind. The result/param attrs are compared (not hashed):
+ * linear scan compared. Only function types enter this set, so neither callback
+ * needs to re-check kind. The result/param attrs are compared (not hashed):
* fewer hash inputs just means eq resolves the (rare) collision. */
static u32 cg_func_hash(CgApiType* const e) {
u32 h = 2166136261u;
@@ -636,7 +621,6 @@ static KitCgTypeId find_func_type_id(Compiler* c, KitCgFuncSig sig) {
if (!c || !c->cg_api) return KIT_CG_TYPE_NONE;
s = (CgApiState*)c->cg_api;
memset(&probe, 0, sizeof probe);
- probe.kind = CG_API_TYPE_FUNC;
probe.count = sig.nparams;
probe.abi_variadic = (u8)(sig.abi_variadic != 0);
probe.call_conv = sig.call_conv;
@@ -662,35 +646,45 @@ KitCgTypeId resolve_type(Compiler* c, KitCgTypeId id) {
return cg_type_get(c, id) ? id : KIT_CG_TYPE_NONE;
}
+/* Fill an entry's identity prefix (the kit/cg.h KitCgTypeInfo) once. storage_id
+ * strips both alias and source-base spellings to the terminal storage type, so
+ * every ABI/codegen/predicate site that unaliases sees the underlying builtin
+ * with no source-base special-casing. Identity is immutable after creation, so
+ * this runs at most once per entry (info.id != 0 thereafter). */
+static void api_type_info_fill(Compiler* c, CgApiType* e, KitCgTypeId id) {
+ const CgType* ty = &e->cg; /* == cg_type_get(c, id) for a user id */
+ KitCgTypeId sid = id;
+ e->info.id = id;
+ e->info.kind = e->cg.kind;
+ if (e->cg.kind == KIT_CG_TYPE_RECORD || e->cg.kind == KIT_CG_TYPE_ENUM ||
+ e->cg.kind == KIT_CG_TYPE_ALIAS || e->cg.kind == KIT_CG_TYPE_SOURCE_BASE)
+ e->info.flags = KIT_CG_TYPEF_NOMINAL;
+ while (ty && (ty->kind == KIT_CG_TYPE_ALIAS ||
+ ty->kind == KIT_CG_TYPE_SOURCE_BASE)) {
+ sid = ty->kind == KIT_CG_TYPE_ALIAS ? ty->alias.base : ty->source_base.base;
+ ty = cg_type_get(c, sid);
+ }
+ e->info.storage_id = ty ? sid : KIT_CG_TYPE_NONE;
+}
+
+/* The CgApiType for a user id with its identity prefix filled, or NULL. */
+static CgApiType* api_type_entry_filled(Compiler* c, KitCgTypeId id) {
+ CgApiType* e = api_type_from_id(c, id);
+ if (!e) return NULL;
+ if (e->info.id == 0) api_type_info_fill(c, e, id);
+ return e;
+}
+
KitCgTypeId api_unalias_type(Compiler* c, KitCgTypeId id) {
- CgApiType* e;
const CgType* ty;
- KitCgTypeId start = id;
- /* Builtins are never aliases — no entry, no chain to walk or cache. */
+ CgApiType* e;
+ /* Builtins are never aliases — no entry, no chain to walk. */
if (id == KIT_CG_TYPE_NONE || id <= KIT_CG_BUILTIN_COUNT) {
ty = cg_type_get(c, id);
return ty ? id : KIT_CG_TYPE_NONE;
}
- e = api_type_from_id(c, id);
- if (e && e->unalias_filled) return e->unaliased;
- ty = cg_type_get(c, id);
- /* Both aliases and source-base spellings are transparent to storage: strip
- * them to the terminal storage type so every ABI/codegen/predicate site that
- * unaliases sees the underlying builtin with no source-base special-casing. */
- while (ty && (ty->kind == KIT_CG_TYPE_ALIAS ||
- ty->kind == KIT_CG_TYPE_SOURCE_BASE)) {
- id = ty->kind == KIT_CG_TYPE_ALIAS ? ty->alias.base : ty->source_base.base;
- ty = cg_type_get(c, id);
- }
- id = ty ? id : KIT_CG_TYPE_NONE;
- /* Cache on the entry for the start id. (start == the original id; e was
- * fetched for it above. Only fill for a real entry.) */
- if (e) {
- e->unaliased = id;
- e->unalias_filled = 1;
- }
- (void)start;
- return id;
+ e = api_type_entry_filled(c, id);
+ return e ? e->info.storage_id : KIT_CG_TYPE_NONE;
}
static KitCgFuncParam* copy_cg_params(Compiler* c, const KitCgFuncParam* src,
@@ -1037,7 +1031,6 @@ KitCgTypeId kit_cg_type_ptr(KitCompiler* c, KitCgTypeId pointee,
if (!e) return KIT_CG_TYPE_NONE;
e->base = pointee;
e->address_space = address_space;
- e->kind = CG_API_TYPE_PTR;
if (!cg_type_set_ptr(c, e, pointee, address_space)) {
return KIT_CG_TYPE_NONE;
}
@@ -1057,7 +1050,6 @@ KitCgTypeId kit_cg_type_array(KitCompiler* c, KitCgTypeId elem,
if (!e) return KIT_CG_TYPE_NONE;
e->base = elem;
e->array_count = count;
- e->kind = CG_API_TYPE_ARRAY;
if (!cg_type_set_array(c, e, elem, count)) {
return KIT_CG_TYPE_NONE;
}
@@ -1073,7 +1065,6 @@ KitCgTypeId kit_cg_type_alias(KitCompiler* c, KitSym name, KitCgTypeId base) {
if (!e) return KIT_CG_TYPE_NONE;
e->base = base;
e->name = name;
- e->kind = CG_API_TYPE_ALIAS;
return cg_type_set_alias(c, e, name, base) ? id : KIT_CG_TYPE_NONE;
}
@@ -1090,7 +1081,6 @@ KitCgTypeId kit_cg_type_source_base(KitCompiler* c, KitSym name,
if (!e) return KIT_CG_TYPE_NONE;
e->base = storage;
e->name = name;
- e->kind = CG_API_TYPE_SOURCE_BASE;
return cg_type_set_source_base(c, e, name, storage, (u8)encoding)
? id
: KIT_CG_TYPE_NONE;
@@ -1105,7 +1095,6 @@ KitCgTypeId kit_cg_type_record_decl(KitCompiler* c, KitSym tag, int is_union) {
e->name = tag;
e->count = 0;
e->fields = NULL;
- e->kind = CG_API_TYPE_RECORD;
memset(&e->cg, 0, sizeof(e->cg));
e->cg.kind = KIT_CG_TYPE_RECORD;
e->cg.record.tag = tag;
@@ -1167,7 +1156,6 @@ KitCgTypeId kit_cg_type_enum(KitCompiler* c, KitSym tag, KitCgTypeId base,
e->name = tag;
e->count = nvalues;
e->values = copied;
- e->kind = CG_API_TYPE_ENUM;
if (!cg_type_set_enum(c, e, tag, base, copied, nvalues)) {
return KIT_CG_TYPE_NONE;
}
@@ -1202,7 +1190,6 @@ KitCgTypeId kit_cg_type_func(KitCompiler* c, KitCgFuncSig sig) {
e->result = sig.result;
e->call_conv = sig.call_conv;
e->abi_variadic = sig.abi_variadic != 0;
- e->kind = CG_API_TYPE_FUNC;
if (!cg_type_set_func(c, e, sig, copied)) {
return KIT_CG_TYPE_NONE;
}
@@ -1232,17 +1219,8 @@ const KitCgTypeInfo* kit_cg_type_view(KitCompiler* kc, KitCgTypeId id) {
s = c->cg_api ? (CgApiState*)c->cg_api : cg_api_get(c);
if (!s) return NULL;
if (id <= KIT_CG_BUILTIN_COUNT) return &s->builtin_info[id - 1u];
- e = api_type_from_id(c, id);
- if (!e) return NULL;
- if (e->info.id == 0) {
- e->info.id = id;
- e->info.storage_id = api_unalias_type(c, id);
- e->info.kind = e->cg.kind;
- if (e->cg.kind == KIT_CG_TYPE_RECORD || e->cg.kind == KIT_CG_TYPE_ENUM ||
- e->cg.kind == KIT_CG_TYPE_ALIAS)
- e->info.flags = KIT_CG_TYPEF_NOMINAL;
- }
- return &e->info;
+ e = api_type_entry_filled(c, id);
+ return e ? &e->info : NULL;
}
int kit_cg_type_is_complete(KitCompiler* c, KitCgTypeId id) {
@@ -1291,30 +1269,29 @@ static uint16_t cg_type_scalar_width(Compiler* c, const CgType* ty) {
KitStatus kit_cg_type_info(KitCompiler* kc, KitCgTypeId id,
KitCgTypeInfo* out) {
Compiler* c = (Compiler*)kc;
- const CgType* exact;
+ const KitCgTypeInfo* v;
const CgType* storage;
- KitCgTypeId storage_id;
ABITypeInfo ti;
if (!out) return KIT_INVALID;
- memset(out, 0, sizeof(*out));
- exact = cg_type_get(c, id);
- if (!exact) return KIT_INVALID;
- storage_id = api_unalias_type(c, id);
- storage = cg_type_get(c, storage_id);
- if (!storage) return KIT_INVALID;
-
- out->id = id;
- out->storage_id = storage_id;
- out->kind = exact->kind;
- if (id <= KIT_CG_BUILTIN_COUNT) out->flags |= KIT_CG_TYPEF_BUILTIN;
- if (exact->kind == KIT_CG_TYPE_RECORD || exact->kind == KIT_CG_TYPE_ENUM ||
- exact->kind == KIT_CG_TYPE_ALIAS ||
- exact->kind == KIT_CG_TYPE_SOURCE_BASE)
- out->flags |= KIT_CG_TYPEF_NOMINAL;
+ v = kit_cg_type_view(kc, id);
+ if (!v) {
+ memset(out, 0, sizeof(*out));
+ return KIT_INVALID;
+ }
+ storage = cg_type_get(c, v->storage_id);
+ if (!storage) {
+ memset(out, 0, sizeof(*out));
+ return KIT_INVALID;
+ }
+
+ /* Identity (id/storage_id/kind and the BUILTIN/NOMINAL flags) comes straight
+ * from the view's interned prefix; only the completion/sizing flags and the
+ * ABI layout are computed per call (they are not maintained on the view). */
+ *out = *v;
if (cg_type_complete_id(c, id)) out->flags |= KIT_CG_TYPEF_COMPLETE;
if (cg_type_sized_id(c, id)) out->flags |= KIT_CG_TYPEF_SIZED;
- ti = abi_cg_type_info(c->abi, storage_id);
+ ti = abi_cg_type_info(c->abi, v->storage_id);
if (ti.align || storage->kind == KIT_CG_TYPE_VOID) {
out->layout.size = ti.size;
out->layout.align = ti.align;