Page 1 of 1

calling convention...

Posted: Thu Sep 10, 2009 7:37 am
by WeirdCat
Hi,

I'm working at the moment on my own x64 kernel. When I started I just used the same plain calling convention for procs as on x86 (all params on the stack and a stack frame), but the more I'm getting used to the x64 instruction set I'm thinking that this is suboptimal... you can't push imm64 directly onto the stack and imm32 only with sign-extension... etc.

In the x64 VC++ calling convention M$ decided (for varargs and the above reasons I suppose) that the caller is responsible to remove the params after the call from the stack, so they don't have to use PUSH/POP but can use a simple MOV. The problem with this approach is IMO that this will only be efficient if you allocate/free space for your params only once in every proc. This is a very simple thing if you are a compiler, you just look for the function call with the most params and reserve that much space in your prolog and release it in the epilog. But if you write your code in assembly by hand this is very cumbersome.

I just wanted to ask if somebody has given this already a thought and perhaps came up with a nice idea or perhaps I'm overlooking something here (It's only my first couple of days with the x64 instruction set :wink:)?

TIA

Re: calling convention...

Posted: Thu Sep 10, 2009 8:09 am
by Solar
Can't really help you on the calling convention, but...
Ash wrote:This is a very simple thing if you are a compiler, you just look for the function call with the most params and reserve that much space in your prolog and release it in the epilog. But if you write your code in assembly by hand this is very cumbersome.
...wellcome to the future. From the POV of chip desigers, writing assembler manually is dead, and you can't really blame them for that. :?

Re: calling convention...

Posted: Thu Sep 10, 2009 8:46 am
by Troy Martin
Personally, I'm starting to think x64 is a bit of a joke, and not a funny one at that.

First what you said above. And then they took out v86 mode and a whole whack of wonderful things they added for protected mode! Why the hell would they do that? Long mode isn't another processor architecture, it's just really an extension of pmode!

And then they took out opcodes that people had loved since the 1/286! PUSHA/POPA come to mind, so do the wonderful trend of PUSH immxx and POP immxx!

Then again, this could be a rant with no accuracy that's based on the fact that this morning is the second day of school and half my teachers are crazy waaaaahhhhh... :P

Re: calling convention...

Posted: Thu Sep 10, 2009 8:55 am
by thepowersgang
The accepted calling convention for x86_64 processors is essentially the same to cdecl (GCC's calling convention) except the first six arguments are passed in the registers rdi, rsi, rdx, rcx, r8 and r9 as well as being pushed to the stack.

As for cleaning up your stack, you don't need to preallocate your argument space, just add rsp, <numArgs>*8 after you call the function and your're done.

e.g.

Code: Select all

push 0x1111
push formatString
call printf
add rsp, 16

Re: calling convention...

Posted: Thu Sep 10, 2009 11:28 am
by earlz
It's a very sad day. I've now heard of at least 3 different calling conventions for x64..I was going to try to write a thing in hex code for automatically generated functions. You know what? It's not possible at this moment for x64 because the official calling convention for C compilers is not yet known(and some compilers don't allow you to force a different calling convention).

There is GCCs first 6 go into registers, and then the rest as cdecl
there is still the cdecl used sometimes
and there is Microsofts hack job that is like GCCs but it does something different that I can't remember

Re: calling convention...

Posted: Thu Sep 10, 2009 1:16 pm
by tantrikwizard
Ash wrote:<snip>...But if you write your code in assembly by hand this is very cumbersome...<snip>
not if you use a good assembler with proper macro support :wink:

Re: calling convention...

Posted: Fri Sep 11, 2009 6:31 am
by ru2aqare
earlz wrote:There is GCCs first 6 go into registers, and then the rest as cdecl
there is still the cdecl used sometimes
and there is Microsofts hack job that is like GCCs but it does something different that I can't remember
The MS calling convention dictates that the first four int64-or-less-sized arguments go to rcx, rdx, r8, r9 in that order. They are also allocated a so-called home location on the stack (meaning that stack space is allocated, but the value is not pushed on the stack - this is used if the argument gets its address taken). The rest of the arguments are stored on the stack. Floating-point values use registers xmm4-xmm7, if I remember correctly. The calling convention is basically cdecl (the function called doesn't do retn N but a simple retn; the caller deallocates the arguments from the stack), but there is a trick. You have to save nonvolatile registers that get trashed (rsi, rdi, rbp, r12-r15) and you are not allowed to modify the stack pointer once the function prologue has run. This means that if you call two functions, one which takes 5 arguments and one which takes 200, you have to allocate stack space for holding the larger number (200) of arguments (and also have to align rsp to a 16 byte boundary). When you call the first function (the one that takes 5 arguments) the upper portion of this stack space is unused, and only the lower 5 'slots' are used; and even then the first 4 arguments go to registers and only the fifth is actually stored on the stack. If I recall correctly, this was introduced so that the stack could be unwound easily. There is an official specification out there, google it.
I consider it a good solution that they did away with the different calling conventions (x86 used cdecl, stdcall, different versions of fastcall, etc), but the stack unwinding thingy is unnecessary complication at first. Especially when you write assembly code that has to call functions coded in C - you have to make sure the stack frame is as the C compiler expects. When writing leaf functions (functions that don't call other functions) in some cases you can do away with the stack frame entirely.

I don't know exactly what calling convention GCC uses, but expect it more or less the same (register-based cdecl with optionally preallocating space for all arguments).

It is indeed unfortunate that MSVC and GCC use slightly different calling conventions.

Re: calling convention...

Posted: Tue Sep 15, 2009 4:57 pm
by Owen
GCC's (Well, AMD's, they created it) is quite simple and logical; just see the reference manual. It's not too heavy reading considering it's about assembly.

As for the removal of legacy features: I can't help but think AMD were designing long mode so that in the future they can ship processors with only Long Mode support, with the intention being to discard much of x86's cruft. I wouldn't be too surprised to see, a few years down the line, a processor which boots straight into long mode and no longer supports said legacy modes come along.

Re: calling convention...

Posted: Tue Sep 15, 2009 4:59 pm
by ru2aqare
Owen wrote: As for the removal of legacy features: I can't help but think AMD were designing long mode so that in the future they can ship processors with only Long Mode support, with the intention being to discard much of x86's cruft. I wouldn't be too surprised to see, a few years down the line, a processor which boots straight into long mode and no longer supports said legacy modes come along.
I think most of us would be happy to see the legacy modes go... I just don't see that (or the BIOS going away) happening for quite some time.