commit c8ee91332d1285e7e3dcd070e08e108a648e6bb2
parent d07e43a3fbb9561a6479b2a1654698bc36cda62e
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Sat, 13 Jun 2026 22:09:06 -0700
perf(cg): drop redundant resolve_type; cache slot cg_id in adapter (PERF §4.1 #2c)
resolve_type(c,id) is cg_type_get(id)?id:NONE -- a full decode whose only
output is validity. Every convert/store dest id is validated by resolve_type and
THEN by api_unalias_type (which also returns NONE for a bad id). Fuse them:
- api_cg_convert_kind: dty = api_unalias_type(dst_type) directly (preserves the
return-before-pop order on an invalid dst_type).
- kit_cg_fpext/fptrunc: drop the leading resolve_type (resolve_type(dst)==dst for
any valid id; the f128/soft predicates and the fall-through validate).
- kit_cg_{s,u}int_to_float / float_to_{s,u}int: replace every inline
resolve_type(g->c, dst) with dst (same reasoning), killing up to ~4 redundant
full decodes per cold conversion and one unconditional decode per common one.
cg_adapter: the hot pcg_mem/pcg_sizeof helpers crossed the type->cg bridge via
uncached pcg_tid even when the type belongs to a stack slot whose cg_id is
already lowered+cached. Route stack-slot-typed callers through the cached id:
- pcg_load: capture pcg_top_cg_id once; size check reuses it.
- pcg_store_impl: cache the TOS (rv) and depth-2 (lv) slot ids; both mem accesses
read pcg_mem_id with the cached id.
- pcg_convert: src is the TOS slot (pcg_top_cg_id); dst lowered once, reused for
size + convert id + the bool-compare source id.
- pcg_inc_dec: cache the TOS id BEFORE pcg_materialize_lv_to_ptr retypes the slot
to a pointer; reuse for both accesses.
Added pcg_top2_cg_id (mirrors pcg_top2_type); removed the now-unused pcg_sizeof.
Gate: perf-gate PASS (60/60 byte-identical). sqlite3.c -c object bit-identical to
golden. test-cg-api / test-toy (1392/0) / test-opt / test-smoke-x64 (3/0) /
test-smoke-rv64 (3/0) green. Cumulative (a+c) sqlite -c: ~-11.1M instructions
(-0.58%, best-of-9, golden-vs-cand).
Diffstat:
2 files changed, 62 insertions(+), 40 deletions(-)
diff --git a/lang/c/parse/cg_adapter.c b/lang/c/parse/cg_adapter.c
@@ -35,8 +35,11 @@ static KitCgTypeId pcg_top_cg_id(Parser* p) {
: KIT_CG_TYPE_NONE;
}
-static u32 pcg_sizeof(Parser* p, const Type* ty) {
- return (u32)kit_cg_type_size(p->c, pcg_tid(p, ty));
+/* The cached CG id of the depth-2 slot's type (matching pcg_top2_type). */
+static KitCgTypeId pcg_top2_cg_id(Parser* p) {
+ return p->cg_type_sp >= 2
+ ? pcg_slot_cg_id(p, &p->cg_slot_stack[p->cg_type_sp - 2u])
+ : KIT_CG_TYPE_NONE;
}
#define PCG_VALUE_LVALUE 1u
@@ -525,7 +528,8 @@ void pcg_load(Parser* p) {
int was_lvalue = pcg_top_is_lvalue(p);
if (pcg_emit_enabled(p)) {
PcgLvAux* lv = pcg_top_lv_aux(p);
- KitCgMemAccess access = pcg_mem_id(p, pcg_top_cg_id(p), ty);
+ KitCgTypeId cg_id = pcg_top_cg_id(p); /* cached TOS-slot id, lowered once */
+ KitCgMemAccess access = pcg_mem_id(p, cg_id, ty);
/* Snapshot bit-field geometry before materialize clears the aux. */
PcgLvAux bf = lv ? *lv : (PcgLvAux){0};
/* Widening signed load (Lever 4): a non-bit-field signed byte/short load is
@@ -534,7 +538,7 @@ void pcg_load(Parser* p) {
* ldrsb/ldrsh and the -O0 path can drop the redundant CV_SEXT. Plain hint;
* a backend that ignores it stays correct. */
if (bf.bit_width == 0 && type_is_int(ty) && pcg_type_is_signed(ty) &&
- pcg_sizeof(p, ty) < 4u) {
+ (u32)kit_cg_type_size(p->c, cg_id) < 4u) {
access.flags |= KIT_CG_MEM_SEXT_LOAD;
}
/* Build the PLACE the strict load requires. A trivial local already has its
@@ -739,6 +743,9 @@ static void pcg_store_impl(Parser* p, int keep_result) {
const Type* lv_ty = pcg_top2_type(p);
const Type* rv_ty = pcg_top_type(p);
const Type* mem_ty = lv_ty;
+ /* Cached CG ids of the two slots (lowered once, not re-crossed by pcg_mem). */
+ KitCgTypeId rv_cg = pcg_top_cg_id(p);
+ KitCgTypeId mem_cg = pcg_top2_cg_id(p); /* tracks mem_ty (= lv_ty) */
int emit = pcg_emit_enabled(p);
/* The aux to consume lives on the lvalue slot at parser depth 1. */
PcgLvAux* lv = pcg_lv_aux_at(p, 1);
@@ -747,8 +754,9 @@ static void pcg_store_impl(Parser* p, int keep_result) {
KitCgMemAccess access;
if (rv_ty && type_is_ptr(rv_ty) && (!lv_ty || !type_is_ptr(lv_ty))) {
mem_ty = rv_ty;
+ mem_cg = rv_cg;
}
- access = pcg_mem(p, mem_ty ? mem_ty : rv_ty);
+ access = pcg_mem_id(p, mem_ty ? mem_cg : rv_cg, mem_ty ? mem_ty : rv_ty);
if (emit) {
int wide =
rv_ty && (rv_ty->kind == TY_INT128 || rv_ty->kind == TY_UINT128 ||
@@ -771,7 +779,7 @@ static void pcg_store_impl(Parser* p, int keep_result) {
*/
FrameSlotDesc fsd;
FrameSlot tmp;
- KitCgMemAccess rv_access = pcg_mem(p, rv_ty);
+ KitCgMemAccess rv_access = pcg_mem_id(p, rv_cg, rv_ty);
int trivial = pcg_lv_is_trivial_local(lv);
memset(&fsd, 0, sizeof fsd);
fsd.type = rv_ty;
@@ -962,13 +970,16 @@ void pcg_cmp(Parser* p, CmpOp op) {
void pcg_convert(Parser* p, const Type* dst) {
const Type* src = pcg_top_type(p);
- u32 ss = pcg_sizeof(p, src);
- u32 ds = pcg_sizeof(p, dst);
+ /* src is the TOS slot: read its cached id; dst is lowered once and reused for
+ * both its size and the convert id. */
+ KitCgTypeId src_id = pcg_top_cg_id(p);
+ KitCgTypeId id = pcg_tid(p, dst);
+ u32 ss = (u32)kit_cg_type_size(p->c, src_id);
+ u32 ds = (u32)kit_cg_type_size(p->c, id);
int si = type_is_int(src) || type_is_ptr(src);
int di = type_is_int(dst) || type_is_ptr(dst);
int sf = pcg_type_is_fp(src);
int df = pcg_type_is_fp(dst);
- KitCgTypeId id = pcg_tid(p, dst);
int emit = pcg_emit_enabled(p);
if (src == dst) return;
/* C has no struct/union conversion: a pcg_convert reaching here with an
@@ -989,7 +1000,7 @@ void pcg_convert(Parser* p, const Type* dst) {
* for any non-bool scalar source. */
if (dst->kind == TY_BOOL && src->kind != TY_BOOL) {
if (emit) {
- KitCgTypeId sid = pcg_tid(p, src);
+ KitCgTypeId sid = src_id; /* cached TOS-slot id */
if (sf) {
kit_cg_push_float(p->cg, 0.0, sid);
kit_cg_fp_cmp(p->cg, KIT_CG_FP_UNE);
@@ -1061,6 +1072,9 @@ static void pcg_emit_inc_step(Parser* p, const Type* ty, BinOp op,
void pcg_inc_dec(Parser* p, BinOp op, int post) {
const Type* ty = pcg_top_type(p);
+ /* Cache the TOS-slot id NOW: pcg_materialize_lv_to_ptr below retypes the slot
+ * to a pointer, so a later pcg_top_cg_id would no longer be `ty`'s id. */
+ KitCgTypeId ty_cg = pcg_top_cg_id(p);
if (!pcg_emit_enabled(p)) {
/* Drop the lvalue parser slot and push the rvalue result type. */
pcg_drop_type(p);
@@ -1072,7 +1086,7 @@ void pcg_inc_dec(Parser* p, BinOp op, int post) {
PcgLvAux* lv = pcg_top_lv_aux(p);
/* Snapshot bit-field geometry before materialize clears the aux. */
PcgLvAux bf = lv ? *lv : (PcgLvAux){0};
- KitCgMemAccess access = pcg_mem(p, ty);
+ KitCgMemAccess access = pcg_mem_id(p, ty_cg, ty);
const Type* step_ty = ty;
u32 step = 1;
if (ty && ty->kind == TY_PTR) {
@@ -1089,7 +1103,7 @@ void pcg_inc_dec(Parser* p, BinOp op, int post) {
FrameSlotDesc fsd;
FrameSlot tmp;
const Type* result_ty = ty;
- KitCgMemAccess r_access = pcg_mem(p, result_ty);
+ KitCgMemAccess r_access = pcg_mem_id(p, ty_cg, result_ty);
memset(&fsd, 0, sizeof fsd);
fsd.type = result_ty;
fsd.size = c_abi_sizeof(p->abi, p->pool, result_ty);
diff --git a/src/cg/arith.c b/src/cg/arith.c
@@ -232,10 +232,12 @@ void api_cg_convert_kind(KitCg* g, KitCgTypeId dst_type, ConvKind ck) {
Operand dst;
if (!g) return;
T = g->target;
- dty = resolve_type(g->c, dst_type);
+ /* api_unalias_type already validates (returns NONE for a bad id), so it
+ * subsumes the standalone resolve_type that preceded it. Keep the
+ * return-before-pop order on an invalid dst_type. */
+ dty = api_unalias_type(g->c, dst_type);
if (!dty) return;
v = api_pop(g);
- dty = api_unalias_type(g->c, dty);
sty = api_unalias_type(g->c, v.type ? v.type : v.op.type);
if (!sty) {
api_release(g, &v);
@@ -1540,7 +1542,11 @@ void kit_cg_bitcast(KitCg* g, KitCgTypeId dst) {
}
void kit_cg_fpext(KitCg* g, KitCgTypeId dst) {
- KitCgTypeId dty = resolve_type(g->c, dst);
+ /* The f128 / soft-double predicates below validate `dst` internally (and the
+ * fall-through reuses `dst` directly), so the standalone resolve_type is
+ * redundant: for a valid id resolve_type(dst)==dst, for an invalid id every
+ * predicate is false and the fall-through validates. */
+ KitCgTypeId dty = dst;
if (api_is_f128_type(g->c, dty)) {
ApiSValue v = api_pop(g);
KitCgTypeId sty = api_unalias_type(g->c, api_sv_type(&v));
@@ -1562,7 +1568,9 @@ void kit_cg_fpext(KitCg* g, KitCgTypeId dst) {
}
void kit_cg_fptrunc(KitCg* g, KitCgTypeId dst) {
- KitCgTypeId dty = resolve_type(g->c, dst);
+ /* resolve_type(dst)==dst for any valid id; the predicates / fall-through
+ * validate, so drop the redundant standalone validation (see kit_cg_fpext). */
+ KitCgTypeId dty = dst;
if (api_f128_stack_top(g, 0)) {
ApiSValue v = api_pop(g);
KitCgTypeId f128 = builtin_id(KIT_CG_BUILTIN_F128);
@@ -1637,38 +1645,38 @@ static void api_fp_conv_name(char* buf, size_t cap, ApiFpConvOp op,
void kit_cg_sint_to_float(KitCg* g, KitCgTypeId dst, KitCgRounding rounding) {
(void)rounding;
- if (api_is_f128_type(g->c, resolve_type(g->c, dst))) {
+ if (api_is_f128_type(g->c, dst)) {
ApiSValue v = api_pop(g);
KitCgTypeId sty = api_unalias_type(g->c, api_sv_type(&v));
u32 sz = (u32)abi_cg_sizeof(g->c->abi, sty);
char name[16];
api_fp_conv_name(name, sizeof name, API_FPCONV_SINT_TO_FLOAT, "tf", sz);
api_push(g, v);
- api_f128_call_unary(g, name, resolve_type(g->c, dst),
+ api_f128_call_unary(g, name, dst,
api_int_builtin_for_size(sz));
return;
}
/* signed int -> soft double: __floatsidf (i32) / __floatdidf (i64). */
- if (api_type_is_soft_double(g, resolve_type(g->c, dst))) {
+ if (api_type_is_soft_double(g, dst)) {
ApiSValue v = api_pop(g);
KitCgTypeId sty = api_unalias_type(g->c, api_sv_type(&v));
u32 sz = (u32)abi_cg_sizeof(g->c->abi, sty);
char name[16];
api_fp_conv_name(name, sizeof name, API_FPCONV_SINT_TO_FLOAT, "df", sz);
api_push(g, v);
- api_f128_call_unary(g, name, resolve_type(g->c, dst),
+ api_f128_call_unary(g, name, dst,
api_int_builtin_for_size(sz));
return;
}
/* signed split-i64 -> hardware single float: use __floatdisf. */
if (api_wide64_stack_top(g, 0)) {
- api_f128_call_unary(g, "__floatdisf", resolve_type(g->c, dst),
+ api_f128_call_unary(g, "__floatdisf", dst,
builtin_id(KIT_CG_BUILTIN_I64));
return;
}
/* i32 -> soft single float (ilp32, no FPU): __floatsisf. */
- if (api_type_is_soft_single(g, resolve_type(g->c, dst))) {
- api_f128_call_unary(g, "__floatsisf", resolve_type(g->c, dst),
+ if (api_type_is_soft_single(g, dst)) {
+ api_f128_call_unary(g, "__floatsisf", dst,
builtin_id(KIT_CG_BUILTIN_I32));
return;
}
@@ -1677,38 +1685,38 @@ void kit_cg_sint_to_float(KitCg* g, KitCgTypeId dst, KitCgRounding rounding) {
void kit_cg_uint_to_float(KitCg* g, KitCgTypeId dst, KitCgRounding rounding) {
(void)rounding;
- if (api_is_f128_type(g->c, resolve_type(g->c, dst))) {
+ if (api_is_f128_type(g->c, dst)) {
ApiSValue v = api_pop(g);
KitCgTypeId sty = api_unalias_type(g->c, api_sv_type(&v));
u32 sz = (u32)abi_cg_sizeof(g->c->abi, sty);
char name[16];
api_fp_conv_name(name, sizeof name, API_FPCONV_UINT_TO_FLOAT, "tf", sz);
api_push(g, v);
- api_f128_call_unary(g, name, resolve_type(g->c, dst),
+ api_f128_call_unary(g, name, dst,
api_int_builtin_for_size(sz));
return;
}
/* unsigned int -> soft double: __floatunsidf (i32) / __floatundidf (i64). */
- if (api_type_is_soft_double(g, resolve_type(g->c, dst))) {
+ if (api_type_is_soft_double(g, dst)) {
ApiSValue v = api_pop(g);
KitCgTypeId sty = api_unalias_type(g->c, api_sv_type(&v));
u32 sz = (u32)abi_cg_sizeof(g->c->abi, sty);
char name[16];
api_fp_conv_name(name, sizeof name, API_FPCONV_UINT_TO_FLOAT, "df", sz);
api_push(g, v);
- api_f128_call_unary(g, name, resolve_type(g->c, dst),
+ api_f128_call_unary(g, name, dst,
api_int_builtin_for_size(sz));
return;
}
/* unsigned i64 -> hardware single float: __floatundisf. */
if (api_wide64_stack_top(g, 0)) {
- api_f128_call_unary(g, "__floatundisf", resolve_type(g->c, dst),
+ api_f128_call_unary(g, "__floatundisf", dst,
builtin_id(KIT_CG_BUILTIN_I64));
return;
}
/* u32 -> soft single float (ilp32, no FPU): __floatunsisf. */
- if (api_type_is_soft_single(g, resolve_type(g->c, dst))) {
- api_f128_call_unary(g, "__floatunsisf", resolve_type(g->c, dst),
+ if (api_type_is_soft_single(g, dst)) {
+ api_f128_call_unary(g, "__floatunsisf", dst,
builtin_id(KIT_CG_BUILTIN_I32));
return;
}
@@ -1718,7 +1726,7 @@ void kit_cg_uint_to_float(KitCg* g, KitCgTypeId dst, KitCgRounding rounding) {
void kit_cg_float_to_sint(KitCg* g, KitCgTypeId dst, KitCgRounding rounding) {
(void)rounding;
if (api_f128_stack_top(g, 0)) {
- KitCgTypeId dty = resolve_type(g->c, dst);
+ KitCgTypeId dty = dst;
u32 sz = (u32)abi_cg_sizeof(g->c->abi, dty);
KitCgTypeId rty = api_int_builtin_for_size(sz);
char name[16];
@@ -1729,7 +1737,7 @@ void kit_cg_float_to_sint(KitCg* g, KitCgTypeId dst, KitCgRounding rounding) {
}
/* soft double -> signed int: __fixdfsi (i32) / __fixdfdi (i64). */
if (api_soft_double_stack_top(g, 0)) {
- KitCgTypeId dty = resolve_type(g->c, dst);
+ KitCgTypeId dty = dst;
KitCgTypeId f64 = builtin_id(KIT_CG_BUILTIN_F64);
u32 sz = (u32)abi_cg_sizeof(g->c->abi, dty);
KitCgTypeId rty = api_int_builtin_for_size(sz);
@@ -1740,14 +1748,14 @@ void kit_cg_float_to_sint(KitCg* g, KitCgTypeId dst, KitCgRounding rounding) {
return;
}
/* hardware single float -> split-i64: use __fixsfdi. */
- if (api_is_wide8_scalar_type(g->c, resolve_type(g->c, dst))) {
- api_f128_call_unary(g, "__fixsfdi", resolve_type(g->c, dst),
+ if (api_is_wide8_scalar_type(g->c, dst)) {
+ api_f128_call_unary(g, "__fixsfdi", dst,
builtin_id(KIT_CG_BUILTIN_F32));
return;
}
/* soft single float -> signed int <=32 (ilp32, no FPU): __fixsfsi. */
if (api_soft_single_stack_top(g, 0)) {
- KitCgTypeId dty = resolve_type(g->c, dst);
+ KitCgTypeId dty = dst;
KitCgTypeId i32 = builtin_id(KIT_CG_BUILTIN_I32);
api_f128_call_unary(g, "__fixsfsi", i32, builtin_id(KIT_CG_BUILTIN_F32));
if (i32 != dty) api_cg_convert_kind(g, dty, CV_TRUNC);
@@ -1759,7 +1767,7 @@ void kit_cg_float_to_sint(KitCg* g, KitCgTypeId dst, KitCgRounding rounding) {
void kit_cg_float_to_uint(KitCg* g, KitCgTypeId dst, KitCgRounding rounding) {
(void)rounding;
if (api_f128_stack_top(g, 0)) {
- KitCgTypeId dty = resolve_type(g->c, dst);
+ KitCgTypeId dty = dst;
u32 sz = (u32)abi_cg_sizeof(g->c->abi, dty);
KitCgTypeId rty = api_int_builtin_for_size(sz);
char name[16];
@@ -1770,7 +1778,7 @@ void kit_cg_float_to_uint(KitCg* g, KitCgTypeId dst, KitCgRounding rounding) {
}
/* soft double -> unsigned int: __fixunsdfsi (i32) / __fixunsdfdi (i64). */
if (api_soft_double_stack_top(g, 0)) {
- KitCgTypeId dty = resolve_type(g->c, dst);
+ KitCgTypeId dty = dst;
KitCgTypeId f64 = builtin_id(KIT_CG_BUILTIN_F64);
u32 sz = (u32)abi_cg_sizeof(g->c->abi, dty);
KitCgTypeId rty = api_int_builtin_for_size(sz);
@@ -1781,14 +1789,14 @@ void kit_cg_float_to_uint(KitCg* g, KitCgTypeId dst, KitCgRounding rounding) {
return;
}
/* hardware single float -> split-u64: use __fixunssfdi. */
- if (api_is_wide8_scalar_type(g->c, resolve_type(g->c, dst))) {
- api_f128_call_unary(g, "__fixunssfdi", resolve_type(g->c, dst),
+ if (api_is_wide8_scalar_type(g->c, dst)) {
+ api_f128_call_unary(g, "__fixunssfdi", dst,
builtin_id(KIT_CG_BUILTIN_F32));
return;
}
/* soft single float -> unsigned int <=32 (ilp32, no FPU): __fixunssfsi. */
if (api_soft_single_stack_top(g, 0)) {
- KitCgTypeId dty = resolve_type(g->c, dst);
+ KitCgTypeId dty = dst;
KitCgTypeId i32 = builtin_id(KIT_CG_BUILTIN_I32);
api_f128_call_unary(g, "__fixunssfsi", i32, builtin_id(KIT_CG_BUILTIN_F32));
if (i32 != dty) api_cg_convert_kind(g, dty, CV_TRUNC);