The __fastcall Calling Convention

This section describes the details of the __fastcall calling convention. The information is for the use of assembly-language programmers who are interested in using either the inline assembler or the Microsoft Macro Assembler (MASM) to write functions declared as __fastcall. Functions declared as __fastcall accept arguments in registers rather than on the stack; functions declared as __cdecl or __pascal accept parameters only on the stack.

Note:

The register usage documented here may change in future releases of the compiler.

Argument-Passing Convention

The __fastcall calling convention is a “strongly typed” register calling convention. This typing allows the compiler to generate better code by passing arguments in registers that correspond to the data type you are passing. Because the compiler chooses registers depending on the type of the argument and not in a strict linear order, the calling program and called function must agree on the types of the arguments in order to communicate data correctly.

For each type of argument there is a list of register candidates. The arguments are allocated to registers or, if no suitable register remains unused, are pushed onto the stack left-to-right. Each argument is put in the first register candidate that does not already contain an argument. Table 1.3 shows the basic types and the register candidate list for each.

Table 1.3 Register Candidates

Type Register Candidates

character AL, DL, BL
unsigned character AL, DL, BL
integer AX, DX, BX
unsigned integer AX, DX, BX
long integer DX:AX
unsigned long integer DX:AX
near pointer BX, AX, DX
far or huge pointer passed on the stack

All far and huge pointers are pushed on the stack, as are all structures, unions, and floating-point types.

Return Value Convention

The __fastcall return value convention is based on the size of the return value, except with floating-point types. All floating point types are returned on the top of the NDP stack. For more information about the NDP stack and returning floating-point values, see Chapter 7, “Controlling Floating-Point Math Operations.” The following list shows how values 4 bytes or smaller, including unions and structures, are returned from a __fastcall function.

Size Return Convention

1 Byte AL Register
2 Bytes AX Register
4 Bytes DX, AX Registers (for pointers, the segment is returned in DX, the offset in AX; for long integers, the most-significant byte is returned in DX, least-significant byte in AX)

Note that the protocol for returning values 4 bytes or smaller is the same as for functions declared as __cdecl. To return structures and unions larger than 4 bytes, the calling program passes a hidden parameter as the last item pushed. This parameter is a near pointer, implicitly SS-relative, to a buffer in which the value is to be returned. A far pointer to SS:hidden-param must be returned in DX:AX. This is the same convention for returning structures as __pascal.

Stack Adjustment Convention

Unlike functions declared as __cdecl, functions declared as __fastcall must pop the arguments off the stack. The calling program does not adjust the stack after function return.

Register Preservation Requirement

All functions must preserve the DS, BP, SI, and DI registers. Your __fastcall function can modify the values in AX, BX, CX, DX, and ES.

Function-Naming Convention

The public name put into the object file for a function declared as __fastcall is the name given by the user with a leading “at sign” (@). No case translation is performed on the function name. The function declaration

int __fastcall FCFunc( void );

causes the compiler to place the public symbol @FCFunc in your object file at every location FCFunc is referenced in your program.

If you do not declare the function as __fastcall in your C or C++ program, the compiler assumes the default calling convention. The default for C is usually the C calling convention but can be changed by the /Gc (FORTRAN/Pascal Calling Convention), /Gr (Register Calling Convention), or /Gd (C Calling Convention) options. The default for C++ is the FORTRAN/Pascal calling convention. If the linker gives you an unresolved external reference, you may have failed to declare an external __fastcall function properly. For more information about calling conventions, see Chapter 11, “Programming with Mixed Languages.”