feat(cranelift): GHC calling convention by playX18 · Pull Request #13913 · bytecodealliance/wasmtime · GitHub
Skip to content

feat(cranelift): GHC calling convention - #13913

Open
playX18 wants to merge 4 commits into
bytecodealliance:mainfrom
playX18:ap/ghc-callconv
Open

feat(cranelift): GHC calling convention#13913
playX18 wants to merge 4 commits into
bytecodealliance:mainfrom
playX18:ap/ghc-callconv

Conversation

@playX18

@playX18 playX18 commented Jul 21, 2026

Copy link
Copy Markdown

This PR adds LLVM’s GHC (ghccc) calling convention to Cranelift as CallConv::Ghc / CLIF ghc, 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) do
heap-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_reg only gives you one reserved
register. tail helps with return_call, but it is still basically a
SysV-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_call between ghc functions.

Summary

  • Mirror LLVM STG register pinning: args only in fixed regs, no callee-saves, no return values, no stack spill of overflow args (Unsupported).
  • return_call allowed between ghc functions (supports_tail_calls); does not use Tail’s callee-pops-stack-args behavior.
  • x86_64: Sp is %rbp, so ghc frames are FP-less (RSP-relative spills only). Entry from SysV/Rust that needs Sp must use a naked asm trampoline (documented in cranelift/docs/ir.md); Cranelift system_v cannot place Sp in %rbp while using it as FP.
  • aarch64 / riscv64: Sp is not the hardware FP, so full STG arg lists work from system_v without asm.
  • Unsupported on s390x/pulley.
  • Exceptions not supported for ghc.

Test plan

  • Verifier: filetests/filetests/verifier/ghc-abi.clif
  • Precise-output: isa/{x64,aarch64,riscv64}/ghc.clif
  • cargo run -p cranelift-tools -- test ./filetests/filetests/isa/*/ghc.clif ./filetests/filetests/verifier/ghc-abi.clif

@playX18
playX18 marked this pull request as ready for review July 21, 2026 00:33
@playX18
playX18 requested review from a team as code owners July 21, 2026 00:33
@playX18
playX18 requested review from cfallin and removed request for a team July 21, 2026 00:33
@playX18 playX18 changed the title feat: GHC calling convention feat(cranelift): GHC calling convention Jul 21, 2026
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>
@playX18 playX18 closed this Jul 21, 2026
@playX18
playX18 deleted the ap/ghc-callconv branch July 21, 2026 01:53
@playX18
playX18 restored the ap/ghc-callconv branch July 21, 2026 01:55
@playX18 playX18 reopened this Jul 21, 2026
@playX18

playX18 commented Jul 21, 2026

Copy link
Copy Markdown
Author

@github-actions github-actions Bot added cranelift Issues related to the Cranelift code generator cranelift:area:machinst Issues related to instruction selection and the new MachInst backend. cranelift:area:aarch64 Issues related to AArch64 backend. cranelift:area:x64 Issues related to x64 codegen cranelift:docs labels Jul 21, 2026
@fitzgen

fitzgen commented Jul 21, 2026

Copy link
Copy Markdown
Member

FYI: I added an agenda item for discussing this feature to tomorrow's cranelift meeting

@cfallin

cfallin commented Jul 21, 2026

Copy link
Copy Markdown
Member

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

@playX18

playX18 commented Jul 22, 2026

Copy link
Copy Markdown
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cranelift:area:aarch64 Issues related to AArch64 backend. cranelift:area:machinst Issues related to instruction selection and the new MachInst backend. cranelift:area:x64 Issues related to x64 codegen cranelift:docs cranelift Issues related to the Cranelift code generator

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants