13.13. C and Fortran Bindings

The C and Fortran (mpi_f08) bindings are generated from Python code in ompi/mpi/bindings. Both the language bindings are generated from template files for each function. In the C case, each template file corresponds to a single generated C file, while in the Fortran case there are three major files generated for all functions.

The Python code depends on special prototype lines used with both the C and Fortran bindings. These “prototypes” are designed to be easy to parse and use specific type constants that can be mapped directly to the expanded language-specific code, error-handling, and conversion code.

13.13.1. C Bindings

This will walk through adding (or converting) a plain-C binding into a templated version controlled by the script.

As an example, for MPI_Send you might have a C file that looks something like this:

#include "ompi_config.h"
...other includes...

int MPI_Send(const void *buf, int count, MPI_Datatype datatype, int dest,
             int tag, MPI_Comm comm)
{
    ...internal checks...
    return internal_mpi_send(buf, count, datatype, dest, tag, comm);
}

To convert this to a template, you will have to first ensure that only a single function is defined in the file, removing or abstracting out static functions, and separating multiple definitions, such as MPI_Send and MPI_Isend, into different files. The template should also not include any macro-processing that attempts to change the name of the function or parameter types; this code should be generated by the script, or abstracted into header files that can work easily with multiple functions.

At this point, the template should look like the example above, with a “header” section, with simple includes or macros, maybe a static global, and the function definition and nothing else.

The next step is to convert the signature line into the prototype format that the script expects. For MPI_Send, this should look something like this:

PROTOTYPE ERROR_CLASS send(BUFFER buf, COUNT count, DATATYPE type, RANK dest,
                           TAG tag, COMM comm)

Notice how the function name is changed, the MPI_ prefix removed and the rest converted to lowercase, and also how each parameter is simplified into a TYPE name format, where the TYPE conforms to an allowed list in ompi/mpi/bindings/ompi_bindings/c_type.py. For newer functions and types, you may have to extend the c_type.py file with a new class showing how to handle the type.

The final step is to update Makefile.am, adding the template name, in this case send.c.in, to the prototype_sources variable, and the generated file name, generated_send.c, to interface_profile_sources. The generated file name must be of the form generated_${basename}.c, where ${basename} is the name of the template file stripped of all extensions.

13.13.2. Fortran Bindings

Adding new Fortran bindings follows a similar process to the C version above. All new interfaces are actually based on a single C-template file following the same format as the C interface templates. However, the C file generated will use Fortran-specific arguments, including CFI_* arguments, when TS 29113 is enabled, MPI_Fint * arguments in other cases, and others specific to how the Fortran MPI types are defined. Most of these files perform Fortran-specific error handling, Fortran-to-C type conversion, and other necessary steps before calling the actually C bindings with the proper arguments.

These templates are used not only to generate a C backing file for the Fortran code, but also the Fortran interface definitions and the Fortran subroutines corresponding to the generated C file. These are output in three separate files:

  • ompi/mpi/fortran/use-mpi-f08/api_f08_generated.F90

  • ompi/mpi/fortran/use-mpi-f08/base/api_f08_generated.c

  • ompi/mpi/fortran/use-mpi-f08/mod/mpi-f08-interfaces-generated.h

The Fortran file api_f08_generated.F90 contains all the internal subroutine definitions, each of which makes a call into corresponding C functions. The internal subroutine names are mapped to the external interface, including multiple interfaces for the bigcount version of functions, in mpi-f08-interfaces-generated.h. The C file api_f08_generated.c basically contains a concatenation of all fully expanded C templates. These files contain preprocessing directives to ensure they can support compilers with and without TS 29113 support, allowing use of CFI_cdesc_t types when available (see Fortran 2018 for more details).

If a new type needs to be added, then one will need to extend fortran_type.py in ompi/mpi/bindings/ompi_bindings with an additional type class specifying how to handle the type in the above generated files, including any required key-value attributes for more complicated types. New types use a Type base class with functions that can be implemented by derived classes, each returning expanded Fortran or C code.

13.13.3. Shared definitions: one source of truth

Two different generators emit the MPI Forum (standard) ABI, and they are driven by different inputs:

  • bindings.py (with the ompi_bindings package) generates the bindings themselves, from the .c.in templates.

  • c_header.py generates the ABI mpi.h, from pympistandard plus the ABI JSON.

Anything the two must agree on — the _ABI_INTERNAL name-mangling suffix, the opaque handle types, the list of deprecated/removed symbols that are pruned from the ABI — therefore lives in ompi_bindings/consts.py, which is the single source of truth. Do not define such a value a second time in c_header.py: the two generators would then be free to drift, and the emitted header would stop matching the emitted bindings.

The tests described below enforce this, including a check that the deprecated list does not drift away from the __mpi_interface_deprecated__ and __mpi_interface_removed__ markers in ompi/include/mpi.h.in.

13.13.4. Unit-testing the generator

The generator is covered by unit tests in ompi/test/bindings-generator/test_bindings_generator.py, which run as part of make check (and therefore in CI). They are plain Python: they import ompi_bindings straight out of the source tree and render a handful of real .c.in templates in memory. Nothing is compiled, no MPI is launched, and no build products are needed, so you can also run them directly against a source tree that has never been built:

shell$ python3 ompi/test/bindings-generator/test_bindings_generator.py

The tests cover the parts of the generator that are pure and deterministic, and where a defect would otherwise only surface as a compile error in the generated code (or, for a naming or casing mistake, not until run time):

  • Prototype parameter parsing — the NAME, NAME:COUNT, and NAME:COUNT:OUTCOUNT forms.

  • Name mangling — MPI_Xxx / PMPI_Xxx / _c bigcount suffixes, and the _ABI_INTERNAL suffix used to keep the standard ABI names from colliding with Open MPI’s own.

  • Prototype classification — the predicates that decide whether a function gets a bigcount variant or needs user-callback wrappers.

  • Template rendering — that representative templates render through both the ompi and standard ABI paths without raising, and that the standard ABI output calls the internal ompi_abi_* shim rather than the public MPI_* symbol.

If you add a new type to the Type hierarchy, a new prototype form, or a new code-emission path, add a test here as well.

13.13.5. Adding New Fortran Interfaces

Open MPI supports multiple Fortran binding layers beyond the modern use mpi_f08 interface described above. Adding a new MPI function requires updating all applicable layers. The MPI standard defines Fortran interfaces for most functions, and Open MPI implements these across three main Fortran binding systems. This section describes the complete pattern for adding new Fortran bindings.

13.13.5.1. The Three Fortran Binding Layers

  1. mpif.h (ompi/mpi/fortran/mpif-h/) — The original Fortran 77 interface. Functions are hand-written C wrappers that convert between Fortran and C types.

  2. use mpi_f08 (ompi/mpi/fortran/use-mpi-f08/) — The modern Fortran 2008 interface with full type safety. Uses template files (.c.in) processed by the build system to generate type-specific variants. This is the interface described in the main section above.

  3. use mpi (ignore TKR) (ompi/mpi/fortran/use-mpi-ignore-tkr/) — An alternative Fortran 90/95 interface using compiler directives to ignore type/kind/rank checks. Interface declarations are in mpi-ignore-tkr-interfaces.h.in.

13.13.5.2. Checklist for Adding a New Fortran Function

When adding a new MPI function that has Fortran bindings (nearly all functions except C-specific ones), you must update multiple files across the layers. Use existing functions as templates.

13.13.5.2.1. For mpif.h Layer

  1. Create the wrapper: Add <function_name>_f.c in ompi/mpi/fortran/mpif-h/. This file:

    • Includes ompi_config.h first

    • Includes ompi/mpi/fortran/mpif-h/bindings.h

    • Uses OMPI_GENERATE_F77_BINDINGS to generate the strong PMPI_* entry points in all name-mangling variants

    • Uses OMPI_GENERATE_WEAK_F77_BINDINGS to generate weak MPI_* entry points that forward to PMPI_* (on platforms without weak aliases, this emits a weak symbol; where weak aliases exist, the macro in bindings.h already emitted the alias)

    • Defines the wrapper function ompi_<function_name>_f()

    • Converts Fortran types to C types using PMPI_Status_f2c(), OMPI_FINT_2_INT(), OMPI_INT_2_FINT(), etc.

    • Calls the C implementation (typically PMPI_<Function_name>())

    • Must handle MPI_STATUS_IGNORE and similar special constants

  2. Update Makefile.am: Add the new .c file to lib@OMPI_LIBMPI_NAME@_mpifh_la_SOURCES in ompi/mpi/fortran/mpif-h/Makefile.am, maintaining alphabetical order.

  3. Update prototypes: Add the function prototype to ompi/mpi/fortran/mpif-h/prototypes_mpi.h using the PN2() macro:

    PN2(void, MPI_Function_name, mpi_function_name, MPI_FUNCTION_NAME,
        (MPI_Fint *arg1, MPI_Fint *arg2, MPI_Fint *ierr));
    

13.13.5.2.2. For use mpi_f08 Layer

  1. Create the template: Add <function_name>.c.in in ompi/mpi/fortran/use-mpi-f08/. This file:

    • Contains a PROTOTYPE declaration (see existing files for syntax)

    • Uses placeholders like @INNER_CALL@ that the build system expands

    • Handles type conversions for Fortran 2008 types

    • Manages MPI_F08_STATUS_IGNORE and similar constants

  2. Update prototype list: Add the template filename to prototype_files in ompi/mpi/fortran/use-mpi-f08/Makefile.prototype_files, maintaining alphabetical order.

13.13.5.2.3. For use mpi (ignore TKR) Layer

The use mpi (ignore TKR) interface declarations are generated from the binding metadata, similar to the use mpi_f08 layer. The interface declarations live in generated .h.in files under ompi/mpi/fortran/use-mpi-ignore-tkr/.

  1. Ensure the binding metadata is complete: The Python generator in ompi/mpi/bindings/ reads the MPI function signatures and generates interface declarations for all three Fortran layers. If the mpif.h and use mpi_f08 layers are complete, the use mpi layer should be generated automatically.

  2. Verify generated interfaces: After building, check that the new function appears in the generated ompi/mpi/fortran/use-mpi-ignore-tkr/mpi-ignore-tkr-interfaces-generated.h file with correct !DIR$ IGNORE_TKR directives. This file is compiled twice during the build: once with OMPI_BUILD_MPI_PROFILING defined to generate PMPI_* interfaces, and once without the define to generate MPI_* interfaces.

  3. Check PMPI support: Verify both MPI_* and PMPI_* interfaces are generated for the new function.

13.13.5.3. Key Conventions for Fortran Bindings

  • Function naming: The C wrapper in mpif.h uses the prefix ompi_<function_name>_f(). The OMPI_GENERATE_F77_BINDINGS macro provides the strong PMPI_* entry points in various case/underscore combinations (PMPI_FUNCTION_NAME, pmpi_function_name, pmpi_function_name_, pmpi_function_name__). The OMPI_GENERATE_WEAK_F77_BINDINGS macro provides weak MPI_* entry points that forward to the strong PMPI_* symbols.

  • Profiling (PMPI) support: All bindings must support the PMPI profiling interface. Open MPI compiles each mpif.h binding exactly once (with OMPI_BUILD_MPI_PROFILING=1), emitting both the strong PMPI_* entry points and weak MPI_* entry points in a single compilation. Where weak aliases are available (ELF), the weak MPI_* is a zero-cost alias to PMPI_*; on platforms without weak aliases (e.g., macOS), the weak MPI_* is a forwarding definition. The profile/ subdirectory no longer exists because a second compilation is not necessary.

  • Type conversions: Use the provided macros:

    • OMPI_INT_2_FINT() / OMPI_FINT_2_INT() for integers

    • OMPI_LOGICAL_2_INT() / OMPI_INT_2_LOGICAL() for logicals

    • PMPI_Status_f2c() / PMPI_Status_c2f() for MPI_Status

  • Special constants: Check for Fortran special constants like OMPI_IS_FORTRAN_STATUS_IGNORE() before dereferencing pointers.

  • Error codes: Always set *ierr = OMPI_INT_2_FINT(c_ierr) to return errors to Fortran in the correct format.

13.13.5.4. Weak Symbols vs. Weak Aliases

Open MPI distinguishes between weak symbols and weak aliases:

  • A weak alias is a linker directive (e.g., #pragma weak MPI_Send = PMPI_Send or __attribute__((weak, alias("PMPI_Send")))) that makes one symbol name resolve to another at link time, with zero runtime cost. Weak aliases are supported on ELF platforms (Linux, most Unix) but not on macOS / Mach-O.

  • A weak symbol is a symbol marked with the weak attribute (e.g., __attribute__((weak))), which allows it to be overridden by a strong definition at link time. On macOS, a weak MPI_Send can be a forwarding function that calls PMPI_Send; this has a trivial runtime cost (one branch instruction) but works everywhere.

Open MPI’s configure terminology and preprocessor macros use “weak alias” consistently: OPAL_HAVE_WEAK_ALIASES, __opal_attribute_weak_alias__, --enable-weak-aliases. The plain weak symbol attribute is __opal_attribute_weak__.

13.13.5.5. Build System Interaction

After adding Fortran bindings, the build system must be regenerated (see the Open MPI developer documentation on modifying the configure/build system). Template files (.c.in) are processed during the build to generate multiple type-specific wrapper functions for the Fortran 2008 interface.

Parameter naming validation: The build system includes check_fortran_param_names.py which validates that Fortran interface parameter names comply with the MPI standard. This check runs automatically during the build for both use mpi_f08 and use mpi layers. If you add a new function, ensure parameter names match the standard exactly.

Testing: After adding bindings, compile a simple Fortran test program that uses the new function with each interface (use mpi_f08, use mpi, and include 'mpif.h') to verify all layers work correctly. Test both the MPI_* and PMPI_* entry points to verify profiling interface support.

13.13.6. Other Considerations

Keep in mind that the generated files will not be deleted with a make clean or make distclean; instead use make maintainer-clean to delete those.