A shared binary contract
Caller and callee must agree on argument and result locations. They must also know which registers a call may overwrite and which the callee must preserve, so later instructions do not use clobbered values.
These rules are distinct from the function’s intended source-level behavior. Apparently matching declarations do not ensure a correct call when the compiled sides disagree about binary passing rules.
Different platform arrangements
Arm’s AArch64 procedure-call standard uses general-purpose argument registers and distinguishes scratch from preserved registers, with additional rules for floating-point, vector and aggregate values.
Microsoft’s default x64 convention assigns its first four argument positions to specified integer or floating-point registers and requires caller-reserved stack space. Later parameters use the stack; this is not a universal rule for all 64-bit machines.
Function form adds rules even on one platform. Microsoft x64 requires caller-provided shadow space for four register parameters even when fewer are passed; for vararg or unprototyped calls, floating-point values in the first four argument positions must also be duplicated in the corresponding general-purpose registers. Reusing a fixed-signature floating-point call sequence for every variadic call can therefore violate the binary contract.
Passing depends on data layout
Aggregate alignment and field sizes affect how a value is passed, including when an indirect address is required. Microsoft’s x64 software conventions therefore specify scalar sizes, structure layout and alignment alongside calling rules.
A function-call convention must also be distinguished from a system-call interface for entering the kernel. Cross-language interfaces and assembly code require agreement on platform, function kind and data representation, not names alone.