commit ba542bfde60e045ffc887bfdcb11f8006a406eef
parent 21fc76d188106a6891e6a316b6daffbba380f03f
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Sat, 13 Jun 2026 17:47:50 -0700
perf(cg): -O0 fp args into xmm0..3 (Lever 1, x64)
Front the fp ARG/RET registers xmm0..xmm3 at the head of x64_fp_allocable[]
so the -O0 NDT value-cache pool (scanned in order by nd_cache_alloc) makes
fp-arg producers materialize directly into the ABI arg registers, removing
the post-materialize shuffle into the call slots. xmm0/xmm1 are also the fp
ret regs but ndt_result_reg_stable is OFF for x64, so the fp result still
moves out of xmm0 via the post-call path (often a no-op). The INT pool is
unchanged (RAX/RDX div-mul, RCX shift-count, R8/R9 driver scratch stay out);
xmm4/xmm5 (driver scratch) and xmm14/xmm15 (emit scratch) stay out of the pool.
Gate (codesize track: run-correctness + determinism, not byte-identity):
- make bin RELEASE=1: clean, no warnings (-Werror).
- make test-smoke-x64: 3 pass, 0 fail.
- make test-toy: 1392 pass, 0 fail, 35 skip (pre-existing structural skips).
- Functional x86_64-macos run (fp(d,d,i,d,i), mixed int/fp args, fp-result-
as-next-arg, shared fp temp, nested fp calls): output byte-identical to
clang -target x86_64-apple-macos.
- Determinism: two compiles of the same .c are byte-identical objects.
Measured on a fp/call-heavy source (x86_64-macos -O0):
total insns 220 -> 202 (-8.2%), movsd 128 -> 110 (-14.1%),
.text 390 -> 336 B (-13.8%), object 1459 -> 1371 B (-6.0%).
The entire delta is movsd elimination; all other mnemonics unchanged.
Diffstat:
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/src/arch/x64/native.c b/src/arch/x64/native.c
@@ -294,10 +294,15 @@ static const NativePhysRegInfo x64_int_phys[] = {
.spill_cost = 0u, \
.copy_cost = 0u}
-/* Allocable FP pool: xmm6..xmm11 (keep arg/ret xmm0..5 clear). xmm4/xmm5 are
- * driver scratch; xmm14/xmm15 are emit scratch. */
+/* Allocable FP pool: arg/ret xmm0..xmm3 lead so -O0 fp-arg producers
+ * materialize directly into the ABI arg registers (Lever 1), then xmm6..xmm11.
+ * xmm0/xmm1 are also the fp ret regs but ndt_result_reg_stable is OFF, so the
+ * fp result still moves out of xmm0 via the post-call path (often a no-op).
+ * xmm4/xmm5 stay driver scratch; xmm14/xmm15 are emit scratch. */
static const Reg x64_fp_allocable[] = {
- X64_XMM6, X64_XMM7, X64_XMM8, X64_XMM0 + 9, X64_XMM0 + 10, X64_XMM0 + 11};
+ X64_XMM0, X64_XMM1, X64_XMM2, X64_XMM3,
+ X64_XMM6, X64_XMM7, X64_XMM8, X64_XMM0 + 9,
+ X64_XMM0 + 10, X64_XMM0 + 11};
static const Reg x64_fp_scratch[] = {X64_XMM4, X64_XMM5};
static const NativePhysRegInfo x64_fp_phys[] = {