kit

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

commit 733201001ee4b7198a384c764d4110e85afba737
parent c562293046d7bf431615d75bb41393db83b99197
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Sat,  9 May 2026 12:22:36 -0700

cg: add cg_tail_call and CG_CALL_TAIL flag on CGCallDesc

Interface-only. Lets the parser/opt mark a call as a sibling call so
the AArch64 backend (and others) can lower as caller-epilogue + B/BR
instead of BL+RET. Must-tail semantics: legality is the caller's job;
the backend panics if it cannot honor the request.

Diffstat:
Msrc/arch/arch.h | 12++++++++++++
Msrc/cg/cg.h | 8++++++++
2 files changed, 20 insertions(+), 0 deletions(-)

diff --git a/src/arch/arch.h b/src/arch/arch.h @@ -268,12 +268,24 @@ typedef struct CGFuncDesc { SrcLoc loc; } CGFuncDesc; +typedef enum CGCallFlag { + CG_CALL_NONE = 0, + /* Sibling call. The target emits the caller's epilogue, transfers + * control to the callee (B/JUMP26 for direct, BR Xn for indirect), and + * does NOT emit a return-style continuation. CG will not invoke + * target->ret afterwards. The target panics if it cannot honor the + * request — legality is the caller's responsibility (see cg_tail_call). */ + CG_CALL_TAIL = 1u << 0, +} CGCallFlag; + typedef struct CGCallDesc { const Type* fn_type; const ABIFuncInfo* abi; Operand callee; const CGABIValue* args; u32 nargs; + u16 flags; /* CGCallFlag */ + u16 pad; CGABIValue ret; } CGCallDesc; diff --git a/src/cg/cg.h b/src/cg/cg.h @@ -56,6 +56,14 @@ void cg_convert(CG*, const Type* dst); /* picks ConvKind from sr * index source of truth). */ void cg_call(CG*, u32 nargs, const Type* fn_type); /* stack: [..., callee, arg0..argN-1] → result (if non-void) */ +/* Sibling call: pops [callee, arg0..argN-1], lowers as a tail call. The + * caller's epilogue runs and control transfers to the callee, whose RET + * returns to the caller's caller. cg_tail_call implicitly terminates the + * function — the parser must NOT follow it with cg_ret. v1 has must-tail + * semantics: legality (ABI-compatible return, args fit in registers, no + * caller-frame pointer escapes via byval) is the caller's responsibility; + * the backend panics if it cannot honor the tail request. */ +void cg_tail_call(CG*, u32 nargs, const Type* fn_type); void cg_ret (CG*, int has_value); /* ----- C declarations and global initializers -----