← board

aarch64: no stack-argument passing for the three C-ABI call kinds

The three sites, compiler/ir_codegen_aarch64.inc

kind guard
external call Procs[procIdx].ParamCount > 8
variadic external call nArgs > 8
cdecl indirect call nArgs > 8

There is a fourth, adjacent guard — (lo > 8) or (hi > 8), "exceeds 8 int or 8 fp argument registers" — which is about one BANK overflowing and is the same defect seen from the other side.

Why the internal helper does not answer it

EmitCallArgRegsA64 assigns register i to argument i. AAPCS64 for C does not: it classifies each parameter and draws from two independent banks, so argument 3 can be d0 and argument 4 x3. A by-REF float counts as a POINTER and belongs in the integer bank — the x86-64 twin of that line once put var d: Double in an SSE register while the callee read a pointer from a GPR, which is bug-a-a-by-ref-float-param-through-a-cdecl-fnptr-is-classified-sse and was a segfault rather than a wrong number.

What it owes before anyone starts

AAPCS64's stack rule is not "the ninth argument onward": it is NSAA, and once an argument is placed on the stack the banks are considered exhausted for the rest of the call, with per-type alignment of each stack slot. Getting that wrong is a crash at a library boundary, which is why this is parked rather than approximated.

Verify against the gcc oracle (tools/gcc_diff_probe.sh --target=aarch64), not against our own second implementation.


TWO OF THREE LANDED 2026-08-31 — frankC, in the C-ABI stack-argument group

Taken because it turned out to be a hard blocker on [[bug-c-a-c-function-s-calling-convention-depends-on-the-target]], which I had said it was not. That was wrong and the correction is worth more than the fix: this ticket's own summary said "nothing reaches it today", which was true of the externals pxx DECLARES and false of the C code pxx COMPILES. Under option A every C function uses the C ABI, so the population became every C program, and the corpus found it immediately:

subject before the flip with the flip, before this fix
c-testsuite aarch64 219 pass 217 pass, 2 fail (00170, 00204)
lua cross aarch64 six scripts pass build error in lua/src/lcode.c
sqlite threads aarch64 pass build error in sqlite3.c

Baseline measured, not assumed: stashed the group, rebuilt clean origin/master (3d5308a75742), and a nine-int C function prints 285 on aarch64.

What landed

One oracle, ABIA64CdeclArgSlot in abi.inc, answering "register or stack, which bank, which index, what offset" for one argument. The caller and the callee each used to count their own lo/hi, get the register half right, and refuse the stack half separately — which is this ticket's own diagnosis of the parent bug ("a missing mechanism wearing four names") arriving a second time. AAPCS64 stage C for scalars: independent NGRN/NSRN, a full bank sends its argument to NSAA and sets that bank to 8 while the other keeps allocating, and every scalar slot is 8 bytes — which is AAPCS64's own rounding for a type of 8 bytes or less, so the area needs no per-argument alignment.

Gate

Against gcc, which is the right instrument here and was available: a 12-argument (int,double)*6 signature and a 10-double one, both banks overflowing to the stack. pxx matches gcc exactly on x86-64, aarch64, arm32, i386 and riscv32 — five targets, one expected string, no pxx-side authority anywhere in it.

STILL OPEN: the cdecl indirect call

ir_codegen_aarch64.inc:3574 still refuses past 8 arguments. Deliberately left, and the ticket stays open for it:

Closing this ticket on two kinds out of three would be the false-green this repo keeps naming. The remaining kind is one refusal, at one line, with the oracle it needs already written.