kit

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

commit 0dcabba636a542050faf4f2e60e3755b217dac35
parent 6a675ba495996db615fb007194c6c42ed0bafb7c
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Sun, 10 May 2026 13:38:47 -0700

arch/cg: thread RegClass through free_reg

Make CGTarget.free_reg take (Reg, RegClass) so backends pick the
right pool by class instead of probing int-then-fp by numeric range.
The probe-by-range strategy mis-routed when pools had overlapping
register numbers — rv64 set both int and fp pools at base=18, so
freeing fs2 found s2 first and panicked "already free".

Per-arch free_reg now switches on cls to pick the pool. All call
sites have the class on hand: Operand.cls for storage releases,
class_of_sv for SValue releases, and statically RC_INT for INDIRECT
bases (always pointer-typed) and indirect callees (function ptrs).

aa64 had the same latent overlap in regs 19..23 but the cg corpus
never allocated fp index >= 11; this closes that gap too.

rv64 parse: 153/10/1 → 163/0/1 (the 10 FP-allocating cases that hit
the rv64 overlap now pass at L0 and L1, R and E paths). aa64 cg and
parse suites unchanged.

Diffstat:
Msrc/arch/aarch64.c | 32+++++++++++++++++++-------------
Msrc/arch/arch.h | 2+-
Msrc/arch/rv64.c | 28++++++++++++++++------------
Msrc/arch/x64.c | 3++-
Msrc/cg/cg.c | 14++++++++------
Msrc/opt/opt.c | 3++-
6 files changed, 48 insertions(+), 34 deletions(-)

diff --git a/src/arch/aarch64.c b/src/arch/aarch64.c @@ -403,7 +403,7 @@ static AASlot* slot_get(AAImpl* a, FrameSlot fs); static u32 force_reg_int(CGTarget* t, Operand op, u32 sf, u32 scratch); static void aa_load(CGTarget* t, Operand dst, Operand addr, MemAccess ma); static void aa_store(CGTarget* t, Operand addr, Operand src, MemAccess ma); -static void aa_free_reg(CGTarget* t, Reg r); +static void aa_free_reg(CGTarget* t, Reg r, RegClass cls); /* ---- helpers ---- */ @@ -820,19 +820,25 @@ static Reg aa_alloc_reg(CGTarget* t, RegClass cls, const Type* ty) { compiler_panic(t->c, a->loc, "aarch64 alloc_reg: class %d unimpl", (int)cls); } -static void aa_free_reg(CGTarget* t, Reg r) { +static void aa_free_reg(CGTarget* t, Reg r, RegClass cls) { AAImpl* a = impl_of(t); - RegPool* pools[2] = {&a->int_pool, &a->fp_pool}; - for (u32 i = 0; i < 2; ++i) { - int rc = regpool_free(pools[i], r); - if (rc == 1) return; - if (rc == -1) { - compiler_panic(t->c, a->loc, "aarch64 free_reg: reg %u already free", - (unsigned)r); - } + RegPool* p; + switch (cls) { + case RC_INT: p = &a->int_pool; break; + case RC_FP: p = &a->fp_pool; break; + default: + compiler_panic(t->c, a->loc, "aarch64 free_reg: class %d unimpl", + (int)cls); + } + int rc = regpool_free(p, r); + if (rc == 1) return; + if (rc == -1) { + compiler_panic(t->c, a->loc, + "aarch64 free_reg: reg %u already free in %s pool", + (unsigned)r, cls == RC_FP ? "fp" : "int"); } - compiler_panic(t->c, a->loc, "aarch64 free_reg: reg %u not a scratch reg", - (unsigned)r); + compiler_panic(t->c, a->loc, "aarch64 free_reg: reg %u not in %s pool", + (unsigned)r, cls == RC_FP ? "fp" : "int"); } static FrameSlot aa_frame_slot(CGTarget* t, const FrameSlotDesc* d) { @@ -971,7 +977,7 @@ static void aa_spill_reg(CGTarget* t, Operand src, FrameSlot slot, addr.type = ma.type; addr.v.frame_slot = slot; aa_store(t, addr, src, ma); - aa_free_reg(t, src.v.reg); + aa_free_reg(t, src.v.reg, src.cls); } static void aa_reload_reg(CGTarget* t, Operand dst, FrameSlot slot, diff --git a/src/arch/arch.h b/src/arch/arch.h @@ -454,7 +454,7 @@ struct CGTarget { * Real targets return physical scratch registers and implement spill/reload * mechanics; opt_cgtarget returns fresh virtual regs and ignores spills. */ Reg (*alloc_reg)(CGTarget*, RegClass, const Type*); - void (*free_reg)(CGTarget*, Reg); /* hint; opt_cgtarget ignores */ + void (*free_reg)(CGTarget*, Reg, RegClass); /* hint; opt_cgtarget ignores */ FrameSlot (*frame_slot)(CGTarget*, const FrameSlotDesc*); void (*param)(CGTarget*, const CGParamDesc*); const Reg* (*clobbers)(CGTarget*, RegClass, u32* nregs); diff --git a/src/arch/rv64.c b/src/arch/rv64.c @@ -546,19 +546,23 @@ static Reg rv_alloc_reg(CGTarget* t, RegClass cls, const Type* ty) { compiler_panic(t->c, a->loc, "rv64 alloc_reg: class %d unimpl", (int)cls); } -static void rv_free_reg(CGTarget* t, Reg r) { +static void rv_free_reg(CGTarget* t, Reg r, RegClass cls) { RImpl* a = impl_of(t); - RegPool* pools[2] = {&a->int_pool, &a->fp_pool}; - for (u32 i = 0; i < 2; ++i) { - int rc = regpool_free(pools[i], r); - if (rc == 1) return; - if (rc == -1) { - compiler_panic(t->c, a->loc, "rv64 free_reg: reg %u already free", - (unsigned)r); - } + RegPool* p; + switch (cls) { + case RC_INT: p = &a->int_pool; break; + case RC_FP: p = &a->fp_pool; break; + default: + compiler_panic(t->c, a->loc, "rv64 free_reg: class %d unimpl", (int)cls); + } + int rc = regpool_free(p, r); + if (rc == 1) return; + if (rc == -1) { + compiler_panic(t->c, a->loc, "rv64 free_reg: reg %u already free in %s pool", + (unsigned)r, cls == RC_FP ? "fp" : "int"); } - compiler_panic(t->c, a->loc, "rv64 free_reg: reg %u not a scratch reg", - (unsigned)r); + compiler_panic(t->c, a->loc, "rv64 free_reg: reg %u not in %s pool", + (unsigned)r, cls == RC_FP ? "fp" : "int"); } static FrameSlot rv_frame_slot(CGTarget* t, const FrameSlotDesc* d) { @@ -730,7 +734,7 @@ static void rv_spill_reg(CGTarget* t, Operand src, FrameSlot slot, addr.type = ma.type; addr.v.frame_slot = slot; rv_store(t, addr, src, ma); - rv_free_reg(t, src.v.reg); + rv_free_reg(t, src.v.reg, src.cls); } static void rv_reload_reg(CGTarget* t, Operand dst, FrameSlot slot, diff --git a/src/arch/x64.c b/src/arch/x64.c @@ -32,8 +32,9 @@ static Reg xx_alloc_reg(CGTarget* t, RegClass cls, const Type* ty) { (void)ty; xx_panic(t, "alloc_reg"); } -static void xx_free_reg(CGTarget* t, Reg r) { +static void xx_free_reg(CGTarget* t, Reg r, RegClass cls) { (void)r; + (void)cls; xx_panic(t, "free_reg"); } static FrameSlot xx_frame_slot(CGTarget* t, const FrameSlotDesc* d) { diff --git a/src/cg/cg.c b/src/cg/cg.c @@ -399,7 +399,7 @@ static SValue* pick_victim(CG* g, u8 cls) { * runtime ownership and need nothing. */ static void release_arg_storage(CG* g, const Operand* st) { if (st->kind == OPK_REG) { - g->target->free_reg(g->target, st->v.reg); + g->target->free_reg(g->target, st->v.reg, st->cls); } else if (st->kind == OPK_LOCAL) { return_spill_slot(g, st->v.frame_slot, st->cls); } @@ -500,7 +500,7 @@ static void ensure_reg(CG* g, SValue* sv) { * stack and not consumed by a downstream operation. */ static void release(CG* g, SValue* sv) { if (sv->res == RES_REG) { - g->target->free_reg(g->target, (Reg)reg_of_sv(sv)); + g->target->free_reg(g->target, (Reg)reg_of_sv(sv), class_of_sv(sv)); } else if (sv->res == RES_SPILLED) { return_spill_slot(g, sv->spill_slot, class_of_sv(sv)); sv->spill_slot = FRAME_SLOT_NONE; @@ -897,9 +897,10 @@ static Operand force_reg(CG* g, SValue* v, const Type* ty) { T->load_imm(T, dst, v->op.v.imm); } else if (is_lvalue(&v->op)) { T->load(T, dst, v->op, mem_for_lvalue(g, &v->op, ty)); - /* Old INDIRECT base reg is no longer referenced — release it. */ + /* Old INDIRECT base reg is no longer referenced — release it. + * INDIRECT bases are always pointer-typed (RC_INT). */ if (v->op.kind == OPK_INDIRECT) { - T->free_reg(T, v->op.v.ind.base); + T->free_reg(T, v->op.v.ind.base, RC_INT); } } else { compiler_panic(g->c, g->cur_loc, "cg: cannot force operand to register"); @@ -982,7 +983,7 @@ void cg_inc_dec(CG* g, BinOp op, int post) { /* Free whichever register is NOT being returned, plus any base reg the * lvalue owned. */ - T->free_reg(T, post ? r_new : r_old); + T->free_reg(T, post ? r_new : r_old, type_class(ty)); release(g, &lv); push(g, make_sv(post ? o_old : o_new, ty)); } @@ -1155,7 +1156,8 @@ void cg_call(CG* g, u32 nargs, const Type* fn_type) { g->avs_in_flight_n = 0; if (callee.op.kind != OPK_GLOBAL) { - T->free_reg(T, callee_op.v.reg); + /* Indirect callees are function pointers; they live in int regs. */ + T->free_reg(T, callee_op.v.reg, RC_INT); } if (has_result) { push(g, make_sv(desc.ret.storage, ret_ty)); diff --git a/src/opt/opt.c b/src/opt/opt.c @@ -112,9 +112,10 @@ static Reg w_alloc_reg(CGTarget* t, RegClass cls, const Type* ty) { return (Reg)v; } -static void w_free_reg(CGTarget* t, Reg r) { +static void w_free_reg(CGTarget* t, Reg r, RegClass cls) { (void)t; (void)r; + (void)cls; } static FrameSlot w_frame_slot(CGTarget* t, const FrameSlotDesc* d) {