kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

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:
Mlang/c/parse/cg_adapter.c | 38++++++++++++++++++++++++++------------
Msrc/cg/arith.c | 64++++++++++++++++++++++++++++++++++++----------------------------
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);