kit

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

commit fd17c4663d0fc98b08b76ea62c6810e5e8470cb6
parent 3164b12b2ead57f2e27ca6378976369d5c004373
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Fri, 12 Jun 2026 15:01:17 -0700

perf(c): fold constant field/element offsets into load/store displacement

A non-trivial lvalue load/store previously reduced the place to a single pointer
(materialize_lv_to_ptr) with the field offset baked in via ptr_to_int; +ofs;
int_to_ptr, then deref'd at 0 — so every p->field / s.member compiled to an
explicit address add plus a zero-offset load. The backend already supports
base+displacement addressing; the offset just never reached it.

pcg_lv_to_memop_place keeps a constant (scale==0, i32-range) offset as the deref
displacement instead: materialize the base pointer, reinterpret it to the field
type (a bitcast, free on the native backend now that same-width no-op casts
elide), then kit_cg_deref(ofs) so the place is INDIRECT[base + ofs] and the load
folds it into ldr w,[base,#ofs]. Indexed and out-of-range offsets keep the old
explicit-pointer fold. Used by both pcg_load and pcg_store.

On sqlite3.c -O0 (arm64-macOS) this raises base+displacement memops from 579 to
14770 (tcc emits 25566) and cuts object text another ~18% (2.97x -> 2.42x of
tcc's). Verified: test-toy 1392/0, test-parse-ok 3920/0, test-parse-err 129/0,
kit-built sqlite runs correctly.

Diffstat:
Mlang/c/parse/cg_adapter.c | 42++++++++++++++++++++++++++++++++++++++----
1 file changed, 38 insertions(+), 4 deletions(-)

diff --git a/lang/c/parse/cg_adapter.c b/lang/c/parse/cg_adapter.c @@ -453,6 +453,7 @@ static int pcg_lv_is_trivial_local(const PcgLvAux* lv) { * into the address) as a single pointer rvalue. Defined below; the memops use * it to build an explicit place before load/store. */ static void pcg_materialize_lv_to_ptr(Parser* p, const Type* result_ptr_ty); +static void pcg_lv_to_memop_place(Parser* p, const Type* field_ty); void pcg_push_local_typed(Parser* p, FrameSlot s, const Type* ty) { if (pcg_emit_enabled(p)) kit_cg_push_local(p->cg, s); @@ -490,8 +491,10 @@ void pcg_load(Parser* p) { * then deref'd to a place. A bit-field place is then tagged with its bit * geometry so the load extracts the field. */ if (!was_lvalue || !pcg_lv_is_trivial_local(lv)) { - if (was_lvalue) pcg_materialize_lv_to_ptr(p, type_ptr(p->pool, ty)); - kit_cg_deref(p->cg, 0); + if (was_lvalue) + pcg_lv_to_memop_place(p, ty); + else + kit_cg_deref(p->cg, 0); } pcg_apply_bitfield(p, &bf); kit_cg_load(p->cg, access); @@ -572,6 +575,38 @@ static void pcg_materialize_lv_to_ptr(Parser* p, const Type* result_ptr_ty) { } } +/* Build the PLACE a load/store reads, from the TOS lvalue, keeping a constant + * field/element offset as the deref displacement (so the backend folds it into + * the load/store: `ldr w, [base, #ofs]`) rather than baking it into an explicit + * `base + ofs` pointer the way pcg_materialize_lv_to_ptr + kit_cg_deref(0) does. + * Mirrors that pair's postcondition (CG TOS is the place; parser slot is the + * field pointer type with aux cleared). The pointer is reinterpreted to the + * field type with a bitcast — free on the native backend (a no-op same-width + * cast), an explicit cast on the C-source backend. Indexed (scale != 0) and + * out-of-displacement-range offsets fall back to the explicit-pointer fold. + * Caller guarantees emit is enabled. */ +static void pcg_lv_to_memop_place(Parser* p, const Type* field_ty) { + PcgLvAux* lv = pcg_top_lv_aux(p); + PcgLvBaseKind base_kind = + lv ? (PcgLvBaseKind)lv->base_kind : PCG_LV_BASE_LOCAL; + i64 ofs = lv ? lv->offset : 0; + u32 scale = lv ? lv->scale : 0u; + const Type* fptr = type_ptr(p->pool, field_ty); + if (scale != 0u || ofs < INT32_MIN || ofs > INT32_MAX) { + pcg_materialize_lv_to_ptr(p, fptr); + kit_cg_deref(p->cg, 0); + return; + } + if (base_kind == PCG_LV_BASE_LOCAL) kit_cg_addr(p->cg); + kit_cg_bitcast(p->cg, pcg_tid(p, fptr)); + kit_cg_deref(p->cg, ofs); + pcg_retag_top(p, fptr); + { + PcgLvAux* out = pcg_top_lv_aux(p); + if (out) out->base_kind = PCG_LV_BASE_POINTER_RV; + } +} + void pcg_addr(Parser* p) { const Type* ty = pcg_top_type(p); pcg_materialize_lv_to_ptr(p, type_ptr(p->pool, ty)); @@ -671,8 +706,7 @@ void pcg_store(Parser* p) { * deref'd to the PLACE the strict store requires, then tagged with the * bit-field geometry so the store inserts the field. */ pcg_drop_type(p); - pcg_materialize_lv_to_ptr(p, type_ptr(p->pool, lv_ty)); /* [dst_ptr] */ - kit_cg_deref(p->cg, 0); /* [dst_place] */ + pcg_lv_to_memop_place(p, lv_ty); /* [dst_place] */ pcg_apply_bitfield(p, &bf); pcg_push_type(p, rv_ty); }