Reprinted from: http://www.allaboutprogram.com
The article describes the function call conventions in several main programming languages;
Function call conventions Housisong@263.net 2004.11.07
The article is from one of the ABP forum: http://bbs.Allaboutprogram.com/viewtopic.php? T = 1245 (The original article writes in 2003.12.01, here added a description of the function return value transmission mode)
Preface: The article tells the function call conventions in several major programming languages;
Compilers and platforms I use: WindowsXP Celeron 1G VC6 (main tools) / Delphi6 / C Builder6;
1: Function call convention; function call agreement is a convention for the function caller and the number of transactions, return value transfer, stack clearance, and register, and the components need to be compatible, functional If the caller and function body uses a different call agreement, it may cause a program to perform an error, and it must be regarded as part of the function declaration;
Two: Common function calls;
Function call convention in VC6;
Call the agreed stack clearance parameter transfer __cdecl caller from right to left, pass the __stdcall function body from right to left, by stack pass __fastcall function body from right to left, prioritize the register (ECX, EDX), then use the stack THISCALL function Body THIS pointer default via ECX, other parameters from right to left in the stack
__cdecl is the default call agreement of C / C ; there is no THISCALL in the invocation convention, which is the default call agreement of the class member function; the call agreement of the main (or wmain) function in C / C must be __cdecl, Do not allow changes; the default call agreement can generally be changed through the compiler settings. If your code rely on the call agreement, please clearly indicate the invocation agreement required to use;
Delphi6 function call conventions;
Call the agreed stack clearance parameter Transferring the Register function from left to right, prioritize the register (EAX, EDX, ECX), then use the stack PASCAL function body from left to right, pass the CDECL caller from right to left, pass the stack pass through the stack (With the C / C default invokes) stdcall function, from right to left, pass the stack delivery (compatible with the __stdcall in VC) SaFecall function, from right to right, passing through the stack transfer (with stdcall)
The default call agreement in Delphi is Register, which is also a way that I think is the most efficient, and CDECL is a call mode I think is the worst efficiency; __fastcall call agreement in VC is generally less efficient than Register. some;
Function call convention in C Builder6;
Call the agreed stack clearance parameter pass __fastcall function body from left to right, prioritize register (EAX, EDX, ECX), then use the stack (compatible with Delphi's register) (Register and __fastcall equivalent) __pascal function body from left to right, pass Stack transfer __cdecl caller from right to left, through stack delivery (compatible with C / C default calls) __stdcall function bodies from right to left, pass the stack pass (compatible with __stdcall in VC) __msfastcall function body from right to left , Prioritize the register (ECX, EDX), then use the Function Call convention common to the stack (compatible VC _fastCall), only the CDECL conventions require caller to clear the stack; the function in C / C supports the number of parameters of parameters For example, the printf function; because the function is not known that the caller is pressed into the stack in the stack, the function should not be convenient to know how to clear the stack, then the best way is to hand over the responsibility of the purge stack to the caller; this It should be the reason why CDECL calls agree;
VB generally uses STDCALL call conventions; (PS: Is there a stronger guarantee) Windows API, generally uses STDCALL conventions; (PS: Is there a stronger guarantee) suggest in call between different languages ( For example, DLL is best adopted by STDCALL calls because it supports the best language compatibility;
Three: Function Return Value Transfer Method In fact, the return value will also imagine an OUT-shaped parameter for function calls; function return value transmission mode is also part of the function call; if there is a function of return value, time INT 32bit data values such as pointers (including 32bit structure) pass through EAX, (Bool, Char passed through Al, Short passed through AX delivery), special __int64, etc. 64bit structure (Struct) via EDX, EAX two registers (same理: 32bit shaping In the 16bit environment via DX, AX delivery); other size structures returns to return their address by EAX; (so the return value type is not 1, 2, 4, 8byte, the efficiency may be relatively poor) In the passage of parameters and return value, the type of reference method can be regarded as the pass pointer mode; Float / Double (including extended in Delphi) is returned by floating point register ST (0);
Four: The function call convention will be explained through the C example in the VC and the resulting assembly list;
(PS: Although there are many compilation, I have done a very detailed note, I hope those who have a "fear" to compile "fear" can also read smoothly; and provide some people who want to use compilation in VC help
A: Test code:
INT X; int __cdecl add (int A, int b) {RETURN A B;} // Use __cdecl call convention int Main (int Argc, char * argv []) {x = add (1, 2); Return }
; Debug mode compiles after compiling
PUBLIC? X @@ 3ha; x _bss segment? X @@ 3ha DD 01H DUP (?); X variable _BSS Ends public? Add @@ yahhh @ z; add public _main __chkesp: near; comdat _main _text segment_main proc Near COMDAT // main function body
PUSH EBP; / / Save EBP value to the stack, with the POP EBP to restore the MOV EBP, ESP; // EBP to the current stack before exiting the function; the function can be used for stack access SUB ESP, 64; // in the stack Open up 64byTE local space
; // Description: These three assembly instructions are usual methods that many of the function began; // Point with EBP to the stack (not changed); and access parameters and local variables via EBP;
PUSH EBX; // Generally, in accordance with the convention where the function is called, Eax, ECX, EDX; Push ESI; // Other registers need to be saved if needed, and recovered at the time; that is, the usage of the register; This also makes the function calls a conventional part; Push EDI; //, after the other function is called, EAX, ECX, EDX is likely to change,; // and other registers (EBX, ESI, EDI, EBP The value can be rest assured to continue use (except ESP)
Lea EDI, DWORD PTR [EBP-64] MOV ECX, 16; 00000010H MOV EAX, -858993460; CCCCCCCH rep Stosd; // The front opened (16 * 4) BYTE partial space full fill 0xcc; // Note: 0xcc is debugging Interrupt (__ASM INT 3) instruction code, so you can imagine, when the program error is jumped to this area, the debug interrupt will be generated.
Push 2; // code: x = add (1, 2); push 1; // From right to left in the stack (__cdecl call convention !!!) call? add @@ yahhh @ z; call the add function; CALL instruction The address (return address) of the next instruction will be pressed into the stack add ESP, 8; after the ADD function calls, the caller is responsible for cleaning the stack (__cdecl call convention !!!); two int type parameters use 8Byte space Stack MOV DWORD PTR? X @@ 3ha, EAX; deposit the return value of the Add function into the X variable, you can see that the return value of the add function is placed in Eax.
XOR EAX, EAX; // Original code: Return 0; Execute Eax Clear, the return value of the main function is in EAX
POP EDI POP ESI POP EBX; // Restore EDI, ESI, EBX Register Add ESP, 64; // Restore 64byTE Local Space CMP EBP, ESP CALL __CHKESP; // At this time, EBP == ESP, DEBUG version is confirmed, If you do not wait, throw an exception, etc. MOV ESP, EBP POP EBP; // Restore EBP Register Ret 0 _Main Endp_Text Ends; // The following is the code of the Add function, you don't have to explain it in detail above.
COMDAT? Add @@ yahhh @ z_text segment _a $ = 8; // Parameter A relative to the stack offset 8 _b $ = 12; // Parameter B relative to the stack offset 12
? add @@ yahhh @ z proc Near; add, comdat // add function body
PUSH EBP MOV EBP, ESP SUB ESP, 64; 00000040H Push EBX PUSH ESI PUSH EDI LEA EDI, DWORD PTR [EBP-64] MOV ECX, 16; 00000010H MOV Eax, -858993460; ccccccccch rep Stosd
MOV EAX, DWORD PTR _A $ [EBP] moves the value of the parameter A to Eax Add Eax, DWORD PTR _B $ [EBP]; the value of the parameter B is accumulated to EAX; it can be seen that the return value returns via EAX
POP EDI POP ESI POP EBX MOV ESP, EBP POP EBP RET 0; Function Regardless of the stack of parameters cleaning (__cdecl calling conventions !!!); RET instruction will remove the return address of the Call instruction, and jump over the past continues
? add @@ yahhh @ z Endp; add _text ends end
Let's take a look at the assembly code obtained by Release mode; it can be seen that there is a lot of compilation instructions than the debug mode, and the speed may of course be faster; no more detailed explanation, please contact the above explanation
PUBLIC? X @@ 3ha; x _bss segment? X @@ 3HA DD 01H DUP (?); X _bss ends public? Add @@ yahhh @ z; add public_main; comdat _main _text segment
_Main proc near; comdat // main function body Push 2 push 1; // from right to left in the stack (__cdecl call convention !!!) call? add @@ yahhh @ z; // call the add function; MOV DWORD PTR? X @@ 3ha, EAX; x add esp, 8; // The caller is responsible for cleaning the stack (__cdecl call convention !!!)
XOR EAX, EAX RET 0 _MAIN ENDP _TEXT ENDS
COMDAT? Add @@ yahhh @ z_Text segment _a $ = 8 _b $ = 12
? add @@ yahhh @ z proc Near; add, comdat // add function body
MOV EAX, DWORD PTR _B $ [ESP-4] Move the value of parameter B to Eax Mov ECX, DWORD PTR _A $ [ESP-4]; move the value of parameter A to ECX Add Eax, ECX; Value is accumulated to EAX; return value passes RET 0 via EAX; the function body does not care about the parameter cleaning of the stack (__cdecl call convention !!!)? Add @@ yahhh @ z endp; add _text ends end
The following analysis will only give assembly code after Release mode compilation.
B: Declare the add function to __stdcall call agreement
INT X; INT __STDCALL ADD (INT A, INT B) {RETURN A B;} INT Main (int Argc, char * argv []) {x = add (1, 2); return 0;}
As a result of the assembly code produced:
; // main function body push 2 push 1; // from right to left in the stack call? add @@ yghhh @ z; add mov dword ptr? x @@ 3ha, Eax; xor Eax, Eax Ret 0
; // Add function body MOV EAX, DWORD PTR _B $ [ESP-4] MOV ECX, DWORD PTR _A $ [ESP-4] Add Eax, ECX RET 8; // Function Responsible for Qing Stack; Two INT type parameters A total of 8byte spaces
C: Declaration ADD function is __fastcall call agreement
INT X; INT __FASTCALL ADD (INT A, INT B) {RETURN A B;} int Main (int Argc, char * argv []) {x = add (1, 2); return 0;}
As a result of the assembly code produced:
// main function body MOV EDX, 2; b passes MOV ECX, 1; A passing Call? add @@ yihh @@ 3ha, Eax; X @@ 3ha, Eax; xor eax , EAX RET 0; // add function body Lea EAX, DWORD PTR [ECX EDX]; // A, and B parameter value is already in ECX, EDX, this sentence will be the two values and put it as return value RET 0; // Here you should be responsible for clearing the stack; but because the two parameters have passed through registers; //, there is no stack, so RET 0;
D: Let's take a look at the call of the member function:
Struct T {int start0; t (): start0 (1) {} int Add (int A, int b); // class member function; as long as it does not explicitly declares that the call will be used by default, thiscall call is used;}; int T: : add (int A, int b) {return (* this) .start0 a b;}
INT X; int Main (int Argc, char * argv []) {t t; x = T.Add (1, 2); Return 0;}
To see the assembly code produced:
; // main function body Push ECX; // Save ECX PUSH 2 Push 1; // Parameter from right to left in the stack LEA ECX, DWORD PTR _T $ [ESP 12]; // t Save to ECX MOV DWORD PTR_T $ [ESP 12], 1; // Perform T :: START0 = 1; call? Add @ T @@ Qaehhh @ z; // Call T :: add function, at this time, T is stored. Address (THIS pointer); MOV DWORD PTR? X @@ 3ha, Eax; XOR EAX, EAX POP ECX RET 0
; // t :: add function body MOV EAX, DWORD PTR [ECX]; // Move the value of start0 through the THIS pointer (saved in ECX) to Eax Mov ECX, DWORD PTR _A $ [ESP-4]; // Moving a value of A to ECX; the value of this will be lost, but add Eax, ECX is not required in the function body; // Put a value to EAX MOV ECX, DWORD PTR _B $ [ESP-4]; / / Move the value of B to ECX; add Eax, ECX; // Put the value of B to EAX RET 8; // The function body is responsible for clearing;
Five: Others
1. When implementing a function body in the VC, you can use the __declspec (name) declaration, it tells the compiler, do not automatically generate start and end codes for the function body; 2. In VC6, you want to get a compilation code list, the setting method is:
Quote: [Project] -> [setting ...] -> [c ] -> [category:] -> [listing file type:] -> [assembily, ...] 3.VC6 The way in the middle into the assembly code is:
__ASM {<编 语语>>} or __asm
4. The default call convention used by the reset function in VC6 is: Quote: In [Project] -> [Setting ...] -> [C ] -> [Project Options:] Add compilation settings such as: / GD representative __cdecl; / GR representative __fastcall; / gz representative __stdcall
Reference: MSDN: Calling Conventions; Delphi6 / C Builder6 Help; Use www.google.com and www.baidu.com to search "Call Convention" or "Calling Conventions";