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>
|
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) |

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