Stream: git-wasmtime

Topic: wasmtime / PR #13913 feat(cranelift): GHC calling convention


view this post on Zulip Wasmtime GitHub notifications bot (Jul 21 2026 at 00:34):

playX18 edited PR #13913.

view this post on Zulip Wasmtime GitHub notifications bot (Jul 21 2026 at 01:49):

playX18 updated PR #13913.

view this post on Zulip Wasmtime GitHub notifications bot (Jul 21 2026 at 01:50):

:cross_mark: playX18 closed without merge PR #13913.

view this post on Zulip Wasmtime GitHub notifications bot (Jul 21 2026 at 01:55):

playX18 reopened PR #13913 from playX18:ap/ghc-callconv to bytecodealliance:main.

view this post on Zulip Wasmtime GitHub notifications bot (Jul 21 2026 at 01:58):

playX18 commented on PR #13913:

Sorry, messed up the fork. Had to re-create branch.

view this post on Zulip Wasmtime GitHub notifications bot (Jul 21 2026 at 02:33):

playX18 updated PR #13913.

view this post on Zulip Wasmtime GitHub notifications bot (Jul 21 2026 at 05:37):

github-actions[bot] added the label cranelift on PR #13913.

view this post on Zulip Wasmtime GitHub notifications bot (Jul 21 2026 at 05:37):

github-actions[bot] added the label cranelift:area:machinst on PR #13913.

view this post on Zulip Wasmtime GitHub notifications bot (Jul 21 2026 at 05:37):

github-actions[bot] added the label cranelift:area:aarch64 on PR #13913.

view this post on Zulip Wasmtime GitHub notifications bot (Jul 21 2026 at 05:37):

github-actions[bot] added the label cranelift:docs on PR #13913.

view this post on Zulip Wasmtime GitHub notifications bot (Jul 21 2026 at 05:37):

github-actions[bot] added the label cranelift:area:x64 on PR #13913.

view this post on Zulip Wasmtime GitHub notifications bot (Jul 21 2026 at 21:41):

fitzgen commented on PR #13913:

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

view this post on Zulip Wasmtime GitHub notifications bot (Jul 21 2026 at 21:47):

cfallin commented on PR #13913:

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

view this post on Zulip Wasmtime GitHub notifications bot (Jul 22 2026 at 01:31):

playX18 commented on PR #13913:

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

(ctx, callee, argc, arg0, arg1, arg2, arg3)

where ctx contains runtime state such as the allocation pointer, stack/runstack (not machine stack!) pointers for overflow arguments (arg4+ go onto this runstack), exception state, and GC metadata.
Each call is always a tail-call (aka jump) to either procedure or heap allocated continuation.

The important requirements for me are:

Compatibility with GHC callconv and LLVM's ghccc is not really necessary for me. I simply chose ghccc because it exists, is easy to find how it works in LLVM depths and is being utilized by some FP compilers that are not GHC with similar purposes: no callee saves, efficient tail calls, and a lot of effectively "pinned" registers even if compiler does not guarantee that.

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 aarc64, 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.

view this post on Zulip Wasmtime GitHub notifications bot (Jul 22 2026 at 01:31):

playX18 edited a comment on PR #13913:

@cfallin

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

(ctx, callee, argc, arg0, arg1, arg2, arg3)

where ctx contains runtime state such as the allocation pointer, stack/runstack (not machine stack!) pointers for overflow arguments (arg4+ go onto this runstack), exception state, and GC metadata.
Each call is always a tail-call (aka jump) to either procedure or heap allocated continuation.

The important requirements for me are:

Compatibility with GHC callconv and LLVM's ghccc is not really necessary for me. I simply chose ghccc because it exists, is easy to find how it works in LLVM depths and is being utilized by some FP compilers that are not GHC with similar purposes: no callee saves, efficient tail calls, and a lot of effectively "pinned" registers even if compiler does not guarantee that.

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 aarc64, 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.

view this post on Zulip Wasmtime GitHub notifications bot (Jul 22 2026 at 01:32):

playX18 edited a comment on PR #13913:

@cfallin

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

(ctx, callee, argc, arg0, arg1, arg2, arg3)

where ctx contains runtime state such as the allocation pointer, stack/runstack (not machine stack!) pointers for overflow arguments (arg4+ go onto this runstack), exception state, and GC metadata.
Each call is always a tail-call (aka jump) to either procedure or heap allocated continuation.

The important requirements for me are:

Compatibility with GHC callconv and LLVM's ghccc is not really necessary for me. I simply chose ghccc because it exists, is easy to find how it works in LLVM depths and is being utilized by some FP compilers that are not GHC with similar purposes: no callee saves, efficient tail calls, and a lot of effectively "pinned" registers even if compiler does not guarantee that.

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.


Last updated: Jul 29 2026 at 05:03 UTC