Skip to content

Generate bigcount interfaces for Fortran and C #12226

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
12 changes: 11 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ ompi/mpi/fortran/mpiext/mpi-ext-module.F90
ompi/mpi/fortran/mpiext/mpi-f08-ext-module.F90
ompi/mpi/fortran/mpiext-use-mpi/mpi-ext-module.F90
ompi/mpi/fortran/mpiext-use-mpi-f08/mpi-f08-ext-module.F90
ompi/mpi/fortran/use-mpi-f08/psizeof_f08.f90

ompi/mpi/fortran/mpif-h/sizeof_f.f90
ompi/mpi/fortran/mpif-h/profile/p*.c
Expand Down Expand Up @@ -516,9 +517,10 @@ docs/_static
docs/_static/css/custom.css
docs/_templates

# Common Python virtual environment directory names
# Common Python virtual environment and cache directory names
venv
py??
__pycache__/

# Copies of PRRTE RST files (i.e., not source controlled in this tree)
docs/prrte-rst-content
Expand All @@ -528,3 +530,11 @@ docs/schizo-ompi-rst-content
# tarballs)
docs/html
docs/man

# Generated C Bindings
ompi/mpi/c/*_generated*.c

# Generated Fortran Bindings
ompi/mpi/fortran/use-mpi-f08/*_generated.F90
ompi/mpi/fortran/use-mpi-f08/base/*_generated.c
ompi/mpi/fortran/use-mpi-f08/mod/mpi-f08-interfaces-generated.h
1 change: 0 additions & 1 deletion config/ompi_config_files.m4
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ AC_DEFUN([OMPI_CONFIG_FILES],[
ompi/mpi/fortran/use-mpi-ignore-tkr/mpi-ignore-tkr-removed-interfaces.h
ompi/mpi/fortran/use-mpi-f08/Makefile
ompi/mpi/fortran/use-mpi-f08/base/Makefile
ompi/mpi/fortran/use-mpi-f08/profile/Makefile
ompi/mpi/fortran/use-mpi-f08/bindings/Makefile
ompi/mpi/fortran/use-mpi-f08/mod/Makefile
ompi/mpi/fortran/use-mpi-f08/mod/mpi-f08-interfaces.h
Expand Down
7 changes: 7 additions & 0 deletions config/ompi_configure_options.m4
Original file line number Diff line number Diff line change
Expand Up @@ -253,5 +253,12 @@ else
fi
AM_CONDITIONAL(OMPI_OMPIO_SUPPORT, test "$ompi_want_ompio" = "1")

# If the binding source files don't exist, then we need Python to generate them
AM_PATH_PYTHON([3.6],,[:])
binding_file="${srcdir}/ompi/mpi/c/ompi_send.c"
AS_IF([! test -e "$binding_file" && test "$PYTHON" = ":"],
[AC_MSG_ERROR([Open MPI requires Python >=3.6 for generating the bindings. Aborting])])
AM_CONDITIONAL(OMPI_GENERATE_BINDINGS,[test "$PYTHON" != ":"])

])dnl

69 changes: 69 additions & 0 deletions config/ompi_fortran_check_ts.m4
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
dnl -*- shell-script -*-
dnl
dnl Copyright (c) 2019 Research Organization for Information Science
dnl and Technology (RIST). All rights reserved.
dnl $COPYRIGHT$
dnl
dnl Additional copyrights may follow
dnl
dnl $HEADER$
dnl

# Check whether or not the C compiler supports ISO_Fortran_binding.h
# Also check whether C and Fortran compiler interoperate.
#
# OMPI_FORTRAN_CHECK_TS([action if found], [action if not found])
# ----------------------------------------------------
AC_DEFUN([OMPI_FORTRAN_CHECK_TS],[
AS_VAR_PUSHDEF([fortran_ts], [ompi_cv_fortran_have_ts])

AC_CHECK_HEADERS([ISO_Fortran_binding.h],
[AC_CACHE_CHECK([if Fortran and C compilers support ISO_Fortran_binding.h], fortran_ts,
[mkdir conftest.$$
cd conftest.$$

# Try to compile the C bindings
cat > conftest_c.c << EOF
#include <ISO_Fortran_binding.h>

int is_contiguous_c(CFI_cdesc_t* x) {
return CFI_is_contiguous(x);
}
EOF
OPAL_LOG_COMMAND([$CC $CCFLAGS -c conftest_c.c],
[cat > conftest.f90 << EOF
module MOD_IS_CONTIGUOUS

interface

function is_contiguous(buf) BIND(C, name="is_contiguous_c")
implicit none
type(*), dimension(..) :: buf
integer :: is_contiguous
end function is_contiguous

end interface

end module

program test_is_contiguous
use MOD_IS_CONTIGUOUS
implicit none
integer :: a0, a1(2), a2(2,2), a3(2,2,2)
write (*,*) is_contiguous(a0)
write (*,*) is_contiguous(a1)
write (*,*) is_contiguous(a2)
write (*,*) is_contiguous(a3)
end program
EOF
OPAL_LOG_COMMAND([$FC $FCFLAGS $FCFLAGS_f90 -o conftest conftest.f90 conftest_c.o $LDFLAGS $LIBS],
[AS_VAR_SET(fortran_ts, yes)],
[AS_VAR_SET(fortran_ts, no)])],
[AS_VAR_SET(fortran_ts, no)])
cd ..
rm -rf conftest.$$])],
[AS_VAR_SET(fortran_ts, no)])

AS_VAR_IF(fortran_ts, [yes], [$1], [$2])
AS_VAR_POPDEF([fortran_ts])dnl
])
63 changes: 48 additions & 15 deletions config/ompi_setup_mpi_fortran.m4
Original file line number Diff line number Diff line change
Expand Up @@ -435,14 +435,27 @@ end program]])],
# If we got all the stuff from above, then also look for the new
# F08 syntax that we can use for the use_mpif08 module.

# We need to have ignore TKR functionality to build the mpi_f08
OMPI_FORTRAN_HAVE_TS=0
OMPI_MPI_SUBARRAYS_SUPPORTED=.false.
OMPI_MPI_ASYNC_PROTECTS_NONBLOCKING=.false.
AS_IF([test $OMPI_TRY_FORTRAN_BINDINGS -ge $OMPI_FORTRAN_USEMPIF08_BINDINGS],
[OMPI_FORTRAN_CHECK_TS([OMPI_FORTRAN_HAVE_TS=1])])

# We need to have ignore TKR or the ISO Fortran bindings functionality to build the mpi_f08
# module
AS_IF([test $OMPI_TRY_FORTRAN_BINDINGS -ge $OMPI_FORTRAN_USEMPIF08_BINDINGS && \
test $OMPI_FORTRAN_HAVE_IGNORE_TKR -eq 1],
[OMPI_BUILD_FORTRAN_BINDINGS=$OMPI_FORTRAN_USEMPIF08_BINDINGS
OMPI_FORTRAN_F08_PREDECL=$OMPI_FORTRAN_IGNORE_TKR_PREDECL
OMPI_FORTRAN_F08_TYPE=$OMPI_FORTRAN_IGNORE_TKR_TYPE
])
AS_IF([test $OMPI_TRY_FORTRAN_BINDINGS -ge $OMPI_FORTRAN_USEMPIF08_BINDINGS],
[AS_IF([test $OMPI_FORTRAN_HAVE_IGNORE_TKR -eq 1],
[OMPI_BUILD_FORTRAN_BINDINGS=$OMPI_FORTRAN_USEMPIF08_BINDINGS
OMPI_FORTRAN_F08_PREDECL=$OMPI_FORTRAN_IGNORE_TKR_PREDECL
OMPI_FORTRAN_F08_TYPE=$OMPI_FORTRAN_IGNORE_TKR_TYPE
])
AS_IF([test $OMPI_FORTRAN_HAVE_TS -eq 1],
[OMPI_BUILD_FORTRAN_BINDINGS=$OMPI_FORTRAN_USEMPIF08_BINDINGS
OMPI_MPI_SUBARRAYS_SUPPORTED=.true.
OMPI_MPI_ASYNC_PROTECTS_NONBLOCKING=.true.])])

AC_SUBST(OMPI_MPI_SUBARRAYS_SUPPORTED)
AC_SUBST(OMPI_MPI_ASYNC_PROTECTS_NONBLOCKING)

# The overall "_BIND_C" variable will be set to 1 if we have all
# the necessary forms of BIND(C)
Expand Down Expand Up @@ -576,17 +589,13 @@ end type test_mpi_handle],
])

OMPI_FORTRAN_NEED_WRAPPER_ROUTINES=1
OMPI_FORTRAN_F08_PREDECL='!'
OMPI_FORTRAN_F08_TYPE=real
OMPI_FORTRAN_HAVE_F08_ASSUMED_RANK=0
AS_IF([test $OMPI_TRY_FORTRAN_BINDINGS -ge $OMPI_FORTRAN_USEMPIF08_BINDINGS && \
test $OMPI_BUILD_FORTRAN_BINDINGS -ge $OMPI_FORTRAN_USEMPIF08_BINDINGS],
[ # Look for Fortran 2008 assumed rank syntax
OMPI_FORTRAN_CHECK_F08_ASSUMED_RANK(
[ # If we have assumed rank, we can build the use
# mpi_f08 module "better"
OMPI_FORTRAN_F08_PREDECL='!'
OMPI_FORTRAN_F08_TYPE='type(*), dimension(..)'
OMPI_FORTRAN_HAVE_F08_ASSUMED_RANK=1])

# Which mpi_f08 implementation are we using?
Expand Down Expand Up @@ -616,6 +625,12 @@ end type test_mpi_handle],
[OMPI_FORTRAN_ELEMENTAL_TYPE=])])
AC_SUBST(OMPI_FORTRAN_ELEMENTAL_TYPE)

OMPI_FORTRAN_HAVE_C_ISO_FORTRAN=0
AS_IF([test $OMPI_TRY_FORTRAN_BINDINGS -ge $OMPI_FORTRAN_USEMPIF08_BINDINGS && \
test $OMPI_BUILD_FORTRAN_BINDINGS -ge $OMPI_FORTRAN_USEMPIF08_BINDINGS],
[OMPI_FORTRAN_CHECK_TS([OMPI_FORTRAN_HAVE_TS=1],
[OMPI_FORTRAN_HAVE_TS=0])])

# Note: the current implementation *only* has wrappers;
# there is no optimized implementation for a "good"
# compiler. I'm leaving the above logic in place for
Expand Down Expand Up @@ -778,10 +793,9 @@ end type test_mpi_handle],
# This goes into mpifort-wrapper-data.txt
AC_SUBST(OMPI_FORTRAN_USEMPIF08_LIB)

# These go into interfaces/mpi-f08-interfaces-[no]bind.h (and
# mpi-f*-interfaces*.h files)
AC_SUBST(OMPI_FORTRAN_F08_PREDECL)
AC_SUBST(OMPI_FORTRAN_F08_TYPE)
# These go into mod/mpi-f08-interfaces.h
AC_SUBST(OMPI_F08_IGNORE_TKR_PREDECL)
AC_SUBST(OMPI_F08_IGNORE_TKR_TYPE)

AC_SUBST(OMPI_MPI_PREFIX)
AC_SUBST(OMPI_MPI_BIND_PREFIX)
Expand Down Expand Up @@ -863,6 +877,25 @@ end type test_mpi_handle],
# For configure-fortran-output.h
AC_SUBST(OMPI_FORTRAN_HAVE_BIND_C)

AM_CONDITIONAL(OMPI_FORTRAN_HAVE_TS,
[test $OMPI_FORTRAN_HAVE_TS -eq 1])
AC_SUBST(OMPI_FORTRAN_HAVE_TS)
AC_DEFINE_UNQUOTED([OMPI_FORTRAN_HAVE_TS],
[$OMPI_FORTRAN_HAVE_TS],
[For ompi/mpi/fortran/use-mpi-f08/base/ts.*: whether the compiler supports TS 29113 or not])

AS_IF([test $OMPI_FORTRAN_HAVE_TS -eq 1],
[OMPI_F08_IGNORE_TKR_TYPE="type(*), dimension(..)"
OMPI_F08_IGNORE_TKR_PREDECL="no attribute required for"
OMPI_F08_BINDINGS_EXTENSION="ts"
OMPI_F08_BINDINGS_TS_SUFFIX="ts"],
[OMPI_F08_IGNORE_TKR_TYPE=$OMPI_FORTRAN_IGNORE_TKR_TYPE
OMPI_F08_IGNORE_TKR_PREDECL=${OMPI_FORTRAN_IGNORE_TKR_PREDECL:1}
OMPI_F08_BINDINGS_EXTENSION="f"
OMPI_F08_BINDINGS_TS_SUFFIX=""])
AC_SUBST(OMPI_F08_BINDINGS_EXTENSION)
AC_SUBST(OMPI_F08_BINDINGS_TS_SUFFIX)

# Somewhat redundant because ompi/Makefile.am won't traverse into
# ompi/mpi/fortran/use-mpi-f08 if it's not to be built, but we
# might as well have ompi/mpi/fortran/use-mpi-f08/Makefile.am be
Expand Down
112 changes: 112 additions & 0 deletions docs/developers/bindings.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
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.

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:

.. code-block:: c

#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 defintion 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:

.. code-block:: c

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.

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).

.. _Fortran 2018: https://fortranwiki.org/fortran/show/Fortran+2018

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.

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.
1 change: 1 addition & 0 deletions docs/developers/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ probably don't need to read this section.
gnu-autotools
sphinx
rst-for-markdown-expats.rst
bindings
11 changes: 11 additions & 0 deletions docs/developers/prerequisites.rst
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,15 @@ build them manually, see the :ref:`how to build and install GNU
Autotools section <developers-installing-autotools-label>` for much
more detail.

Python
------

Python >= v3.6 is required for generating the Fortran bindings, which
is necessary if you build Open MPI from a Git clone.

Python is also required for running Sphinx to generate the docs, too
(:ref:`see below <developers-requirements-sphinx-label>`).

Perl
----

Expand Down Expand Up @@ -88,6 +97,8 @@ MacPorts on MacOS), see `the Flex Github repository
<https://github.com/westes/flex>`_.


.. _developers-requirements-sphinx-label:

Sphinx (and therefore Python)
-----------------------------

Expand Down
Loading