All Manuals > Foreign Language Interface User Guide and Reference Manual > 4 Defining foreign functions and callables

4.2 Specifying a calling convention.

The FLI macros such as define-foreign-function and define-foreign-callable take a keyword :calling-convention. Apart from on 32-bit Windows and on the ARM architectures, there is only one calling convention and in most cases you do not need to specify it.

The common case when you need to specify the calling convention is on 32-bit Windows where the default LispWorks calling convention is __stdcall. This matches the Win32 API functions, but compilers typically produce __cdecl by default (which is the same as the non-Windows x86 systems).

ARM (both 32-bit and 64-bit) also has more than one calling convention, but it should be rare (in 32-bit) or extremely rare (in 64-bit) that you need to specify the convention. Note however that, on ARM, failing to specify that a function is variadic (by the keyword :variadic-num-of-fixed) is more likely to cause crashes than on the other architectures.

4.2.1 Windows 32-bit calling conventions

The Win32 API functions in 32-bit Windows applications are compiled using the __stdcall calling convention, but compilers normally use __cdecl by default. Thus if you call functions that are not part of the Win32 API from 32-bit LispWorks then you need to check the calling convention and in most cases you need to specify it as __cdecl by passing :calling-convention :cdecl. To specify __stdcall, pass :calling-convention :stdcall, which is the default so is not really needed.

Note that all the other LispWorks architectures, including 64-bit Windows, interpret both :cdecl and :stdcall to mean the default.

Since whole libraries are normally compiled with the same calling convention, it is usually convenient to define your own defining macro that expands to the FLI defining macro and passes it the calling convention. For example, LispWorks itself uses the following defining macro to define foreign calls to the MySQL library:

(defmacro def-mysql-function (&body x)
  `(dspec:def (def-mysql-function ,(car x))
     (define-foreign-function ,@x
       :module 'mysql-library
       :calling-convention :cdecl)))

4.2.2 ARM 32-bit calling conventions

32-bit ARM systems have two calling conventions: hard float and soft float. These calling conventions are binary incompatible, and operating systems generally support only one or the other. Currently, Android and iOS are both soft float but Android is now starting to support hard float code, while ARM Linux distributions are now almost always hard float, but used to be soft float. Moreover, iOS has a calling convention which is soft, and somewhat different from the Android/old-Linux soft float, so these are also binary incompatible.

Thus LispWorks supports 3 calling conventions:

Soft float conventions:

The calling convention that is used by iOS.
soft Linux
The calling convention that is used by Android, and was used by old Linux systems.

Hard float convention:

hard float
The calling convention used by newer Linux systems.

When LispWorks compiles a foreign call or callable function, it (by default) generates "tri-compatible" code that can interface with either hard float, soft Linux or iOS foreign code. At run time, the code checks an internal flag and uses the appropriate calling convention. The internal flag is set to the correct value on start-up. The tri-compatible code is needed only for functions where the calling conventions differ, and when 2 or more of the conventions need the same code LispWorks avoids duplicating code, while remaining compatible with all 3 conventions.

Because of the tri-compatible code, LispWorks binaries (fasl files) are compatible with all the conventions. The compiled Lisp code is also compatible with all conventions. However, LispWorks executables (including LispWorks as a shared library) have a small C program that starts Lisp (the "xstarter"), and this is either hard float, soft Linux or iOS. Therefore, a LispWorks executable can run only on one calling convention, but the code that LispWorks compiles can run on all of them.

In particular, that means that it is possible to compile and build runtimes for Android and iOS on either soft float or hard float systems, because the runtime is created using the appropriate xstarter for the target OS.

It is possible to tell LispWorks to compile a foreign call or callable function for only one calling convention, by supplying the keyword :calling-convention with one of these values:

hard float.
soft Linux.
Android. Currently that is an alias to :soft-linux.
Code that selects between :soft-linux and :ios .

All other values generate tri-compatible code.

You are only required to pass :calling-convention when you use a library with a calling convention that does not match the calling convention of the OS. That should be rare.

Passing :calling-convention also makes the code smaller and slightly faster, but the difference is unlikely to be significant.

Note that variadic functions (for example printf and sscanf) are always soft float, which means that when compiling calls to such functions it is essential to specify that they are variadic (by passing :variadic-num-of-fixed) to ensure that LispWorks does not try to pass the arguments as hard float.

Compatibility note: in LispWorks 7.0, you had to pass :calling-convention :soft-float for variadic functions. This still works, but passing :variadic-num-of-fixed is more correct and will make it work properly on other architectures, (in particular 64-bit ARM).

4.2.3 ARM 64-bit calling conventions

There is a standard calling convention for 64-bit ARM (documented by ARM), but iOS uses a slightly different one. Therefore, there are effectively two calling conventions: the standard one and iOS.

By default, LispWorks compiles code that selects which convention to use at run time. However, the difference between the conventions is quite minor and affects only a small number of functions, so the code is the same for most functions. Thus the overhead is quite small and you will not normally have a reason to pass :calling-convention for 64-bit ARM.

You can use the following values with :calling-convention to tell LispWorks to compile for a specific convention:

Compile only the iOS convention.
Compile only the standard convention.

Other values are treated as the default.

Note that all the keywords used for 32-bit ARM (see 4.2.2 ARM 32-bit calling conventions), with the exception of :ios, are treated as the default on 64-bit ARM.

4.2.4 Fastcall on 32-bit x86 platforms

On 32-bit x86 platforms, the C compilers have a fastcall calling convention. In Visual C and the GNU C compiler, this it is specified by the __fastcall qualifier. If you call a foreign function that is compiled as a fastcall, you must specify the calling convention :fastcall.

On other architectures, the calling convention :fastcall is quietly ignored, and the code produced is the same as would be produced without it.

The calling convention :fastcall cannot be used in foreign callables (calls from foreign code into LispWorks).

Foreign Language Interface User Guide and Reference Manual - 01 Dec 2021 19:34:57