Exploring calling conventions with x86 assembly
If you have any experience with writing Asm you&#x27;ll see how rigid calling conventions are almost entirely an artificial construct of HLLs (and possibly an artifact of early compilers), and it&#x27;s possible to do so much more and so much better --- which is partly the reason why Asm can be so fun. ;-)<p>When calling functions written in Asm from Asm, you get to decide exactly how to do it: Pass arguments in registers in any order, on the stack, a combination of both, <i>directly following the call instruction itself</i>[1], etc.; the limit is practically your imagination. You can choose the best way to pass arguments for each function instead of being forced into one suboptimal one for every function. Ditto for return values --- you can easily return multiple values, in different registers, and also make use of HLL-inaccessible &quot;registers&quot; like the flags (carry bit in particular is quite useful).<p>I think the PC BIOS &#x2F; DOS API is a pretty nice calling convention, clearly designed for and by Asm programmers; all arguments are passed in registers and CF is used to indicate success&#x2F;error. These compiler-imposed calling conventions like cdecl&#x2F;stdcall&#x2F;fastcall are just awfully inefficient in comparison because of how much memory access they require, especially when &quot;fastcall&quot; can only pass <i>two</i> arguments in registers.<p>Incidentally, these 3 examples are also great at showing how compilers can be so <i>very</i> stupid at code generation. Observe that in all 3 cases, the return value in eax after calling foo is written to memory --- then immediately read from memory again, <i>into the exact same register</i>. This is not something that should ever appear in human-written Asm, and I&#x27;ve actually made use of this fact in marking a course assignment: asked to manually &quot;compile&quot; a short function, some students cheated and used the compiler (with no optimisations, i.e. the defaults), and it was dead easy to recognise.<p>It&#x27;s funny to see the <i>entirely-register-based</i> fastcall somehow still managing to generate 5 totally useless memory accesses. If I really wanted to write a fastcall min() function instead of just inlining it as I probably should, it&#x27;d be 4 lines:<p><pre><code>    mov eax, ecx
    cmp edx, ecx
    cmovl eax, edx
    ret
</code></pre>
Likewise, cdecl and stdcall (only differing in one instruction):<p><pre><code>    mov eax, [esp+4]
    cmp eax, [esp+8]
    cmovl eax, [esp+12]
    ret    ; ret 8 for stdcall
</code></pre>
... and seeing WINAPI in GAS&#x2F;AT&amp;T syntax just feels very <i>very</i> weird.<p>[1] Like this:<p><pre><code>    call puts
    db &quot;Hello world!&quot;, 0
    ; execution continues here
</code></pre>
I believe it&#x27;s not the fastest on modern CPUs, but it does save space and was a very common technique on 8-bit CPUs  like 8080&#x2F;8085&#x2F;Z80. It&#x27;s also a good way of confusing automatic disassemblers.