Before posting this question, I have already read some documentation, SO answers and watched some videos to understand __stdcall. So far I have understood that it's a calling convention and specifies to push parameters from right to left on the stack. This also signifies when to clear the stack.
But I am still not able to understand the advantage and cases where I should use __stdcall.
I have come across the following code, which is called when an OPC-UA client is closed. I see that this should follow the __stdcall calling convention but why? What may have happened if __stdcall was not specified for the following method?
OpcUa_StatusCode __stdcall opcua_client::onShutdownMessage(OpcUa_Handle hApplication, OpcUa_Handle hSession, OpcUa_String strShutdownMessage, void* extraParam)
{
opcua_client* pOpcClient = NULL;
if (extraParam)
{
try
{
pOpcClient = qobject_cast <opcua_client*>((QObject*)extraParam);
throw(55);
}
catch (int exeption)
{
}
}
OpcUa_StatusCode uStatus = OpcUa_Good;
QString strShutDownMsg = QString::fromUtf8(OpcUa_String_GetRawString(&strShutdownMessage));
bool bOK;
OpcUa_StatusCode uClientLibStatus = uClientLibStatus = strShutDownMsg.toUInt(&bOK, 16);
if (pOpcClient)
{
if (uClientLibStatus > 0)
pOpcClient->process_onShutdownMessage(uClientLibStatus);
}
else
{
return uStatus;
}
return uStatus;
}
Function calling convention must be followed by the caller. If it is there, then there must be some caller of this function that will assume it has to be called in a specific way. This is usually important when you have a callback function.
Other than that, stdcall is meant to reduce code size compared to cdecl by moving argument cleanup code from the caller to the callee (which can use ret imm16 on x86).
This is an old micro optimization for IA-32 architecture. MSVC accepts __stdcall keyword on other architectures too, but it doesn't do anything there.
Check this example:
__declspec(noinline) int sum(int a, int b)
{
return a + b;
}
int add1(int a)
{
return sum(a, 1);
}
MSVC compiles this to:
int sum(int,int) PROC
mov eax, DWORD PTR _a$[esp-4]
add eax, DWORD PTR _b$[esp-4]
ret 0 ; this instruction will be replaced
int sum(int,int) ENDP
int add1(int) PROC
push 1
push DWORD PTR _a$[esp]
call int sum(int,int)
add esp, 8 ; this instruction will be removed
ret 0
int add1(int) ENDP
And version with stdcall:
__declspec(noinline) int __stdcall sum(int a, int b)
{
return a + b;
}
int add1(int a)
{
return sum(a, 1);
}
which compiles to:
int sum(int,int) PROC
mov eax, DWORD PTR _a$[esp-4]
add eax, DWORD PTR _b$[esp-4]
ret 8 ; 0 argument replaced with 8
int sum(int,int) ENDP
int add1(int) PROC
push 1
push DWORD PTR _a$[esp]
call int sum(int,int)
ret 0 ; extra instruction is missing here
int add1(int) ENDP
Notice how the 2nd example uses ret 8 and the caller doesn't have an additional add esp, 8.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With