the stack and the call instruction are used for function calls and appear differently in assembly code. the three most common calling conventions encountered are cdecl, std-call and fastcall.
BASIC FUNCTION CALL
CDECL
with this convention, the parameters are pushed onto the stack from right-to-left, the caller cleans up the stack when the function is complete, and the return value is stored in EAX
#C CODE SNIPPPET - PSEUDO CODE
int test(int x, int y, int z);
int a, b, c, ret;
ret = test(a, b, c);
#ASSEMBLY CODE SNIPPET: CDECL
push c
push b
push a
call test
add esp, 12 //this represents that the stack is cleaned up by the caller
mov ret, eax
* in this example the cdecl pushed the parameters onto the stack from right-to-left, beginning with c
STDCALL
this convention is similar to cdecl, except stdcall requires the callee to clean up the stack when the function is complete
#C CODE SNIPPPET - PSEUDO CODE
int test(int x, int y, int z);
int a, b, c, ret;
ret = test(a, b, c);
#ASSEMBLY CODE SNIPPET: CDECL
push c
push b
push a
call test
//the add esp, 12 is not required with stdcall since the function called would be the one responsible for cleaning up the stack
//the epilogue would be responsible for cleaning up the stack
mov ret, eax
* in this example the cdecl pushed the parameters onto the stack from right-to-left, beginning with c
FASTCALL
in this convention, the first few arguments (typically two) are passed in registers, with the most commonly used registers being EDX and ECX (the MS fastcall convention). additional arguments are loaded from right-to-left, and the calling function is usually responsible for cleaning up the stack, if necessary. it is often more efficient to use fastcall than other conventions, because the code doesn't need to involve the stack as much.
PUSH VS MOVE
the compiler determines whether to use the PUSH instruction or an alternative approach, such as MOV with stack pointer adjustment (SUB), based on optimization strategies and architectural considerations when storing values on the stack. the MOV + SUB is sometimes preferred for manual stack management (e.g., aligning the stack for function calls or reducing dependencies in instruction execution pipelines)
#C CODE SNIPPET
int adder(int a, int b)
{
return a+b;
}
void main()
{
int x = 1;
int y = 2;
printf("the function returned the number %d\n", adder(x,y));
}
#ASSEMBLY CODE SNIPPET
00401730 push ebp
00401731 mov ebp, esp
00401733 mov eax, [ebp+arg_0]
00401736 add eax, [ebp+arg_4]
00401739 pop ebp
0040173A retn
* eax stores the return value
FUNCTION CALL WITH TWO DIFFERENT CALLING CONVENTIONS