commit 9a060c6c4f6e3d811e3d5d0a493a73625571062d
parent 407e8ac9402c41d518db4ec5f084baea375f33ae
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Sat, 13 Jun 2026 11:40:17 -0700
perf(cg): one type decode per id at multi-predicate sites + hoist non-CSE unalias (B1)
(cherry picked from commit a687d419874a87ced7dc15ff447a9cd059b55815)
Diffstat:
5 files changed, 49 insertions(+), 35 deletions(-)
diff --git a/src/cg/local.c b/src/cg/local.c
@@ -10,8 +10,9 @@ int api_local_requires_memory(KitCg* g, KitCgTypeId ty, KitCgLocalAttrs attrs) {
* state, and any non-scalar type must live in memory. */
if (api_is_wide16_scalar_type(g->c, ty)) return 1;
if (api_is_wide8_scalar_type(g->c, ty)) return 1;
- return !(cg_type_is_int(g->c, ty) || cg_type_is_float(g->c, ty) ||
- cg_type_is_ptr(g->c, ty));
+ /* int||float||ptr, in one type decode instead of three. */
+ return !(api_type_pred_bits(g->c, ty) &
+ (API_PRED_INT | API_PRED_FLOAT | API_PRED_PTR));
}
KitCgLocal api_local_handle(u32 index) {
diff --git a/src/cg/memory.c b/src/cg/memory.c
@@ -388,6 +388,7 @@ void kit_cg_store(KitCg* g, KitCgMemAccess access) {
Operand mem_op;
int is_lvalue;
int is_bitfield;
+ int ty_is_agg;
if (!g) return;
T = g->target;
if (access.flags & KIT_CG_MEM_VOLATILE) api_local_const_memory_boundary(g);
@@ -407,20 +408,20 @@ void kit_cg_store(KitCg* g, KitCgMemAccess access) {
ty = api_mem_access_type(g, access, api_sv_type(&base), "store");
access_ty = ty;
- /* Aggregate store: memcpy through the source place. */
- if (!is_bitfield && (cg_type_is_aggregate(g->c, ty) ||
- cg_type_is_aggregate(g->c, api_sv_type(&rv)))) {
+ /* Aggregate store: memcpy through the source place. `ty` is stable here, so
+ * decode its aggregate predicate once and reuse it below. */
+ ty_is_agg = cg_type_is_aggregate(g->c, ty);
+ if (!is_bitfield &&
+ (ty_is_agg || cg_type_is_aggregate(g->c, api_sv_type(&rv)))) {
KitCgTypeId ptr_ty;
Operand dst_addr, src_addr;
int src_ptr_rvalue;
AggregateAccess agg;
u32 src_size;
- u32 dst_size = cg_type_is_aggregate(g->c, ty)
- ? api_mem_type_size(g, ty, "store")
- : api_mem_type_size(g, api_sv_type(&base), "store");
- u32 access_size = cg_type_is_aggregate(g->c, ty)
- ? api_mem_type_size(g, ty, "store")
- : dst_size;
+ u32 dst_size = ty_is_agg ? api_mem_type_size(g, ty, "store")
+ : api_mem_type_size(g, api_sv_type(&base), "store");
+ u32 access_size =
+ ty_is_agg ? api_mem_type_size(g, ty, "store") : dst_size;
src_ptr_rvalue =
!api_is_lvalue_sv(&rv) && cg_type_is_ptr(g->c, api_sv_type(&rv));
src_size = src_ptr_rvalue ? access_size
diff --git a/src/cg/type.c b/src/cg/type.c
@@ -54,15 +54,8 @@ typedef struct CgApiType {
KitCgTypeId self_id;
} CgApiType;
-/* Flat per-id predicate bitset, computed on the UNALIASED type. The membership
- * rules MUST match the legacy cg_type_is_* exactly (notably: IS_INT covers
- * INT|BOOL|ENUM, and IS_AGGREGATE == IS_RECORD). */
-#define API_PRED_INT 0x01u
-#define API_PRED_FLOAT 0x02u
-#define API_PRED_PTR 0x04u
-#define API_PRED_VOID 0x08u
-#define API_PRED_RECORD 0x10u
-#define API_PRED_AGGREGATE 0x20u
+/* The flat per-id predicate bitset (API_PRED_*) and api_type_pred_bits live in
+ * cg/type.h so multi-predicate-on-same-id call sites can decode an id once. */
/* High bit of CgApiType.cached_class: set once the class has been computed, so
* a genuine {WK_NARROW, non-aggregate} (packed 0) is told apart from unfilled. */
@@ -447,10 +440,11 @@ static u8 api_pred_bits_for_kind(const CgType* ty) {
}
}
-/* One predicate query for `id`: builtin fast path (no aliasing, no entry) or a
- * descriptor load off the user-type entry, filled lazily on first miss. Returns
- * (bits & mask) != 0. Mirrors api_type_class's CACHED-bit memo. */
-static int api_type_pred(Compiler* c, KitCgTypeId id, u8 mask) {
+/* Full API_PRED_* bitset for `id` via exactly one decode: builtin fast path (no
+ * aliasing, no entry) or a descriptor load off the user-type entry, filled
+ * lazily on first miss. Multi-predicate-on-the-same-id call sites read this once
+ * and then test the masks locally instead of decoding `id` per predicate. */
+u8 api_type_pred_bits(Compiler* c, KitCgTypeId id) {
CgApiType* e;
if (id == KIT_CG_TYPE_NONE) return 0;
if ((id >> CG_API_TYPE_SEG_SHIFT) == CG_API_TYPE_BUILTIN_SEG) {
@@ -458,8 +452,8 @@ static int api_type_pred(Compiler* c, KitCgTypeId id, u8 mask) {
* 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;
+ if (s && off < KIT_CG_BUILTIN_COUNT) return s->builtin_pred[off];
+ return api_pred_bits_for_kind(cg_type_get(c, id));
}
e = api_type_from_id(c, id);
if (!e) return 0;
@@ -470,7 +464,13 @@ static int api_type_pred(Compiler* c, KitCgTypeId id, u8 mask) {
e->pred_bits = api_pred_bits_for_kind(cg_type_get(c, u));
e->pred_valid = 1;
}
- return (e->pred_bits & mask) != 0;
+ return e->pred_bits;
+}
+
+/* One predicate query for `id`: returns (bits & mask) != 0 off the single-decode
+ * bitset. Mirrors api_type_class's CACHED-bit memo. */
+static int api_type_pred(Compiler* c, KitCgTypeId id, u8 mask) {
+ return (api_type_pred_bits(c, id) & mask) != 0;
}
int cg_type_is_int(Compiler* c, KitCgTypeId id) {
diff --git a/src/cg/type.h b/src/cg/type.h
@@ -88,6 +88,20 @@ KitCgTypeId api_unalias_type(Compiler*, KitCgTypeId);
int cg_type_is_void(Compiler*, KitCgTypeId);
int cg_type_is_aggregate(Compiler*, KitCgTypeId);
+/* Flat per-id predicate bitset (computed on the UNALIASED type). The six
+ * cg_type_is_* predicates are (bits & API_PRED_*) tests over this set; a site
+ * that asks several predicates of the SAME id reads the bitset once via
+ * api_type_pred_bits and tests the masks locally (one decode, not N). The
+ * membership rules MUST match cg_type_is_* exactly: INT covers INT|BOOL|ENUM and
+ * AGGREGATE == RECORD. */
+#define API_PRED_INT 0x01u
+#define API_PRED_FLOAT 0x02u
+#define API_PRED_PTR 0x04u
+#define API_PRED_VOID 0x08u
+#define API_PRED_RECORD 0x10u
+#define API_PRED_AGGREGATE 0x20u
+u8 api_type_pred_bits(Compiler*, KitCgTypeId);
+
/* Codegen-relevant scalar predicates: a value of this type is a float, a
* 128-bit scalar (f128 / i128), or a wide 8-byte scalar the selected ABI splits
* into two 32-bit lanes (forced memory-resident; legalized as lane sequences /
diff --git a/src/cg/value.c b/src/cg/value.c
@@ -366,15 +366,14 @@ int api_sv_owns_operand_local(const ApiSValue* sv, const Operand* op) {
void api_ensure_local(KitCg* g, ApiSValue* sv) {
if (sv->kind == SV_CMP) {
KitCgTypeId ty = api_sv_type(sv);
+ KitCgTypeId uty = api_unalias_type(g->c, ty); /* compute the dst terminal once */
Operand dst;
if (sv->delayed->cmp.a_owned && sv->delayed->cmp.a.kind == OPK_LOCAL &&
- api_unalias_type(g->c, sv->delayed->cmp.a.type) ==
- api_unalias_type(g->c, ty)) {
+ api_unalias_type(g->c, sv->delayed->cmp.a.type) == uty) {
dst = api_op_local(sv->delayed->cmp.a.v.local, ty);
} else if (sv->delayed->cmp.b_owned &&
sv->delayed->cmp.b.kind == OPK_LOCAL &&
- api_unalias_type(g->c, sv->delayed->cmp.b.type) ==
- api_unalias_type(g->c, ty)) {
+ api_unalias_type(g->c, sv->delayed->cmp.b.type) == uty) {
dst = api_op_local(sv->delayed->cmp.b.v.local, ty);
} else {
CGLocal r =
@@ -386,15 +385,14 @@ void api_ensure_local(KitCg* g, ApiSValue* sv) {
}
if (sv->kind == SV_ARITH) {
KitCgTypeId ty = api_sv_type(sv);
+ KitCgTypeId uty = api_unalias_type(g->c, ty); /* compute the dst terminal once */
Operand dst;
if (sv->delayed->arith.a_owned && sv->delayed->arith.a.kind == OPK_LOCAL &&
- api_unalias_type(g->c, sv->delayed->arith.a.type) ==
- api_unalias_type(g->c, ty)) {
+ api_unalias_type(g->c, sv->delayed->arith.a.type) == uty) {
dst = api_op_local(sv->delayed->arith.a.v.local, ty);
} else if (api_arith_rhs_reusable(sv) && sv->delayed->arith.b_owned &&
sv->delayed->arith.b.kind == OPK_LOCAL &&
- api_unalias_type(g->c, sv->delayed->arith.b.type) ==
- api_unalias_type(g->c, ty)) {
+ api_unalias_type(g->c, sv->delayed->arith.b.type) == uty) {
dst = api_op_local(sv->delayed->arith.b.v.local, ty);
} else {
CGLocal r =