__cdecl
C언어 표준 호출 규약이다. 파라미터는 오른쪽에서 왼쪽으로 스택을 통해 전달되며, 호출한 곳에서 스택을 정리한다. 특징적인건 호출한 쪽에서 스택을 정리하기 때문에 가변 인자를 지원한다는 것이다.
- extern "C" int __cdecl CdeclFunc(int a, int b, int c)
- {
- printf("%d %d %d\n", a, b, c);
- }
push 3
push 2
push 1
call CdeclFunc
add esp, 12
__stdcall
윈도우 API의 표준 호출 규약이다. 파라미터는 오른쪽에서 왼쪽으로 스택을 통해서 전달되며, 스택 정리는 호출 당한 곳에서 이루어진다.
- extern "C" int __stdcall StdcallFunc(int a, int b, int c)
- {
- printf("%d %d %d\n", a, b, c);
- }
호출 코드는 아래와 같다. __cdecl과 유사하지만 스택 정리 코드가 없는게 특징이다.
push 3
push 2
push 1
call _StdcallFunc@12
__fastcall
말 그대로 빠른 호출이다. 파라미터 중 일부를 레지스터를 통해서 전달하는 함수다. x86 계열에서는 일반적으로 ecx, edx로 파라미터를 전달하고 나머지는 스택으로 전달한다. __cdecl과 같이 오른쪽에서 왼쪽으로 파라미터를 전달하며, 스택 정리는 호출을 당한 곳에서 수행한다. 따라서 가변 인자를 지원하지 못한다.
- extern "C" int __fastcall FastcallFunc(int a, int b, int c)
- {
- printf("%d %d %d\n", a, b, c);
- }
위 함수를 호출 하는 코드는 아래와 같다. 앞 쪽 두 개의 파라미터가 ecx와 edx를 통해서 전달된다. 파라미터가 두 개 이하인 함수에 대해서는 스택을 전혀 사용하지 않기 때문에 빠르게 동작한다.
push 3
mov edx, 2
mov ecx, 1
call @FastcallFunc@12
< 스택 호출에 따른 이유 >
__cdecl 을 제외한 다른 호출들은 호출 당한 곳에서 함수 종료 시점에서 파라미터들이 들어있는 스택을 정리하는데..
이유는 속도에 있다.
x86 CPU의 경우 ret 어셈블리 명령어를 두 가지 형태로 지원한다.
ret를 하면 단순히 스택에 저장된 복귀 주소로 리턴 한다. 하지만 ret imm16을 사용하면 imm16만큼 스택에서 팝 한 다음
꺼내진 복귀 주소로 리턴 한다. 위에서 살펴 보았듯이 __cdecl은 스택 정리를 위해서 add 명령어가 추가된다. 반면에 호출을
당한 곳에서 정리하는 방식은 리턴할 때에 ret imm16을 사용함으로써 add 명령어가 추가되지 않아도 된다. 또한 486을
기준으로 했을 때 ret와 ret imm16은 5클럭 사이클을 소모하는 동일한 명령어로 처리된다.
따라서 __cdecl보다는 다른 호출 규약이 근소하게 빠를 수 있다.
< 사용된 소스 >
- int _tmain(int argc, _TCHAR* argv[])
- {
- CdeclFunc(1, 2, 3);
- FastcallFunc(1, 2, 3);
- StdcallFunc(1, 2, 3);
- CCallConv conv;
- conv.ThisCall(1,2,3);
- return 0;
- }
< 출처 : http://www.jiniya.net/lecture/techbox/callconv.html >