6

So, I'm learning assembly, and I've come to know the ABIs and i got some basics tests working using the cdecl calling convention to use the c's stdlib under nasm. But I've seen other Calling Conventions (like topspeed/Clarion/JPI/watcom/borland register(delphi), fastcall, etc). And I wonder, what are the REAL benefits of using cdecl instead of Clarion. More specifically Pushing instead of using the registers.

This are some of the benefits i imagined please let me know which of these apply.

  1. I've read that cdecl is important because it allows to use variable parameters, but as I see it, I can do the same using registers. The problems are to know the count, type and order of the params, but that problem exists on cdecl too. It can be inferred from the format string (in printf) or from the function signature. And if I run out of registers I can push the rest of the params.

  2. I imagine that performance shouldn't be a big thing because cpus manufactures might have optimized everything that they could regardless of the calling convention of mayor use (and this time is cdecl?¿?). But if I think of raw operations (and disregard caches and writeback buffers) I think it should be faster to use registers instead of the stack (which should be located in ram right?). I mean, is like "inc eax" vs "add eax, 1" (am I right?).

  3. By pushing i create a memory space that goes off when I ret (just like creating a local variable). That might seem handy (to have a local var just by the time of being called).
    But, as most params (in my experience) are used as a read value, and very few times we need them as a variable, to store mutated values, and when you do you are actually dealing with complex structures that are passed as pointers anyway.
    So I don't really see the value on creating a memory space BEFORE actually needing it.

    As I see, if I do need a variable is better to have the option to create it (like in clarion), but if I don't it would be nice to be able to not create it (like you can't in cdecl).

  4. You preserve the registers.
    I can't even think of a positive side. AFAIK the registers are used for intermediary calculations, and as such are volatile in nature. If I think of them as volatile I could push/pop/mov them whenever i need to "hold" the value and only in such cases. In that sense I see it as the most efficient use (I only access the ram when i need it).

But if I don't do that, and try to "preserve the registers":

  • callee: I have no certainty whatsoever what the callee code will do with a register (unless is being documented or they adhere to the same calling convention). By hoping it will preserve them, it imposes an artificial constraint on the callee. As the callee has no idea which registers need to be really preserved they'll tend to overpreserve unnecessary registers. (like pusha/popa in x86?) Which sounds really inefficient to me.

  • caller: the caller have no idea what registers will the callee [s]taint?[/s] use, so it'll just preserve all of them. It'll end up with the same inefficient result as before.

I noticed that linux syscalls as well as 8086 (from my old classes) use registers instead of stack to pass parameters. What happened there?

So those are my thoughts, thanks for all the clarification possible.

Notes:

  • I am learning, most of this are assumptions based on what i've readed/tried so far. i'll be glad if you correct me as needed (just be nice).
  • I do understand this are all x86 CC, and in 64 there is another (which i'm not familiar with).
  • I am not asking which is the "best", i just want to understand more and clarify my assumptions.
  • I am looking for benefits of the calling convention by itself (by contrasting with others). not by it's side-effects (like compilers and cpus being optimized or its ubiquity)
  • Basically a theoretical question to understand more of why that way was chosen
  • cdecl is important because the C programming language uses it, which makes cdecl the most portable/universal of all the calling conventions. – Robert Harvey Oct 21 '15 at 4:11
  • 1
    Sorry like the title implies, i am looking for benefits of the calling convention by itself. not by it's sideeffects (like compilers, or cpu being optimized or its ubiquity) – Nande Oct 21 '15 at 4:14
  • There might not be any significant benefits, other than its ubiquity. A calling convention is just an agreement between the caller and the callee. As you've already pointed out in your question, there are many ways to do that successfully, all of which work more or less equally well. – Robert Harvey Oct 21 '15 at 4:32
  • there are only so many registers and if the registers are being used for parameter passing, then what happens when some of those registers are needed for the body of a function? Then the registers are saved (on the stack). And when the call stack is of any real depth, you will run out of available registers real fast. The only real difference is: does the caller push/pop the passed parameters to/from the stack (cdecl) or does the callee do that?(stdcal) In general, the stdcal ties up more registers than cdecl, so more of the registers need to be saved/restored by the callee – user3629249 Oct 21 '15 at 5:19
  • 1
    " then what happens when some of those registers are needed for the body of a function?" Well, then, and only then, you push them. Benefit could be marginal, but the difference is there (in my opinion). You have the chance to choose whether you push or not. Of course that would impose a lot of complexity on the compiler. – Nande Oct 21 '15 at 5:21
5

Raymond Chen put together a history of calling conventions here. While he doesn't touch on Clarion, he does touch on Fastcall, which while not the same as Clarion does use more of a register-based approach.

He has this to say:

Fastcall (__fastcall)

The Fastcall calling convention passes the first parameter in the DX register and the second in the CX register (I think). Whether this was actually faster depended on your call usage. It was generally faster since parameters passed in registers do not need to be spilled to the stack, then reloaded by the callee. On the other hand, if significant computation occurs between the computation of the first and second parameters, the caller has to spill it anyway. To add insult to injury, the called function often spilled the register into memory because it needed to spare the register for something else, which in the "significant computation between the first two parameters" case means that you get a double-spill. Ouch!

Consequently, __fastcall was typically faster only for short leaf functions, and even then it might not be.

I believe the criticism applied here is still relevant - Clarion is likely faster for certain types of calls, but not others.

That being said, your points about the register usage are quite valid. While you did not want to consider x64 in the scope of your question, the pattern discussed later in that series for Itanium might interest you!

  • Thank you so much! That explains more and exposes more of the consequences of the design. i will read your links deeper. I didn't want to mix x86_64 because it is different, and also i don't know it as much, but i am interested into learn the reasons why they've decided to chose another CC. – Nande Oct 24 '15 at 23:12
1

Calling conventions depend somewhat on the platform. On Linux, GCC provides the defacto standard. This page goes into some detail about the pros and cons of various calling conventions on Linux (albeit with a bit of hyperbole):

Often you will be told that using C library (libc) is the only way, and direct system calls are bad. This is true, to some extent. In general, you must know that libc is not sacred, and in most cases it only does some checks, then calls kernel, and then sets errno. You can easily do this in your program as well (if you need to), and your program will be dozen times smaller, and this will result in improved performance as well, just because you're not using shared libraries (static binaries are faster)

In the Windows world, there are a number of calling conventions, some of which are Microsoft-specific, and some which are no longer supported. This page explains some of the pros and cons of each. In particular:

__cdecl is the default calling convention for C and C++ programs. Because the stack is cleaned up by the caller, it can do vararg functions. The __cdecl calling convention creates larger executables than __stdcall, because it requires each function call to include stack cleanup code.

The __stdcall calling convention is used to call Win32 API functions. The callee cleans the stack, so the compiler makes vararg functions __cdecl. Functions that use this calling convention require a function prototype.

  • Thanks! I'm reading the links. I've readed that argument about the varargs, but i think it could be implemented using registers, am i missing something? – Nande Oct 21 '15 at 4:53
  • Your goal should be to not do something exotic. The closer you are to a generally-accepted standard, the better. – Robert Harvey Oct 21 '15 at 5:08
  • 1
    That's not my objective. I'm trying to learn (about the other calling conventions). – Nande Oct 21 '15 at 5:14
  • That link is cool. i like the first 3 sentences in your post. also it says "The user-stack is not touched, so you needn't have a valid one when doing a syscall." is that a pro? i don't need to set up a proper stack? – Nande Oct 21 '15 at 5:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.