kit

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

commit f75874cb64e948bc45a02ff2eb90bdb4b5457da3
parent 3516b3d341e45839a621d05bf189000302a461f0
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Fri, 12 Jun 2026 19:27:35 -0700

perf(cg): route -O0 address results through the write-back cache

The single-pass address producers (nd_addr_of, nd_local_addr, nd_load_label_addr)
computed the address into a scratch register and then stored it to the result
local's home (nd_dst_scratch + nd_store_operand_from_reg). When the address feeds
an immediate consumer — overwhelmingly the load it was computed for, or the base
of one — that meant a spill + reload around every &member / &local / &arr[i].

Route them through the write-back register cache exactly like nd_load already
does (nd_dst_reg + nd_dst_writeback): load_addr writes straight into the dst
local's cache register, which the consumer then reads directly. The address
temps are still pinned and nd_cache_alloc skips pinned regs, so the cache
register never aliases the base/index. Caching a computed address is
correctness-equivalent to caching a loaded pointer (which nd_load already does)
and is dropped at every barrier.

sqlite3.c -c, arm64-macOS: 599,488 -> 586,773 insns (-2.1%); object 2.79 -> 2.74MB.
Combined with the temp-slot reuse this run: 831,078 -> 586,773 (-29.4%), now
~1.71x tcc. (The remaining ~63K reg-reg movs are kit_cg_copy value-stack copies
between distinct locals; eliminating those needs liveness-based storage
coalescing, deliberately out of scope here.)

test-toy 1392/0, test-parse-ok 3920/0 (O0/O1 x D/R/J/E), test-parse-err 129/0,
test-smoke-x64/rv64 3/0; sqlite shell links+runs (84|2).

Diffstat:
Msrc/cg/native_direct_target.c | 25++++++++++++++++---------
1 file changed, 16 insertions(+), 9 deletions(-)

diff --git a/src/cg/native_direct_target.c b/src/cg/native_direct_target.c @@ -1064,11 +1064,13 @@ static void nd_local_addr(CgTarget* t, Operand dst, const CGLocalDesc* desc, lv.type = l->type; lv.v.local = local; { - NativeLoc reg = nd_dst_scratch(d, dst); + /* Route the &local address through the write-back cache (see nd_addr_of): + * write it into dst's cache register rather than a scratch copied to the + * home, killing the mov and keeping it resident for an immediate consumer. */ + NativeLoc reg = nd_dst_reg(d, dst); ND_REQUIRE_NATIVE(d, load_addr, "target does not materialize addresses"); d->native->load_addr(d->native, reg, nd_addr_storage(d, lv)); - nd_store_operand_from_reg(d, dst, reg); - nd_release_materialized(d, reg); + nd_dst_writeback(d, dst, reg); } } @@ -1164,12 +1166,11 @@ static void nd_load_label_addr(CgTarget* t, Operand dst, Label label) { NativeDirectTarget* d = nd_of(t); NativeLoc reg; nd_flush_all(d); - reg = nd_dst_scratch(d, dst); + reg = nd_dst_reg(d, dst); ND_REQUIRE_NATIVE(d, load_label_addr, "target does not materialize label addresses"); d->native->load_label_addr(d->native, reg, nd_mc_label(d, label)); - nd_store_operand_from_reg(d, dst, reg); - nd_release_materialized(d, reg); + nd_dst_writeback(d, dst, reg); } static int nd_local_static_data_begin(CgTarget* t, @@ -1484,11 +1485,17 @@ static void nd_addr_of(CgTarget* t, Operand dst, Operand lv) { l->flags |= CG_LOCAL_ADDR_TAKEN; } naddr = nd_addr_materialize(d, nd_addr_storage(d, lv), &temps, mem); - NativeLoc reg = nd_dst_scratch(d, dst); + /* Route the address through the write-back register cache (like nd_load): + * load_addr writes straight into the dst local's cache register instead of a + * scratch that is then copied to the home, killing the mov and keeping the + * address resident for an immediate consumer (e.g. the base of the load it + * feeds). nd_dst_reg falls back to a scratch + home store via nd_dst_writeback + * when dst is not cacheable. The address temps are still pinned, and + * nd_cache_alloc skips pinned regs, so the cache reg never aliases base/index. */ + NativeLoc reg = nd_dst_reg(d, dst); ND_REQUIRE_NATIVE(d, load_addr, "target does not materialize addresses"); d->native->load_addr(d->native, reg, naddr); - nd_store_operand_from_reg(d, dst, reg); - nd_release_materialized(d, reg); + nd_dst_writeback(d, dst, reg); nd_addr_temps_release(d, &temps); }