First of all: C is intended to hide calling conventions and everything that is specific to how your code is executed from the programmer and provide an abstract layer above it.
The only condition when you need to (as you say) "manually" call a function is when you do it from asm
.
C as a language has no direct control over the stack or the program counter.
To cite from GCC manual for fastcall
for x86:
On the Intel 386, the `fastcall' attribute causes the compiler to
pass the first two arguments in the registers ECX and EDX.
Subsequent arguments are passed on the stack. The called function
will pop the arguments off the stack. If the number of arguments
is variable all arguments are pushed on the stack.
Also as far as I remember return values are passed in EAX
.
So in order to call a function in this way you need to provide the arguments in ECX
, EDX
and then invoke the call
instruction on the function address
int __fastcall Add(int a, int b) {
return a + b;
}
Please note I have changed the return type to int
, because I do not remember how doubles are passed back.
int a, b;
// set a,b to something
void* p = reinterpreted_cast<void*>(Add);
int return_val;
asm (
"call %3"
: "=a" (return_val) // return value is passed in eax
: "c" (a) // pass c in ecx
, "d" (b) // pass b in edx
, "r" (p) // pass p in a random free register
);
By calling convention it is up to the callee to clean up any used stack space. In this case we didn't use any, but if we did then your compiler will translate your function Add
in such a way that it cleans up the stack automatically.
The code above is actually a hack in such a way that I use the GCC extended asm
syntax to automatically put our variables into the appropriate registers. It will generate sufficient code around this asm
call to make sure data is consistent.
If you wish to use a stack based calling convention then cdecl
is the standard one
int __cdecl Add(int a, int b) {
return a + b;
}
Then we need to push the arguments to the stack prior to calling
asm (
"push %1\n" // push a to the stack
"push %2\n" // push b to the stack
"call %3" // the callee will pop them from the stack and clean up
: "=a" (return_val) // return value is passed in eax
: "r" (a) // pass c in any register
, "r" (b) // pass b in any register
, "r" (p) // pass p in any register
);
One thing that I have not mentioned is that this asm
call does not save any of our in-use registers, so I do not recommend putting this in a function that does anything else. In 32 bit x86 there is an instruction pushad
that will push all general purpose registers to the stack and an equivalent (popad
) to restore them. An equivalent for x86_64 is unavailable though. Normally when you compile C code the compiler will know which registers are in use and will save them in order for the callee not to overwrite them. Here it does not. If your callee uses registers that are in use by the caller - they will be overwritten!