kit

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

commit af44b31a755fcb5fe512d1554b5157163a38fb48
parent 9fb2baf5d5280fc66fafcff63cf83bf9fca0eaec
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Sat, 13 Jun 2026 07:55:31 -0700

perf(cg): -O0 copy coalescing — `x = <dead transient>` renames, no mov

The scalar-local plain-store path (`x = <value>` / `int x = <value>`) emitted
`T->copy(dst, src)` -> a routing `mov` whenever the RHS landed in a transient
register that the store then copied into x's storage.

Flag that source dead (OPK_FLAG_KILL) when it is a transient with no other live
value-stack reference (api_temp_dead; rv, its last reference, was popped at the
top of kit_cg_store). The nd_copy hook added with convert coalescing then
renames the transient's live register to x instead of emitting the mov — the
producer's register simply becomes x's. A live or shared source stays clear in
the flag and is copied as before (correctness over a missed mov).

sqlite3.c -c -O0 (arm64-macOS): emitted insns 462,967 -> 457,115 (-5,852);
object 2,246,368 -> 2,222,960 B (-1.0%); compile instrs ~2,133.4M -> ~2,131.9M.
Cumulative with convert coalescing: emitted 474,752 -> 457,115 (-3.7%); object
2,293,504 -> 2,222,960 B (-3.1%). Byte-deterministic (compile x2 identical).

Green: test-toy 1392/0, test-parse 3920+129/0, test-smoke-x64/rv64 3/0,
test-cg-api 0 fail, test-opt 0 fail, test-libc musl 18 + glibc 9; sqlite e2e
84|2 at -O0 and -O1; clang-differential probe matches (value-live-after-copy,
multi-copy chains, self-referential) at -O0/-O1/clang.

Diffstat:
Msrc/cg/memory.c | 10+++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/src/cg/memory.c b/src/cg/memory.c @@ -524,7 +524,15 @@ void kit_cg_store(KitCg* g, KitCgMemAccess access) { api_local_const_store(g, base.source_local, access, src.v.imm); } else { if (src.kind != OPK_LOCAL) src = api_force_local(g, &rv, ty); - if (src.v.local != dst.v.local) T->copy(T, dst, src); + if (src.v.local != dst.v.local) { + /* `x = <value in a dead transient>`: flag the source dead so the -O0 + * backend renames its register to x instead of emitting a routing mov + * (rv, its last reference, was popped above). */ + if (api_coalesce_on(g) && src.kind == OPK_LOCAL && + api_temp_dead(g, src.v.local)) + src.flags |= OPK_FLAG_KILL; + T->copy(T, dst, src); + } if (base.source_local != KIT_CG_LOCAL_NONE) api_local_const_clear(api_local_from_handle(g, base.source_local)); }