feat(cranelift): GHC calling convention#13913
Conversation
GHC entries have RSP≡8 (return address only). A 16-aligned frame would leave RSP≡8 before `call`, violating the SysV ABI. Pad by 8 when the function makes any regular call. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Sorry, messed up the fork. Had to re-create branch. |
|
FYI: I added an agenda item for discussing this feature to tomorrow's cranelift meeting |
|
@playX18 can you summarize (in a bit more detail) what your use-case for this is? I ask because it's not a trivial change to our ABI code -- this adds a bunch of parallel paths that are conditional on the new calling convention, +1.2kLoC overall, which is a lot of maintenance burden in critical code that we may need to update for security fixes, optimizations, new features, etc. in the future. If this serves a significant and widespread use-case that benefits many users, it may be worth it; but we'll want to weigh this not as a "nice to have" feature with a ready PR that we can merge and move on from, but rather, something with ongoing cost. And if, for example, the desire is to pin more registers with args in a tail-call-between-basic-blocks style, as your description suggests: would an extension to an existing ABI, where we "keep counting beyond the limit" (i.e. SysV but we go beyond the 6 existing int regs on x86-64), do as well for you? That kind of thing would be a lot simpler to merge into the existing code. Or do you really need compat with the GHC ABI? (Looking forward to discussing in the meeting tomorrow; the purpose of this comment is to fact-find beforehand) |
My use-case is a Scheme implementation whose compiler lowers programs into CPS. Calls between compiled Scheme procedures are effectively jumps carrying a continuation and a set of VM values. The current calling convention is approximately: where The important requirements for me are:
Compatibility with GHC callconv and LLVM's ghccc is not really necessary for me. I simply chose An extended Cranelift-native ABI—roughly “SysV/AAPCS register assignment, but continue allocating from an explicitly defined larger register set”—would likely work for me, but callee-saves are sort of a problem: they increase the code size and somewhat decrease performance (Tail callconv currently requires frame pointer to always be available). On my compiler I have verified overall ~18% compiled code size reduction compared to Tail cc, and most importantly 5% to 15% performance increase (depending on benchmarks). I am also happy to narrow the initial implementation to x86-64 and aarch64, which are my immediate targets, rather than adding three architectures at once. So, to summarize: I need a register-heavy, tail-call-oriented internal ABI, but I do not specifically need GHC interoperability. I would be happy to rework this toward a smaller Cranelift-specific convention if that is the direction everyone prefers. |
This PR adds LLVM’s GHC (
ghccc) calling convention to Cranelift asCallConv::Ghc/ CLIFghc, for x86_64, aarch64, and riscv64.Why?
People writing compilers for functional languages often want "register
pinning": keep runtime / VM state in fixed hardware registers, and jump
between functions instead of doing normal C calls. Haskell does this with
the STG machine and LLVM's
ghccc. SML/NJ (and similar CPS systems) doheap-allocated continuations and need something like jump-with-arguments.
Scheme compilers in CPS style want the same kind of thing.
In Cranelift today,
enable_pinned_regonly gives you one reservedregister.
tailhelps withreturn_call, but it is still basically aSysV-like ABI: it still builds native frames, and it does not pin a big
set of STG-style registers for you.
So this PR adds LLVM's GHC calling convention. Then you can map many
virtual machine registers to real regs through the callconv, with no
callee-saves, and use
return_callbetweenghcfunctions.Summary
Unsupported).return_callallowed betweenghcfunctions (supports_tail_calls); does not use Tail’s callee-pops-stack-args behavior.%rbp, soghcframes are FP-less (RSP-relative spills only). Entry from SysV/Rust that needs Sp must use a naked asm trampoline (documented incranelift/docs/ir.md); Craneliftsystem_vcannot place Sp in%rbpwhile using it as FP.system_vwithout asm.ghc.Test plan
filetests/filetests/verifier/ghc-abi.clifisa/{x64,aarch64,riscv64}/ghc.clifcargo run -p cranelift-tools -- test ./filetests/filetests/isa/*/ghc.clif ./filetests/filetests/verifier/ghc-abi.clif