The gen on function calling conventions.

If you want to see the list of sections in this document, to more easily navigate to a specific section, enable the navigation bar support in your WWW browser.

In machine code, a function is called with a JSR or CALL instruction of some kind, and the function returns to its caller with a RTS or RET instruction of some kind. If one is writing assembly language, these menmonics are what one writes. But when one writes a function call with a (compiled) high-level language such as C, C++, Pascal, Modula, Fortran, and so forth, this is insufficient. In high level languages, functions have parameters and return values. Even in assembly language, programmers need to know, for example, what processor registers a function needs, and is liable to have modified upon return to the caller. (Macro assemblers, such as Borland's Turbo Assembler, provide high-level-language-like directives such as ARG for dealing in function parameters much like a high-level language.)

A function calling convention is a convention that specifies how the parameters are passed to the function when it is called, how the return value is returned to the caller, and what the processor register state is expected to be upon entry to and exit from the function. It is an interface contract between function callers and the called function.

The interface contract encompasses several aspects:

Although they are, strictly speaking, not parts of such interface contracts, other things are often specified via the same mechanisms (i.e. extension keywords, linkage specifiers, and so forth) that are used in high-level languages to specify calling conventions:

As stated, calling conventions encompass what CPU registers the caller can expect to change and to remain the same across a function call. This is the notion of register volatility:

Calling conventions can be grouped into three main categories:

Platform-defined calling conventions

Platform-defined calling conventions usually comprise just system API calling conventions. Platforms generally don't dictate what the calling conventions are for things that are not the system API on those platforms.

System API calling conventions are, as stated, the calling conventions required by the API for the operating system itself. In any compiled high-level language that supports program code calling operating system API functions directly, the compiler either has to use the system API calling convention as its default calling convention, or provide some mechanism for functions to be declared as having the system API calling convention.

Using something other than the system API calling convention is one of mistakes to avoid when designing DLLs. It's also a mistake to avoid when designing statically-linked libraries that are designed to be callable by code written in arbitrary languages and compiled with arbitrary compilers.

OS/2 system API calling conventions

The OS/2 system API calling convention is, technically, "APIENTRY". That is the macro, defined by the system API C and C++ language header <os2.h>, that expands to whatever the compiler's particular keywords for specifying the requisite calling convention actually are.

"APIENTRY" is, however, two distinct calling conventions, one for 16-bit OS/2 and one for 32-bit OS/2, whose calling conventions differ from each other. The APIENTRY macro expands to two different sets of things, depending from whether one is using the 16-bit OS/2 Developers' Toolkit for OS/2 version 1.x or the 32-bit OS/2 Developers' Toolkit for OS/2 version 2.x.

This is further compounded by the fact that the 32-bit OS/2 version 2.x code can still call the 16-bit OS/2 system API if it wants to. The 32-bit OS/2 Developers' Toolkit's <os2.h> header provides the APIENTRY16 macro, for specifying the 16-bit OS/2 system API calling convention for function declarations in 32-bit code.

32-bit OS/2 system API calling convention

The 32-bit APIENTRY calling convention is as follows:

16-bit OS/2 system API calling convention

The 16-bit APIENTRY calling convention is as follows:

Windows system API calling conventions

The Windows system API calling convention is, technically, "WINAPI". Similarly, the Windows application callback calling convention is, technically, either "CALLBACK" or "CALLBACK EXPORT". Those are the macros, defined by the system API C and C++ language header <windows.h>, that expand to whatever the compiler's particular keywords for specifying the requisite calling convention actually are. (CALLBACK was first introduced with the DOS-Windows version 3.1 Developers' Toolkit. Before then, the macros were "FAR PASCAL", and you might see a fair amount of code here and there that failed to catch up with the DOS-Windows 3.1 toolkit in this regard. It has been twenty years since then, however. It's been CALLBACK for Win16 for that long, and it has always been CALLBACK for Win32.)

"WINAPI" is, however, two distinct calling conventions, one for the Win16 API and one for the Win32 API, whose calling conventions differ from each other. Similarly, "CALLBACK (EXPORT)" is two distinct calling conventions, one for Win16 applications and one for Win32 applications. The WINAPI and CALLBACK macros expand to two different sets of things, depending from whether one is using the 16-bit Windows Developers' Toolkit for Win16 or the 32-bit Windows Developers' Toolkit for Win32.

This is further compounded by the fact that "CALLBACK" is more properly "CALLBACK EXPORT" or "CALLBACK loadds" on Win16. It is in fact three distinct calling conventions in its own right, dependent from whether the function is in a DLL or an EXE, and whether MakeProcInstance() needs to be called or the function is a "smart callback".

A final complication is that while Win16 only exists on x86 architectures, Win32 exists on x86, x86-64, IA64, Alpha, PowerPC, and MIPS architectures, each with their individual calling conventions. The latter three are mainly of academic interest nowadays, given that Windows NT has been discontinued for those processor architectures. This leaves just x86, x86-64, and IA64 whose Win32 calling conventions are of practical interest.

32-bit x86 Windows system API and application callback calling conventions

The 32-bit WINAPI and 32-bit CALLBACK calling conventions for x86 processors are identical, and are as follows:

64-bit x86-64 Windows system API and application callback calling conventions

The 64-bit WINAPI and 64-bit CALLBACK calling conventions for x86-64 processors are identical. They are documented by Microsoft in the Visual C/C++ Programming Guide, and are as follows:

16-bit Windows system API calling convention

The 16-bit WINAPI calling convention is as follows:

16-bit Windows application callback calling convention

The 16-bit CALLBACK EXPORT calling convention is as follows:

As Raymond Chen reports, Michael Geary discovered a trick in 1989 that did away with the necessity for MakeProcInstance() entirely. This trick was later incorporated into Borland's, Watcom's, Microsoft's, and the Digital Mars compilers. The upshot of the trick was this:

The Windows image loader will not overwrite any of these latter 3-byte prefixes, since they are not forms that it recognizes.

Unfortunately, however, there is no longer, with this modified "smarter" perilogue, a uniform approach that can be used in both DLLs and EXEs. Code for a CALLBACK EXPORT function compiled for a DLL cannot be linked into and properly used in an EXE, and vice versa. EXEs will have different DGROUPs for different instances, and in DLLs SS is not equal to the DLL's data segment. Hence, any statically-linked library providing CALLBACK EXPORT functions has to be provided in two forms, one for linking into EXEs and one for linking into DLLs.

On the gripping hand, generally only window procedures will be CALLBACK EXPORT, and it is more usually the case for statically-linked libraries to provide functions that use the WINAPI or some other calling convention, to which all of these considerations for CALLBACK EXPORT do not apply.

Unix system API calling conventions

Given the plethora of processor architectures that Unices and Linux are available for, there isn't a single system API calling convention for Unices and Linux, as there is for OS/2, Win32, and Win16. Rather, Unices and Linux adhere to what is known as an ABI, an Application Binary Interface. Most ABIs are descended from the AT&T System V Unix ABI definitions. They are defined, for Unix, for a given processor architecture, and any Unix or Linux for that processor architecture will usually adhere to the architecture's Unix ABI in its system call library.

This hasn't stopped some Unix vendors, such as Apple, defining their own individual ABIs for various processor architectures, that are specific to their operating systems.

To an extent there's a "Unix-tinted glasses" effect that Unix programmers suffer from when talking about Unix ABIs. They can tend to paint them as architecture-specific rather than as platform-specific. However, as can be seen from the differences between them and the OS/2, Win16, and Win32 calling conventions for the same architectures, they really are platform-specific, pertaining to Unix in particular and not to an entire processor architecture in general.

This is often given away by the formal full names of the ABI specifications, which explicitly state their platform-specific natures. For example: The Apple ABI is, formally, the MacOS X ABI (i.e. specific to the MacOS 10 platform), which is in turn based upon the System V Application Binary Interface (i.e. the ABI specifically for AT&T Unix System V).

MacOS version 10 x86 ABI calling convention

The x86 ABI for MacOS version 10 (documented by Apple in the MacOS 10 Reference Library) includes the following calling convention, as used by the system API library:

64-bit x86-64 System V Unix ABI calling convention

The 64-bit System V Unix ABI calling convention for x86-64 processors (documented by AMD) is as follows:

IA64 System V Unix ABI calling convention

The System V Unix ABI calling convention for IA64 processors is documented by Intel.

Architecture-defined calling conventions

Architecture-defined calling conventions are, as stated, calling conventions defined by the instruction set architecture itself, independently of the platform.

x86 interrupt calling conventions

The "interrupt" calling convention for the x86 family of processors is the calling convention required by the instruction set architecture for functions that are to be used directly as processor interrupt handling functions.

The interrupt calling convention is as follows:

Although the caller is unable to pass arguments to an interrupt function, in high-level languages interrupt functions can (if the compiler supports doing so) operate as if a caller had passed, as arguments, the processor registers as they were on entry to the function, allowing direct access to those registers values from within the function code as if they were ordinary function parameters. On Watcom C/C++, for example, an interrupt function can be declared with a union INTPACK argument. The compiler automatically generates code in the called function to save all of the register values upon entry to the function, in such a way that they can be accessed as if they were normal function parameters.

This calling convention, as provided by C and C++ compilers, also introduces an otherwise entirely foreign concept into the C and C++ languages: pass-by-value-return parameters. C and C++ function parameters are either pass-by-value or pass-by-reference. But the register block parameters to an interrupt function are pass-by-value-return. The values in the parameters are copied back into the processor registers upon function exit. Thus, and oddly for the C and C++ languages, modifications to what appear, syntactically, to be parameters passed by value actually propagate out to the original values outside of the function being called.

Compiler-defined calling conventions

Compiler-defined calling conventions are, as stated, calling conventions defined by a particular compiler. Compilers often define their own calling conventions, in addition to the platform-defined ones. Mainly this is in cases where the compiler-defined calling conventions are in some respects superior to the platform-defined ones, since they can take advantage of the fact that they are usually language-specific, whereas the platform-defined calling conventions have to be language-neutral.

There are two main groupings of compiler-defined calling conventions:

In most cases, a compiler-defined calling convention is in fact the default calling convention used by the compiler, and platform-defined or architecture-defined calling conventions have to be explicitly specified either by compiler command-line options or via explicit calling convention specifiers in the program source code.

Except for the "Microsoft C-like" calling convention, most compiler-defined calling conventions are also compiler specific, in that there's no way to specify exactly the same calling convention with another compiler. Sometimes the keywords look the same, but the conventions differ. Sometimes there simply isn't a keyword at all.

Even Watcom C/C++, whose #pragma aux facility allows programmers to define new keywords for user-specified calling conventions, is not flexible enough to be capable of all compiler-defined calling conventions. (A programmer cannot define IBM's Optlink convention with Watcom C/C++'s #pragma aux, for example, since it has no mechanism for specifying either placeholders on the call stack or arguments passed in the CPU registers. Watcom C/C++ contains an internal bodge for working around this that is not expressible in source code form.)

"common" cdecl calling conventions

The cdecl calling convention is a last vestige of the days of PC/MS/DR-DOS. Back before the existence of Windows NT, OS/2, or even DOS-Windows, the only (major) operating system API in the world of the IBM PC/AT compatible was the PC/MS/DR-DOS system API. The DOS system API, unlike the system APIs of its aforementioned successors, was not directly callable from high-level languages. Therefore the language bindings to the PC/MS/DR-DOS API comprised wrapper functions, private to the runtime library of each language.

The DOS system API's calling conventions were thus language-defined rather than platform-defined. The DOS API bindings for Microsoft C had one calling convention. The DOS API bindings for Microsoft PASCAL had another. Microsoft FORTRAN and COBOL had specific calling conventions for their DOS API wrappers, too. Thus came about calling conventions known as cdecl, pascal, fortran, and so forth. Each was whatever calling convention the Microsoft compiler for that language used for its DOS API wrapper functions.

The notion of language-defined calling conventions disappeared over twenty years ago. Operating systems with system APIs that were directly callable from high-level languages appeared, and by the time of the DOS-Windows 3.1 Developers' Toolkit, which finally replaced FAR PASCAL with CALLBACK, the notion of the platform-defined calling convention had almost entirely replaced the notion of the language-defined calling convention.

The one remaining vestige of language-defined calling conventions is the common cdecl convention still supported by most C/C++ compilers for x86, both 16-bit and 32-bit. The calling convention remains "whatever Microsoft C used to do" for its 16-bit DOS API wrappers.

Although commonly thought of as the default calling convention for C and C++ compilers, the cdecl calling convention is actually not the default in all but a very few C/C++ compilers. Everyone else defaults to their own compiler-defined calling conventions, defaults to to whatever the target platform's platform-defined system API calling convention is, or (in one oddball case) defaults to the Win32 system API calling convention irrespective of target platform. The reality is that the cdecl calling convention is, everywhere outside of Microsoft's own compilers and 16-bit Borland C/C++, an alternative non-default compatibility option, for doing "whatever Microsoft C used to do" if that is desired.

16-bit cdecl calling convention

The 16-bit cdecl calling convention is, as stated, "whatever Microsoft C used to do" for its 16-bit DOS API wrappers, and is as follows:

32-bit cdecl calling convention

The 32-bit cdecl calling convention is provided by 32-bit compilers that have 16-bit predecesors, and is the logical extension of the 16-bit cdecl calling convention to 32-bits, albeit that there was, of course, no 32-bit Microsoft C for DOS and thus nothing to mimic. It differs from the 16-bit calling convention in several major respects:

The full calling convention, including the variations across compilers and target platforms, is as follows:

The Optlink calling convention is the default calling convention used by several of IBM's x86 compilers — including VisualAge for C/C++ for OS/2, VisualAge for C/C++ for Windows, and COBOL for Windows. It is a "go faster" variant of the 32-bit OS/2 system API (APIENTRY) calling convention, that places up to three integer and (near) pointer and up to four floating point arguments in CPU registers (but with the same space left for them on the stack as would be in APIENTRY).

The full convention (documented by IBM for its COBOL for Windows compiler) is as follows:

Watcom's Watcall calling conventions

The Watcall calling convention is the default calling convention used by Watcom's x86 C, C++, and Fortran compilers. It is, in fact, two different calling conventions. The default calling convention used by the compiler, and the meaning of the __watcall keyword, vary according to whether the -3r/-4r/-5r or the -3s/-4s/-5s compiler options are used. The r variants specify a register-based Watcall calling convention, and the s variants specify a stack-based Watcall calling convention. (The default, if no option is specified, is register-based.)

The inability to specify the "other" Watcall calling convention with a keyword means that the C/C++ library headers declaring C/C++ library functions declare them with one of two calling conventions according to what command-line option was specified. Watcom C, C++, and Fortran thus ship with two sets of runtime libraries, one compiled with the register-based Watcall calling convention and one compiled with the stack-based Watcall calling convention.

The register-based Watcall calling convention is essentially a "go faster" version of the stack-based Watcall calling convention, that places up to four integer and (near) pointer arguments and (POD) structure/class type return values in CPU registers instead of on the stack or in memory.

Both the register-based and the stack-based conventions come in 16-bit and 32-bit flavours, analogous to each other but differing in how floating-point values are returned in non-FPU registers, whether (E)DX is used for return values, and whether (some) floating point arguments can be passed in registers. Both also come in near and far forms, that differ solely in the distance attribute of the function.

The following descriptions describe the actual calling convention, as employed by code compiled with the Watcom C/C++/Fortran compilers and by the code in their C/C++/Fortran run-time libraries. Note that there are significant differences between these descriptions and what the OpenWatcom documentation states. The OpenWatcom documentation does not correctly describe the actual output generated by the compilers. The following descriptions are based upon actual observations of generated code and inspection of the source code for the OpenWatcom 1.x compilers.

Given that the descriptions are of what the run-time library code calling conventions are, the following descriptions do not take the -zdp, -zdf, -zfp, -zff, -zgp, and -zgf compiler options into account. Although these options directly modify the effect of the __watcall keyword, and the compiler's default calling convention, they do not change what libraries are linked to, or the code in those libraries. In fact, they will cause linkage to the C, C++, and Fortran run-time libraries to break, because they will alter how the header files effectively declare the run-time library functions (which are declared using the __watcall keyword), and thus how application code will generate calls to run-time library functions.

Conversely, the -r compiler option is taken into account in the following, because it relates to a modification to the Watcall calling convention that is existent in run-time libraries. This option causes the Watcom C/C++/Fortran version 9, 10, and 11 compilers, and the OpenWatcom 1.x compilers, to revert to the same Watcall calling convention as used by the Watcom C/C++/Fortran version 8 compiler, whose run-time libraries are compiled to employ that particular calling convention.

Watcom's 16-bit stack-based Watcall calling convention

The 16-bit stack-based Watcall calling convention is as follows:

Watcom's 16-bit register-based Watcall calling convention

The 16-bit register-based Watcall calling convention is as follows:

Watcom's 32-bit stack-based Watcall calling convention

The 32-bit stack-based Watcall calling convention is as follows:

Watcom's 32-bit register-based Watcall calling convention

The 32-bit register-based Watcall calling convention is as follows:

Microsoft's Fastcall calling convention

Microsoft's x86 fastcall calling convention is a "go faster" variant of the 32-bit x86 Win32 system API calling convention, that places up to two integer and (near) pointer arguments in CPU registers instead of on the stack or in memory. (The 32-bit x86 WINAPI calling convention already returns small POD structures in registers.)

It is documented by Microsoft in the Visual C/C++ Programming Guide, and is as follows:

Borland's Register calling conventions

Borland's x86 Register calling conventions are "go faster" variants of its own cdecl calling conventions, that place up to three integer and (near) pointer arguments in CPU registers instead of on the stack or in memory.

What combinations of registers are used and what lexical order the arguments are assigned to registers are undocumented and not guaranteed, not even from version to version of Borland's own compilers. Therefore:

Borland's 16-bit Register calling convention

Borland's 16-bit Register calling convention is documented (to an extent) by Borland in its Borland C/C++ version 3.1 for DOS/Windows User Guide, in Appendix A. (The following fills in some of the missing parts from Borland's 16-bit cdecl calling convention.) That documentation was dropped from its Borland C/C++ version 4.0 for DOS/Windows User Guide.

It is as follows:

Borland's 32-bit Register calling convention

Borland's 32-bit Register calling convention is hardly documented at all in its Borland C/C++ version 4.0 for DOS/Windows User Guide. It is, however, more fully albeit still incompletely documented in its Borland C/C++ version 2.0 for OS/2 User Guide, in appendix A. (The following fills in some of the missing parts from Borland's 32-bit cdecl calling convention.)

It is as follows:

Borland's FastThis calling conventions

Borland's x86 FastThis calling convention (called the "Object Data" calling convention in version 3.1 of its DOS/Windows compiler) is a variant of its own cdecl calling convention, that places the implicit this pointer, passed as a hidden parameter in calls to non-static function members of classes, in a register instead of on the stack.

Obviously enough, it only applies to the C++ compiler. It is as follows:


© Copyright 2010 Jonathan de Boyne Pollard. "Moral" rights asserted.
Permission is hereby granted to copy and to distribute this web page in its original, unmodified form as long as its last modification datestamp is preserved.