Skip to content

Updated ABI generation code and new libraries - #13280

Merged
hppritcha merged 13 commits into
open-mpi:mainfrom
hppritcha:abi-generate-ver2
Aug 5, 2026
Merged

Updated ABI generation code and new libraries#13280
hppritcha merged 13 commits into
open-mpi:mainfrom
hppritcha:abi-generate-ver2

Conversation

@hppritcha

@hppritcha hppritcha commented May 27, 2025

Copy link
Copy Markdown
Member

Implement the MPI-5 c ABI functionality.

Two external MPI libraries are now created: libmpi.so and libmpi_abi.so. Backend code that was originally in libmpi.la has been extracted into a libopen-mpi.la to be linked in to both libraries.

Almost all of the Open MPI C interface is now being generated from templates processed by the binding infrastructure.
The binding infrastructure introduced to support big count has been extended to generate code to support the MPI ABI specification. The bindings infrastructure is also used to generate the MPI ABI compliant mpi.h header file, as well as the converter functions to translate between OMPI and MPI ABI constants.

Before attempting to review this PR it would probably be good to become familiar with the MPI-5 ABI chapter and also to peruse the README ((ompi/mpi/README_ABI.md) included in this PR.

A few todos left:

  • add a bit of plumbing to assist with cleanup of resources used by the attributes callback function wrappers
  • write up docs to explain ABI support both for users and for developers
  • add man pages
  • add fortran entry points for MPI_Abi functions
  • open separate PR to deal with the libnbc release_arrays problem
  • refactor how FORTRAN datatypes are handled in configury procedure. Changes to comply with section 20.4 of the MPI-5 standard concerning these datatypes is not included in this PR.
  • Update python framework to use refactored libnbc release arrays code (PR nbc: rework the way to add arrays to clean up #13529)
  • add info about MPI ABI and Fortran bindings options

todos for follow-up PRs:

  • optimize some converters, in particular MPI ABI datatype -> OMPI ABI datatype and reverse
  • integrate the self standing mpi.h/abi.h generator into the python framework
  • refactor/simplify python framework in particular c_type.py
  • (MPI Forum repos) fix up standard latex with annotations for not-in-abi properties, types of some constants, update pympistandard to address some issues found in that package.
  • PR to actually use FORTRAN datatype info if provided by an application via MPI_Abi_set_fortran_info and MPI_Abi_set_fortran_booleans.

This PR supercedes #12033

Fixes #14243

@dalcinl

dalcinl commented Jul 11, 2025

Copy link
Copy Markdown
Contributor

Maybe you should somehow vendor the mpi.h header from https://github.com/mpi-forum/mpi-abi-stubs and use it as the baseline to extract values for handles and constants? That's what MPICH is doing.
Alternatively, the mpi.h header the mpi-abi-stubs repo could be downloaded on the fly by some Python script and the values in that header used to update stuff to be committed in the ompi repo.

In short, I think it would be in everyone's convenience to use https://github.com/mpi-forum/mpi-abi-stubs as the "source of truth" for ABI-related stuff, avoiding manual synchronization of handle/constant values.

@jsquyres

Copy link
Copy Markdown
Member

I wonder if we should make the bot not complain about unsigned commits on draft PRs. That would reduce some of the noise on PR's like this.

hppritcha added a commit to hppritcha/ompi that referenced this pull request Aug 25, 2025
Turns out that in commit 6bd36a7 we had a function that is not part of the MPI standard.
This showed while working on ABI support - which requires us to pay attention to the truth
rather than make stuff up.

This commit removes our made up MPI_Session_set_info method.
Turns out who ever was doing the fortran bindings knew this wasn't a method in the standard
so there's no need to change the fortran bindings.  Same thing applies to the man pages.

Related to open-mpi#13280

Signed-off-by: Howard Pritchard <howardp@lanl.gov>
@dalcinl

dalcinl commented Aug 27, 2025

Copy link
Copy Markdown
Contributor

@hppritcha There is some issue with out-of-source builds, i.e

git clone ... ompi-main
...
mkdir -p ompi-BUILD/main
cd ompi-BUILD/main
../../ompi-main/configure ...
make install
Making install in mpi/c
make[2]: Entering directory '/home/dalcinl/Devel/REPOS/ompi-BUILD/main/ompi/mpi/c'
mkdir -p standard_abi
  GENERATE abi.h
  GENERATE standard_abi/mpi.h
Traceback (most recent call last):
  File "/home/dalcinl/Devel/REPOS/ompi-BUILD/main/ompi/mpi/c/../../../../../ompi-main/ompi/mpi/bindings/c_header.py", line 262, in <module>
    with open(OUTPUT, 'tw') as header_out:
         ~~~~^^^^^^^^^^^^^^
FileNotFoundError: [Errno 2] No such file or directory: '../../../../../ompi-main/ompi/mpi/c/standard_abi/mpi.h'
make[2]: *** [Makefile:23335: standard_abi/mpi.h] Error 1
make[2]: *** Waiting for unfinished jobs....
make[2]: Leaving directory '/home/dalcinl/Devel/REPOS/ompi-BUILD/main/ompi/mpi/c'
make[1]: *** [Makefile:2785: install-recursive] Error 1
make[1]: Leaving directory '/home/dalcinl/Devel/REPOS/ompi-BUILD/main/ompi'
make: *** [Makefile:1526: install-recursive] Error 1

@hppritcha

Copy link
Copy Markdown
Member Author

interesting distcheck didn't check this.

@hppritcha

Copy link
Copy Markdown
Member Author

jenkins ci runs make distcheck

@dalcinl

dalcinl commented Aug 28, 2025

Copy link
Copy Markdown
Contributor

The ABI mpi.h header is missing some MPI_T_XXX types. Also, the Status f08/c converters are declared, and they should not.

I did the following manual edits to the installed mpi.h header:

diff -up ./mpi.h.orig ./mpi.h
--- ./mpi.h.orig	2025-08-28 18:55:49.842968779 +0300
+++ ./mpi.h	2025-08-28 19:03:07.192957305 +0300
@@ -490,6 +490,13 @@ enum {
 /* C preprocessor constants and Fortran parameters */
 /* $CATEGORY:C_PREPROCESSOR_CONSTANTS_FORTRAN_PARAMETERS$ */
 
+typedef struct MPI_T_enum_t* MPI_T_enum;
+typedef struct MPI_T_cvar_handle_t* MPI_T_cvar_handle;
+typedef struct MPI_T_pvar_handle_t* MPI_T_pvar_handle;
+typedef struct MPI_T_pvar_session_t* MPI_T_pvar_session;
+typedef struct MPI_T_event_registration_t* MPI_T_event_registration;
+typedef struct MPI_T_event_instance_t* MPI_T_event_instance;
+
 /* Handles used in the MPI tool information interface */
 #define MPI_T_ENUM_NULL                       ((MPI_T_enum) 0)
 #define MPI_T_CVAR_HANDLE_NULL                ((MPI_T_cvar_handle) 0)
@@ -558,20 +565,20 @@ enum {
 };
 
 /* Source event ordering guarantees in the MPI tool information interface */
-enum {
+typedef enum MPI_T_source_order {
     MPI_T_SOURCE_ORDERED                      = 1,
     MPI_T_SOURCE_UNORDERED                    = 2,
-};
+} MPI_T_source_order;
 
 /*
  * Callback safety requirement levels used in the MPI tool information interface
  */
-enum {
+typedef enum MPI_T_cb_safety {
     MPI_T_CB_REQUIRE_NONE                     = 0x00,
     MPI_T_CB_REQUIRE_MPI_RESTRICTED           = 0x03,
     MPI_T_CB_REQUIRE_THREAD_SAFE              = 0x0F,
     MPI_T_CB_REQUIRE_ASYNC_SIGNAL_SAFE        = 0x3F,
-};
+} MPI_T_cb_safety;
 
 
 /* Callback functions */
@@ -1107,13 +1114,13 @@ int MPI_Ssend_init(const void* buf, int
 int MPI_Ssend_init_c(const void* buf, MPI_Count count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm, MPI_Request* request);
 int MPI_Start(MPI_Request* request);
 int MPI_Startall(int count, MPI_Request array_of_requests[]);
-/* int MPI_Status_c2f(const MPI_Status* c_status, MPI_Fint* f_status);
- */int MPI_Status_c2f08(const MPI_Status* c_status, MPI_F08_status* f08_status);
-int MPI_Status_f082c(const MPI_F08_status* f08_status, MPI_Status* c_status);
-/* int MPI_Status_f082f(const MPI_F08_status* f08_status, MPI_Fint* f_status);
- *//* int MPI_Status_f2c(const MPI_Fint* f_status, MPI_Status* c_status);
- *//* int MPI_Status_f2f08(const MPI_Fint* f_status, MPI_F08_status* f08_status);
- */int MPI_Status_get_error(const MPI_Status* status, int* err);
+// /* int MPI_Status_c2f(const MPI_Status* c_status, MPI_Fint* f_status);
+//  */int MPI_Status_c2f08(const MPI_Status* c_status, MPI_F08_status* f08_status);
+// int MPI_Status_f082c(const MPI_F08_status* f08_status, MPI_Status* c_status);
+// /* int MPI_Status_f082f(const MPI_F08_status* f08_status, MPI_Fint* f_status);
+//  *//* int MPI_Status_f2c(const MPI_Fint* f_status, MPI_Status* c_status);
+//  *//* int MPI_Status_f2f08(const MPI_Fint* f_status, MPI_F08_status* f08_status);
+//  */int MPI_Status_get_error(const MPI_Status* status, int* err);
 int MPI_Status_get_source(const MPI_Status* status, int* source);
 int MPI_Status_get_tag(const MPI_Status* status, int* tag);
 int MPI_Status_set_cancelled(MPI_Status* status, int flag);
@@ -1799,13 +1806,13 @@ int PMPI_Ssend_init(const void* buf, int
 int PMPI_Ssend_init_c(const void* buf, MPI_Count count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm, MPI_Request* request);
 int PMPI_Start(MPI_Request* request);
 int PMPI_Startall(int count, MPI_Request array_of_requests[]);
-/* int PMPI_Status_c2f(const MPI_Status* c_status, MPI_Fint* f_status);
- */int PMPI_Status_c2f08(const MPI_Status* c_status, MPI_F08_status* f08_status);
-int PMPI_Status_f082c(const MPI_F08_status* f08_status, MPI_Status* c_status);
-/* int PMPI_Status_f082f(const MPI_F08_status* f08_status, MPI_Fint* f_status);
- *//* int PMPI_Status_f2c(const MPI_Fint* f_status, MPI_Status* c_status);
- *//* int PMPI_Status_f2f08(const MPI_Fint* f_status, MPI_F08_status* f08_status);
- */int PMPI_Status_get_error(const MPI_Status* status, int* err);
+// /* int PMPI_Status_c2f(const MPI_Status* c_status, MPI_Fint* f_status);
+//  */int PMPI_Status_c2f08(const MPI_Status* c_status, MPI_F08_status* f08_status);
+// int PMPI_Status_f082c(const MPI_F08_status* f08_status, MPI_Status* c_status);
+// /* int PMPI_Status_f082f(const MPI_F08_status* f08_status, MPI_Fint* f_status);
+//  *//* int PMPI_Status_f2c(const MPI_Fint* f_status, MPI_Status* c_status);
+//  *//* int PMPI_Status_f2f08(const MPI_Fint* f_status, MPI_F08_status* f08_status);
+//  */int PMPI_Status_get_error(const MPI_Status* status, int* err);
 int PMPI_Status_get_source(const MPI_Status* status, int* source);
 int PMPI_Status_get_tag(const MPI_Status* status, int* tag);
 int PMPI_Status_set_cancelled(MPI_Status* status, int flag);

I'm able to compile (using mpicc_abi) the pure-C demo/helloworld.c file from mpi4py sources using gcc-15.
Note however that the executable fails to link:

$ mpicc_abi helloworld.c
/usr/bin/ld: /home/devel/mpi/openmpi/main/lib/libmpi_abi.so: undefined reference to `ompi_mpi_info_null'
/usr/bin/ld: /home/devel/mpi/openmpi/main/lib/libmpi_abi.so: undefined reference to `ompi_info_get'
/usr/bin/ld: /home/devel/mpi/openmpi/main/lib/libmpi_abi.so: undefined reference to `ompi_mpi_info_env'
/usr/bin/ld: /home/devel/mpi/openmpi/main/lib/libmpi_abi.so: undefined reference to `ompi_isendrecv'
/usr/bin/ld: /home/devel/mpi/openmpi/main/lib/libmpi_abi.so: undefined reference to `ompi_info_get_nthkey'
/usr/bin/ld: /home/devel/mpi/openmpi/main/lib/libmpi_abi.so: undefined reference to `ompi_info_memkind_copy_or_set'
/usr/bin/ld: /home/devel/mpi/openmpi/main/lib/libmpi_abi.so: undefined reference to `ompi_info_set'
/usr/bin/ld: /home/devel/mpi/openmpi/main/lib/libmpi_abi.so: undefined reference to `ompi_info_delete'
/usr/bin/ld: /home/devel/mpi/openmpi/main/lib/libmpi_abi.so: undefined reference to `ompi_sendrecv'
/usr/bin/ld: /home/devel/mpi/openmpi/main/lib/libmpi_abi.so: undefined reference to `ompi_info_get_bool'
/usr/bin/ld: /home/devel/mpi/openmpi/main/lib/libmpi_abi.so: undefined reference to `ompi_info_free'
/usr/bin/ld: /home/devel/mpi/openmpi/main/lib/libmpi_abi.so: undefined reference to `ompi_info_memkind_cb'
/usr/bin/ld: /home/devel/mpi/openmpi/main/lib/libmpi_abi.so: undefined reference to `ompi_info_memkind_process'
/usr/bin/ld: /home/devel/mpi/openmpi/main/lib/libmpi_abi.so: undefined reference to `ompi_info_dup'
/usr/bin/ld: /home/devel/mpi/openmpi/main/lib/libmpi_abi.so: undefined reference to `ompi_mpiinfo_init'
/usr/bin/ld: /home/devel/mpi/openmpi/main/lib/libmpi_abi.so: undefined reference to `ompi_info_get_nkeys'
/usr/bin/ld: /home/devel/mpi/openmpi/main/lib/libmpi_abi.so: undefined reference to `ompi_info_allocate'
/usr/bin/ld: /home/devel/mpi/openmpi/main/lib/libmpi_abi.so: undefined reference to `ompi_mpiinfo_init_env'
/usr/bin/ld: /home/devel/mpi/openmpi/main/lib/libmpi_abi.so: undefined reference to `ompi_info_get_valuelen'
collect2: error: ld returned 1 exit status

@dalcinl

dalcinl commented Sep 2, 2025

Copy link
Copy Markdown
Contributor

@hppritcha Actually, take a look at mpi-forum/mpi-abi-stubs#63

@dalcinl

dalcinl commented Sep 3, 2025

Copy link
Copy Markdown
Contributor

The installed mpi.h header is still broken, I had to fix it as per the patch in my previous comment.
Afterwards, my basic helloworld program compiles and links, but it fails to run:

$ mpicc_abi helloworld.c
$ mpiexec -n 1 ./a.out 
[optiplex:00000] *** An error occurred in MPI_Init_thread
[optiplex:00000] *** reported by process [3633774593,0]
[optiplex:00000] *** on a NULL communicator
[optiplex:00000] *** MPI_ERR_ARG: invalid argument of some other kind
[optiplex:00000] *** MPI_ERRORS_ARE_FATAL (processes in this communicator will now abort,
[optiplex:00000] ***    and MPI will try to terminate your MPI job as well)
--------------------------------------------------------------------------
prterun has exited due to process rank 0 with PID 0 on node optiplex calling
"abort". This may have caused other processes in the application to be
terminated by signals sent by prterun (as reported here).
--------------------------------------------------------------------------

@dalcinl

dalcinl commented Sep 3, 2025

Copy link
Copy Markdown
Contributor

@hppritcha The following definitions in the generated mpi.h file are wrong, they should be like ((MPI_Comm_copy_attr_function*) 0), that is, add the missing * to get a function POINTER type.
https://github.com/mpi-forum/mpi-abi-stubs/blob/main/mpi.h#L470

/* Predefined functions */
#define MPI_COMM_NULL_COPY_FN                 ((MPI_Comm_copy_attr_function) 0)
#define MPI_COMM_DUP_FN                       ((MPI_Comm_copy_attr_function) 1)
#define MPI_COMM_NULL_DELETE_FN               ((MPI_Comm_delete_attr_function) 0)
#define MPI_WIN_NULL_COPY_FN                  ((MPI_Win_copy_attr_function) 0)
#define MPI_WIN_DUP_FN                        ((MPI_Win_copy_attr_function) 1)
#define MPI_WIN_NULL_DELETE_FN                ((MPI_Win_delete_attr_function) 0)
#define MPI_TYPE_NULL_COPY_FN                 ((MPI_Type_copy_attr_function) 0)
#define MPI_TYPE_DUP_FN                       ((MPI_Type_copy_attr_function) 1)
#define MPI_TYPE_NULL_DELETE_FN               ((MPI_Type_delete_attr_function) 0)
#define MPI_CONVERSION_FN_NULL                ((MPI_Datarep_conversion_function) 0)
#define MPI_CONVERSION_FN_NULL_C              ((MPI_Datarep_conversion_function_c) 0)

/* Deprecated predefined functions */
#define MPI_NULL_COPY_FN                      ((MPI_Copy_function) 0)
#define MPI_DUP_FN                            ((MPI_Copy_function) 1)
#define MPI_NULL_DELETE_FN                    ((MPI_Delete_function) 0)

@dalcinl

dalcinl commented Sep 3, 2025

Copy link
Copy Markdown
Contributor

PS: You may have a similar problems in some MPI_T_XXX routines having arguments of type MPI_T_xxx_function, I believe you should add the * .

Comment thread ompi/mpi/c/abi_converters.h Outdated
@dalcinl

dalcinl commented Sep 3, 2025

Copy link
Copy Markdown
Contributor

@hppritcha What's your plan for stuff introduced in MPI 4.1 and MPI 5.0? The generated mpi.h header says MPI_VERSION=5, however the MPI library libmpi_abi.so.0 seems to miss a bunch of stuff from MPI 4.1 and 5.0.

This is a list of what I managed to detect as missing from mpi4py's configuration machinery for missing MPI stuff.
Note there are a few that are quite old, like MPI_Aint_add/diff, MPI_Wtick, and MPI_Pcontrol.

MPI_Aint_add
MPI_Aint_diff
MPI_Type_get_value_index
MPI_Type_get_envelope
MPI_Type_get_contents
MPI_Type_get_envelope_c
MPI_Type_get_contents_c
MPI_Buffer_flush
MPI_Buffer_iflush
MPI_Comm_attach_buffer
MPI_Comm_detach_buffer
MPI_Comm_flush_buffer
MPI_Comm_iflush_buffer
MPI_Session_attach_buffer
MPI_Session_detach_buffer
MPI_Session_flush_buffer
MPI_Session_iflush_buffer
MPI_Comm_errhandler_fn
MPI_Comm_attach_buffer_c
MPI_Comm_detach_buffer_c
MPI_Session_attach_buffer_c
MPI_Session_detach_buffer_c
MPI_Remove_error_class
MPI_Remove_error_code
MPI_Remove_error_string
MPI_Abi_get_fortran_info
MPI_Get_hw_resource_info
MPI_Wtick
MPI_Pcontrol
MPI_Comm_toint
MPI_Errhandler_toint
MPI_File_toint
MPI_Group_toint
MPI_Info_toint
MPI_Message_toint
MPI_Op_toint
MPI_Request_toint
MPI_Session_toint
MPI_Type_toint
MPI_Win_toint
MPI_Comm_fromint
MPI_Errhandler_fromint
MPI_File_fromint
MPI_Group_fromint
MPI_Info_fromint
MPI_Message_fromint
MPI_Op_fromint
MPI_Request_fromint
MPI_Session_fromint
MPI_Type_fromint
MPI_Win_fromint

@dalcinl

dalcinl commented Sep 3, 2025

Copy link
Copy Markdown
Contributor

Current status regarding mpi4py:

  • I'm able to build mpi4py using export MPICC=mpicc_abi.
  • However, due to missing symbols, I'm not able to import the Python module.
  • I can switch via LD_LIBRARY_PATH to MPICH's libmpi_abi.so, but there are a few issues related to datatype handles and a few failing tests involving MPI callbacks (errhandlers and attributes). I need to investigate further.

@hppritcha

Copy link
Copy Markdown
Member Author

Thanks for checking this stuff out in its early state @dalcinl. some of the above functions have been implemented but are sitting in various states in PRs. some of these should be defined - like MPI_Wtick and the aint related functions so i'll see what's going on. As for the fromint/toint that seems like an excellent project for a AI LLM. I'll see what can be done there.

We still need to add some plumbing in the ompi internals for callbacks. that will come in as part of this PR.

The NERSC folks would really like ABI working for Doudna so to the extent there's a "plan" it would be nice to get this working sooner than later.

@dalcinl

dalcinl commented Sep 4, 2025

Copy link
Copy Markdown
Contributor

Minor nit: Do you really need/want to install the abi.h header? IMHO, it is an implementation detail.

@dalcinl

dalcinl commented Sep 4, 2025

Copy link
Copy Markdown
Contributor

some of these should be defined - like MPI_Wtick and the aint related functions so i'll see what's going on.

  • MPI_Wtick: In ompi/mpi/c/, looks like you have to rename wtick.c -> wtick.c.in and update prototipes as needed to allow the code generation layer do its magic.
  • MPI_Pcontrol: Same thing as above.
  • MPI_Aint_add/diff: The OMPI ABI implements them as macros. There are no corresponding *.c.in files. Not sure what's the proper fix. Anyway, maybe the OMPI libmpi.so should export the Aint_add/diff routines anyway?

jtronge and others added 13 commits August 2, 2026 09:12
Two external MPI libraries are now created: libmpi.la and libmpi_abi.la.
Backend code that was originally in libmpi.la has been extracted into
libopen_mpi.la, which is installed and linked into both front-end
libraries.  MCA components and the tests that reference internal OMPI
symbols link libopen_mpi explicitly, since the linker does not always
pull it in transitively.

Parts of the Open MPI C interface are now generated for both the Open
MPI ABI and the MPI standard ABI from the same source templates,
including new bigcount interfaces.  The generation is part of the
bindings framework used for Big Count (ompi/mpi/bindings/).

To compile standard ABI code, there is a new mpicc_abi compiler wrapper,
and pkg-config files (ompi-abi.pc, ompi-abi-c.pc, ompi-abi-cxx.pc) are
installed for building against the standard ABI without the wrapper.

libmpi_abi carries its own SO version, set by convention across MPI
implementations (the MPI standard ABI intentionally does not mandate an
SONAME), so that an application can switch between implementations of a
given ABI version without recompiling.

Signed-off-by: Jake Tronge <jtronge@lanl.gov>
Signed-off-by: Howard Pritchard <howardp@lanl.gov>
Co-authored-by: Howard Pritchard <howardp@lanl.gov>
Signed-off-by: Jeff Squyres <jeff@squyres.com>
This mod switches from using "synthetic" defined values and handles to
the ones specified in the MPI 5.1 standard, and bumps the MPI standard
version reported by Open MPI to 5.0.

The python infrastructure included here generates a "canonical" MPI
standard ABI compatible mpi.h using two json files:

1) mpi-standard-abi.json
2) mpi-standard-apis.json

2 is generated as part of building the MPI standard.  We import that
into our project for use in generating both the mpi.h as well as
interface definitions in the man pages.

1 is generated using a separate script that processes the tables in
Appendix A of the standard.  Ideally this script will be merged into the
MPI standard code base at some point.  This script is currently at
https://github.com/Joe-Downs/mpi-standard/tree/pr/handle-constant-tool/const-tool .
It is used to generate the portion of mpi.h where defined values and
handles are specified.

This commit also enables generation of the abi interfaces and header
files by default.

Signed-off-by: Joseph Downs <joe.downs@lanl.gov>
Co-authored-by: Howard Pritchard <howardp@lanl.gov>
Signed-off-by: Jeff Squyres <jeff@squyres.com>
Fill in the parts of the C API surface that the initial standard-ABI
work left out:

* Handle converter functions.  Generate MPI_<Class>_toint() and
  MPI_<Class>_fromint() for every handle type, in both Open MPI ABI and
  standard ABI variants, with man pages for each.  Support machinery for
  converting handles that cross the ABI boundary inside callbacks is
  added to the errhandler, attribute, and op back-ends (a datatype
  converter function pointer on ompi_op_t, an errhandler converter
  typedef, and attribute-keyval ABI support).

* MPI_T.  Convert the MPI_T tool interface sources to generated
  templates so that libmpi_abi gets standard-ABI MPI_T bindings
  (mpi/tool/libmpi_mpit_abi.la).

* MPI_ERR_ABI.  Add the new error class mandated by the ABI chapter,
  including its Fortran value and man page documentation.

* Add the MPI_ABI_VERSION macro to the Open MPI ABI mpi.h.  See the
  discussion at mpi-forum/mpi-issues#1098

* Map optional Fortran COMPLEX datatypes without a Fortran counterpart
  to OMPI_DATATYPE_MPI_UNAVAILABLE in the internal datatype tables.

Fix MPI_Type_size_c to return the full size for big-count datatypes:
the type_size.c.in template was unconditionally casting the datatype
size to (int) regardless of whether it was generating MPI_Type_size()
or MPI_Type_size_c(), truncating sizes larger than INT_MAX.

Also fix several correctness bugs found while reviewing this work:

* MPI_Request_toint raised MPI_ERR_FILE (a copy-paste from the file
  converter) instead of MPI_ERR_REQUEST for a NULL request.

* Four consecutive entries in the c_header.py DEPRECATED_FUNCTIONS list
  were missing commas, so Python implicit string concatenation fused
  them into one token and MPI_DUP_FN, MPI_NULL_COPY_FN,
  MPI_NULL_DELETE_FN, and MPI_Attr_delete were emitted uncommented into
  the generated standard-ABI mpi.h.

* For DATATYPE_ARRAY_ASYNC and NEIGHBOR_DATATYPE_ARRAY_ASYNC bindings,
  the converted-datatype temporary array was only freed (or handed to
  the request) inside the "request still in flight" guard; on every
  other outcome the array was leaked.

* On MPI_Isendrecv_replace's temporary-buffer allocation-failure path,
  rc was left at MPI_SUCCESS, so the call reported success while
  *request was never assigned.

* Remove a dead "free(helper)" statement placed after the return in the
  Win delete-attribute wrapper; the helper is already freed by
  ompi_attribute_keyval_destruct(), so executing the statement would be
  a double free.

Signed-off-by: Howard Pritchard <howardp@lanl.gov>
Co-authored-by: Lisandro Dalcin <dalcinl@gmail.com>
Co-authored-by: Jeff Squyres <jeff@squyres.com>
Signed-off-by: Jeff Squyres <jeff@squyres.com>
Implement the MPI_Abi_* function family from the MPI-5 ABI chapter --
MPI_Abi_get_version, MPI_Abi_get_info, MPI_Abi_get_fortran_info,
MPI_Abi_get_fortran_booleans, MPI_Abi_set_fortran_booleans, and
MPI_Abi_set_fortran_info -- with C bindings for both libraries, mpif.h
and use-mpi-f08 Fortran bindings, man pages, and a make-check unit test
for the Fortran-boolean back-end (test/class/ompi_abi_fortran).

The default Fortran logical size requires special handling in
MPI_Abi_get_fortran_booleans, and the .FALSE. value is now probed
coherently with the .TRUE. value: the configure cache-invalidation
guard unconditionally unset ompi_cv_fortran_false_value whenever it was
"0" -- but 0 is the valid Fortran .FALSE. value on every compiler.
Since the .FALSE. value is computed in an AC_CACHE_CHECK keyed on the
.TRUE. cache variable, a cached reconfigure left
OMPI_FORTRAN_VALUE_FALSE defined to an empty token.  Invalidate both
cache variables together so they stay coherent.

The ompi_abi_get/set_fortran_booleans back-end validates logical_size
symmetrically in all builds: a logical_size that is not a power of two
returns MPI_ERR_ARG, as the man pages document.  The user_logicals[]
index is computed by scanning a well-defined bit range with
opal_hibit() and bounds-checking the result before it is used, and the
128-bit local declarations use the correct HAVE_INT128_T macro.

The man pages document the two constraints the implementation
enforces -- logical_size must be a power of two (MPI_ERR_ARG
otherwise), and MPI_Abi_set_fortran_booleans may be called at most once
(MPI_ERR_ABI on a subsequent call) -- and use the correct parameter
names and directions for each binding.

Signed-off-by: Howard Pritchard <howardp@lanl.gov>
Co-authored-by: Jeff Squyres <jeff@squyres.com>
Signed-off-by: Jeff Squyres <jeff@squyres.com>
libopen_mpi is the lower layer, and libmpi / libmpi_abi are the thin
front-ends above it.  libopen_mpi must therefore never reference the
public MPI_* / PMPI_* / MPIX_* / PMPIX_* entry points or the OMPI_*
extensions, all of which are defined in the layer above; doing so
creates a circular library dependency.

Remove the remaining upward references from back-end code:

* Add ompi_wtime(), an internal, profiling-neutral equivalent of
  MPI_Wtime() that uses the same time origin, and call it from the
  back-end and component code (libnbc, ompio, fcoll, fault-tolerance
  timing loops) that previously called MPI_Wtime()/PMPI_Wtime().

* Register MPI extension initialization via a callback function pointer
  handed down from the bindings layer instead of calling up into
  ompi/mpiext from ompi_instance.

* Drop dead MPI_* usage from the comm_method hook.

Then enforce the rule:

* config/ompi_setup_layering_checks.m4 probes for a linker flag that
  makes an unresolved symbol in a shared library a hard error and adds
  it to libopen_mpi's LDFLAGS, so a layering violation fails at link
  time on platforms whose linker supports it.  The --no-undefined
  probe is skipped on FreeBSD: there the environ global is provided by
  the executable's C runtime startup objects (crt1.o/Scrt1.o) rather
  than libc.so, so shared libraries legitimately reference an
  undefined environ and --no-undefined would break the build.  FreeBSD
  relies on the nm-based audit below instead (as macOS already does).

* config/ompi_check_layering.sh is the authoritative nm-based audit,
  run from an all-local hook after libopen_mpi is built.  It also
  covers static builds -- which have no link step at all -- and still
  fires if someone "fixes" a link error by putting libmpi on
  libopen_mpi's link line, which is precisely the regression being
  guarded against.

Signed-off-by: Howard Pritchard <howardp@lanl.gov>
Co-authored-by: Jeff Squyres <jeff@squyres.com>
Signed-off-by: Jeff Squyres <jeff@squyres.com>
Open MPI builds two C libraries from the same sources: the traditional
library (libmpi, MPI_MAX_OBJECT_NAME == OPAL_MAX_OBJECT_NAME == 64) and
the standard-ABI library (libmpi_abi, MPI_MAX_OBJECT_NAME == 128, the
value mandated by the MPI Forum ABI).  The internal object-name storage
for communicators, datatypes, and windows was sized to the traditional
64-byte limit, which is too small for the standard-ABI build and led to
two distinct defects:

  1. Heap overflow.  In the standard-ABI binding, the generated
     MPI_Type_set_name bounded its memset()/copy with the
     @MPI_MAX_OBJECT_NAME@ template token, which expands to 128, while
     ompi_datatype_t.name[] was char[64].  Every call wrote 128 bytes
     into a 64-byte field and corrupted the adjacent object, surfacing
     later as a "free(): invalid pointer" abort in MPI_Type_free.

  2. Lost conformance.  Even with a safe bound, a 64-byte field cannot
     hold the full-length names that a standard-ABI application is
     entitled to set (up to 128 bytes), so such names would be silently
     truncated below what the Forum ABI promises.

Resolve both by storing object names at the ABI maximum everywhere
internally, while preserving the long-standing 64-byte limit on the
traditional Open MPI entry points:

  * Introduce OMPI_MPI_MAX_OBJECT_NAME_ABI.  It is extracted at
    configure time from docs/mpi-standard-abi.json -- the same JSON that
    the C binding generator already consumes to emit
    MPI_MAX_OBJECT_NAME_ABI_INTERNAL -- so the value has a single source
    of truth.  The new config/ompi_get_forum_abi_values.m4 macro
    (OMPI_GET_FORUM_ABI_VALUES) reads it with the configure-selected
    Python (AC_REQUIRE([AM_PATH_PYTHON])), using .get() chaining so the
    one-liner contains no square brackets for m4 to consume, and
    AC_DEFINEs it into opal_config.h.

  * Size ompi_datatype_t.name[], ompi_win_t.w_name[], and the
    communicator c_name allocation to OMPI_MPI_MAX_OBJECT_NAME_ABI so
    the storage can always hold a full-length ABI name regardless of
    which library is being built.

  * Use OMPI_MPI_MAX_OBJECT_NAME_ABI for all internal name writes --
    automatic communicator naming, datatype duplication/creation, and
    the FT/DPM bookkeeping paths -- so nothing internal computes against
    the smaller bound.

  * Keep the entry points asymmetric and explicit: the traditional
    MPI_{Comm,Type,Win}_{set,get}_name continue to honor
    OPAL_MAX_OBJECT_NAME (64), while the standard-ABI entry points honor
    the Forum ABI maximum (128).  The set_name paths pre-truncate the
    caller's string into a correctly sized buffer before storing, so an
    over-long name is truncated safely (as the standard requires)
    instead of overrunning.

  * Update ompi/include/ompi/memchecker.h to annotate the full
    ABI-sized storage, and correct the communicator annotation, which
    now describes a heap pointer rather than an inline array.

Documentation: the man pages for all six MPI_{Comm,Type,Win}_{set,get}_
name functions now describe the Open MPI (64) versus Forum ABI (128)
limits using Sphinx substitutions (|ompi_max_object_name| and
|mpi_abi_max_object_name|) computed in docs/conf.py from opal_config.h
and the standard JSON, so the documented values always track the build.

The accompanying ABI-length round-trip tests are added in a separate
commit.

Signed-off-by: Jeff Squyres <jeff@squyres.com>
Co-authored-by: Jeff Squyres <jeff@squyres.com>
Open MPI now builds a standard-ABI C library (libmpi_abi) alongside its
traditional library, together with an mpicc_abi wrapper and a standard
ABI header.  That ABI layer is a distinct surface: it exposes the MPI
Forum ABI header and constants, advertises ABI include and link paths
through its wrapper, translates between standard ABI integer
handle/sentinel values and Open MPI's internal handle representation,
and forwards public MPI_* calls into the existing implementation.  None
of that was covered by Open MPI's general MPI correctness tests, which
exercise the traditional library and assume the implementation beneath
the ABI layer is already tested.

This adds a dedicated test suite under ompi/test/mpi-abi/ that checks the
ABI-facing surface from several directions -- metadata authority,
installed artifacts, symbol reachability, handle translation, complete
public-API call paths, callback conversion, and cross-implementation
compatibility -- without re-testing the underlying MPI algorithms.
Passing these tests does not prove every underlying MPI algorithm is
correct; it proves the ABI surface is consistent with the standard ABI
metadata derived from the MPI standard and can drive the already-tested
Open MPI implementation through the ABI path.

The runner is a Python program (mpi_abi_tests.py) split across sibling
_abi_*.py modules for discovery, manifest, probe generation, fast
checks, installed checks, cross-implementation checks, lookup tables,
and reporting.  Every module, template, and generated test case is
listed in EXTRA_DIST so VPATH and distribution-tarball builds can import
and run the suite from a read-only source tree; the runner is invoked
with "python -B" so it never writes __pycache__ next to the modules in
the source tree.  Probe bodies are generated from .cbody.in and
.prologue.in templates, and each logical probe is compiled into its own
executable because MPI process state is undefined after many runtime
failures.  Reports are written as JSON and text into mode-specific
build-tree directories.  The suite is wired into Automake so its checks
run in CI, and it remains Python 3.7 compatible like the rest of Open
MPI's Python tooling.  A new top-level requirements.txt unions the
per-area docs/requirements.txt and ompi/test/mpi-abi/requirements.txt files,
so installing that one file provides every Python package needed both
to build the documentation and man pages with Sphinx and to run all of
the MPI ABI checks.

Three make targets drive the suite, each with different prerequisites
and its own results directory:

* "make check" runs the fast metadata, manifest, and source checks (the
  runner's check-fast mode, reached through check-local).  These run
  entirely from the source and build trees and require neither an
  installed Open MPI nor mpicc_abi nor mpirun, so they are safe in any
  build environment and participate in the normal recursive make check.
  They compare the MPI-standard-derived ABI metadata under docs/ against
  the runner's manifest, classification rules, generated-source
  contracts, C header constants, and Fortran helper source contracts,
  catching drift between the ABI description and what the suite believes
  is implemented, skipped, or still uncovered before anything is
  installed or launched.  Output goes to check-results/.

* "make check-abi" runs the installed standard ABI checks against an
  installed Open MPI.  It uses the installed mpicc_abi wrapper, the
  installed standard ABI header, and installed mpirun, expected on PATH
  unless overridden by the OMPI_ABI_TEST_* environment or make
  variables.  It verifies that the wrapper advertises the ABI include
  and link paths, that the installed header declares exactly the
  implemented standard ABI C APIs with signatures matching the binding
  metadata (and does not declare non-ABI APIs), and that the ABI library
  exports the expected MPI_* / PMPI_* symbols.  It then exercises the
  ABI helper conversion functions (MPI_Comm_toint / _fromint,
  MPI_Type_toint, and their PMPI forms) by round-tripping predefined,
  null, and dynamic handles, status sentinels, error classes, keyval
  sentinels, and configured datatype constants; runs real MPI programs
  built with mpicc_abi and launched with mpirun that validate return
  codes, output handles, statuses, counts, data movement, object state,
  request completion, RMA, and MPI-IO results through the ABI entry
  points; isolates callback and retained-lifetime probes so one callback
  failure cannot poison other probes; and runs Fortran binding
  regression checks.  Open MPI does not yet provide an ABI-capable
  Fortran wrapper, so the Fortran checks deliberately record current
  behavior -- for example MPI_Abi_get_version reporting -1, -1 -- rather
  than claiming MPI-5 Fortran ABI coverage.  That absence is an
  intentional wait-and-see decision whose rationale is documented in
  docs/building-apps/mpi-forum-abi.rst.  Output goes to
  check-abi-results/.

* "make check-abi-mpich" runs the optional cross-implementation
  compatibility checks against MPICH, and is the most demanding target.
  It requires both an installed Open MPI with standard ABI support and
  an installed MPICH built with MPI Forum ABI support (for MPICH 5.0.x,
  configured with --enable-mpi-abi so it installs mpicc_abi, mpi_abi.h,
  and libmpi_abi).  MPICH's normal internal ABI is not the MPI Forum
  ABI, and neither implementation's plain mpicc is a substitute, so the
  runner discovers and classifies the MPI Forum ABI wrappers and
  launchers before selecting them; explicit MPICH_ABI_TEST_* and
  OMPI_ABI_TEST_* overrides are honored as operator intent and validated
  rather than silently falling back to another tool on PATH.  Because
  invoking this target is an explicit request for compatibility results,
  missing or invalid prerequisites are reported as failures, not skips.
  The target records both ABI directions -- compile with MPICH and run
  against Open MPI's ABI runtime, and compile with Open MPI's mpicc_abi
  and run against MPICH -- after first compiling and launching a
  one-rank MPI_Init / MPI_Finalize sanity program with each
  implementation's own wrapper, launcher, and ABI library so that broken
  local launchers are not misreported as ABI mismatches.  For each
  cross-direction executable it sanitizes the platform runtime library
  path (LD_LIBRARY_PATH on Linux; DYLD_LIBRARY_PATH plus rewriting the
  embedded ABI dylib load commands on macOS) so a binary compiled
  against one implementation cannot load a stale libmpi from the shell
  environment at run time, and it applies MPICH transport defaults
  (FI_PROVIDER=tcp with a non-loopback, non-tunnel IPv4 interface for
  ch4:ofi builds; UCX_TLS=self,sm for ch4:ucx builds) for local one- and
  two-rank jobs.  It treats libmpi_abi as the sole MPI Forum ABI library
  per MPI-5.0 section 21.2.1 and validates the PMPI alternate entry
  points required by section 16.2.1 as symbols and as calls through
  libmpi_abi.  Output goes to check-abi-mpich-results/.

The check-abi and check-abi-mpich targets are also defined at the top of
the tree, where they recurse into ompi/test/mpi-abi for the OMPI project and
otherwise print a SKIP message.  All three targets additionally skip
cleanly when configure did not find a usable Python.

Signed-off-by: Jeff Squyres <jeff@squyres.com>
Co-authored-by: Howard Pritchard <howardp@lanl.gov>
Open MPI v6.0.0 adds support for the MPI Forum (standard) ABI defined
by MPI-5.0 for C applications, alongside Open MPI's own long-standing
ABI.  The user-facing documentation did not yet describe this new,
separate binary interface: how to build against it, its current
limitations, or how it relates to Open MPI's existing ABI-compatibility
guarantees.  This adds and reorganizes that documentation.

High points of the documentation in this commit:

* A new "Building MPI applications using the MPI Forum ABI" page
  (docs/building-apps/mpi-forum-abi.rst) explaining what the MPI Forum
  ABI is, that it is new in v6.0.0 and distinct from the Open MPI ABI,
  and its limitations: the application must be dynamically linked, run
  with a matching launcher, and written in C.

* How to build and run standard-ABI C applications with the mpicc_abi
  wrapper and libmpi_abi -- the wrapper adds the standard ABI header
  directory ($prefix/include/standard_abi) and links libmpi_abi -- plus
  the --showme flags and the effect of configuring with
  --disable-standard-abi.

* How to check ABI support at run time with MPI_Abi_get_version, which
  reports version 1.0 from libmpi_abi but -1, -1 from the normal libmpi,
  because the normal library uses Open MPI's own ABI rather than the
  standard ABI.

* A clean separation of the two ABIs across the existing pages: the
  abi-compatibility.rst page is rescoped to the Open MPI ABI and
  cross-references the new page; version-numbering.rst distinguishes the
  Open MPI ABI from the MPI Forum ABI and moves its release-series ABI
  details into abi-compatibility.rst; and both pages state that the two
  ABIs are not interchangeable.

* A "danger" warning that object files or libraries built against
  different MPI ABIs must not be mixed in one executable, with the
  concrete unsupported mpicc_abi / mpicc / mpifort combinations and the
  rule to use one ABI consistently for every MPI-using object.

* The intentional decision not to ship a Fortran standard ABI yet (no
  mpifort_abi wrapper, no libmpifort_abi): a documented wait-and-see
  approach taken while no other implementation supports the Fortran ABI
  and the MPI Forum is still converging on it.

* Reference documentation for the Open MPI MPI_Abi_* query/helper
  routines: man pages for MPI_Abi_get_version, MPI_Abi_get_info,
  MPI_Abi_{get,set}_fortran_booleans, and MPI_Abi_{get,set}_fortran_info
  describing their Open MPI behavior; the mpicc_abi wrapper in the
  wrapper-compiler man page; and the standard-ABI configure option in
  the configure CLI options.

* Release-notes and v6.0.x changelog entries announcing MPI-5.0
  standard ABI support for C applications and noting the absence of a
  Fortran ABI wrapper.

Signed-off-by: Jeff Squyres <jeff@squyres.com>
Add CI coverage for the new MPI Standard ABI library:

  * ompi_mpi4py.yaml: also run the mpi4py test suite against
    libmpi_abi, not just the regular libmpi.
  * ompi-pr-builds.yaml: run the MPI Standard ABI test suite
    (make check-abi) as part of the ompi-pr-builds workflow.

ompi/test/mpi-abi: XFAIL the datarep cross probe against the MPICH runtime

MPICH does not implement user-defined datarep conversions.  Its
MPI_Register_datarep() fails with

  MPI_REGISTER_DATAREP(3042): Read and Write datarep conversions are
  currently not supported by MPI-IO

and reports that as error class MPI_ERR_CONVERSION rather than as
MPI_ERR_UNSUPPORTED_DATAREP or MPI_ERR_UNSUPPORTED_OPERATION.  The
callback_datarep probe only recognizes the latter two as "unsupported",
so it exits 2 and check-abi-mpich records a hard failure.

This is not a cross-ABI problem: a program compiled with MPICH's own
mpicc_abi and run under MPICH fails in exactly the same way.  The failure
is a limitation of the MPICH runtime, not of Open MPI or of the MPI Forum
ABI.

Reported upstream as pmodels/mpich#7873.  If
MPICH returns MPI_ERR_UNSUPPORTED_DATAREP, the probe takes its existing
77 skip path and this XFAIL should be removed.

Add XFAIL as a first-class result -- distinct from SKIP, which means the
probe declined to run, and from FAIL, which means something is wrong with
us -- and mark this probe as an expected failure when MPICH provides the
runtime.  Keying it on the run implementation (rather than on the probe
alone) means it can only ever excuse a MPICH-runtime limitation; a real
regression on the Open MPI side still fails.

Note that the key is the probe's exit code 2, which means "registration
failed with something other than the two unsupported classes".  That is
the narrowest key the probe's exit codes offer, but it is broader than
the upstream bug: it would also absorb a future genuine MPICH datarep
registration failure.

XFAIL is reported in its own colour and with its own reason code, so the
known limitation stays visible instead of vanishing into the skip count.

Signed-off-by: Jeff Squyres <jeff@squyres.com>
The MPI Forum ABI requires the public MPI_* symbols to be weak
definitions.  When an application is linked against an implementation
whose libmpi_abi exports MPI_* weak -- as MPICH's does -- the linker
records a weak-def (coalesced) bind, and (at least on macOS) the loader
will only satisfy such a bind from another weak definition; a strong one
is rejected outright:

  dyld: Symbol not found: _MPI_Send
        Expected as weak-def export from some loaded dylib

Open MPI's libmpi_abi exported MPI_* strong on macOS, so an application
compiled with MPICH's mpicc_abi could not run against Open MPI's
libmpi_abi at all -- precisely the portability the standardized ABI
exists to provide, and in the direction that matters most for adoption.

On Linux this already worked by accident: where weak aliases are
available the MPI_* alias is itself a weak definition.  The problem is
confined to the platforms where weak aliases are not available and the
bindings are therefore compiled a second time to produce MPI_* -- that
separately-compiled copy was strong.  Mark it weak.

This touches libmpi_abi only; libmpi's symbols are unchanged.  The
hand-written files shared with libmpi (pcontrol.c and the MPI_T
bindings) are guarded on OMPI_NO_MPI_PROTOTYPES, which is defined only
for the libmpi_abi compiles.

With this, check-abi-mpich's MPICH-compiled / Open-MPI-run direction goes
from failing to 61 PASS / 0 FAIL on macOS.

ABI: make the libmpi MPI_* symbols weak too

Commit "ABI: make the libmpi_abi MPI_* symbols weak" did this for
libmpi_abi, where the MPI Forum ABI requires it.  Do the same for libmpi,
so that both libraries present the public MPI_* symbols the same way, and
so that Open MPI matches the scheme MPICH now uses: strong PMPI_*, weak
MPI_*.

Weak MPI_* is also what lets a PMPI profiling library supply a strong
MPI_* that overrides ours while still reaching the back end through
PMPI_*.  Where weak aliases are available (Linux) the MPI_* alias was
already a weak definition, so this is a no-op there; where they are not
(macOS) the bindings are compiled a second time to produce MPI_*, and it
is that separately-compiled copy that was strong.  Mark it weak.

This keeps the macOS compile-everything-twice scheme intact; removing it
is a separate step.

The four MPI_*_F90 accessors in ompi/mpi/fortran/base (MPI_WTIME_F90 and
friends) are deliberately left strong: they have no PMPI_* counterpart,
are not part of the profiling interface, and are not MPI Forum ABI
symbols.

After this, on macOS libmpi exports 698/698 of its C MPI_* weak and all
698 PMPI_* strong, and libmpi_abi is unchanged at 657/657 weak.

Signed-off-by: Jeff Squyres <jeff@squyres.com>
Where weak aliases are unavailable, Open MPI compiled every C binding a
second time, with OMPI_BUILD_MPI_PROFILING=0, purely to produce the public
MPI_* symbols; the first compile produced PMPI_*.  That is what the
libmpi_c_noprofile, libmpi_c_abi_noprofile, libmpi_mpit_noprofile and
libmpi_mpit_abi_noprofile convenience libraries were for.

Now that MPI_* are weak symbols rather than weak aliases, the second
compile is unnecessary: the profiling compile can emit both.  Emit a weak
MPI_* definition that forwards to the strong PMPI_* one, and delete the
four *_noprofile libraries.  This is the scheme MPICH uses -- on arm64 the
resulting MPI_Send is a weak symbol whose entire body is a single branch
instruction to PMPI_Send.

Note that a weak *alias* is not an option here: Mach-O cannot express one.
There is no way to mark a ".set" alias as a weak definition (verified
against ld64: .weak_definition, .weak_reference and #pragma weak all leave
the alias strong), which is precisely why the second compile existed.  The
forwarding definition is guarded on !OPAL_HAVE_WEAK_ALIASES, since where
aliases *are* available (ELF) the alias already is the weak MPI_*
definition and a second definition would be a duplicate symbol.

MPI_Pcontrol is variadic, so its arguments cannot be forwarded.  Open MPI's
implementation ignores them and returns MPI_SUCCESS, and MPICH's weak
MPI_Pcontrol does the same, so the wrapper does that directly.

Add __opal_attribute_weak__ for the plain weak attribute; OPAL previously
had only __opal_attribute_weak_alias__.

This covers the C bindings (generated, hand-written, and MPI_T).  The
Fortran mpif-h bindings still compile twice: they are 443 hand-written
files, each exposing four name-mangled entry points, so converting them
means roughly 1,772 forwarding definitions and wants a generator rather
than a hand edit.  BUILD_MPI_BINDINGS_LAYER therefore survives, now used
only by Fortran.

Verified on macOS (single compile; MPI_* 698/698 weak in libmpi, 657/657 in
libmpi_abi) and Linux (unchanged; the wrapper is correctly suppressed):
examples, make check, make check-abi, make check-abi-mpich, and PMPI
interposition all pass.

ABI: compile the mpif.h bindings once, not twice

This finishes what the previous commit started for the C bindings.

Where weak aliases are unavailable, every mpif.h binding was compiled a
second time, with OMPI_BUILD_MPI_PROFILING=0, purely to emit the public
MPI_* Fortran entry points; the profiling compile emitted PMPI_* and
renamed the implementation to pompi_x_f.  The bodies were compiled twice.

Emit the MPI_* entry point from the profiling compile instead, as a weak
definition forwarding to the same implementation the strong PMPI_* entry
point calls, and delete the second compile.  The implementation keeps its
ompi_x_f name and is no longer renamed.

Add OMPI_GENERATE_WEAK_F77_BINDINGS alongside OMPI_GENERATE_F77_BINDINGS.
Like its sibling it emits a single entry point, in whichever name-mangling
scheme the Fortran compiler uses; it just marks it weak.  Twelve bindings
(MPI_Aint_add and friends) spell their entry points out longhand because
the macro does not cover non-void return types; those are marked weak
directly.

With only one compile left, the profile/ subdirectory has no reason to
exist: its entire apparatus -- the 441 p*.c symlinks, the duplicate
linked_files list, and the symlink rule -- existed only to compile the
same basenames a second time with different flags.  Compile the real
sources directly in mpif-h/ (with OMPI_BUILD_MPI_PROFILING=1) instead,
and delete profile/ entirely.  The sources move back into the library's
_SOURCES list, which also makes them distributed again -- the previous
arrangement compiled them only as nodist_ symlinks, so "make dist"
silently shipped no mpif.h binding sources at all.  psizeof_f.f90
generation moves up alongside sizeof_f.f90, and the
BUILD_PMPI_FORTRAN_MPIFH_BINDINGS_LAYER conditional (now identical to
OMPI_BUILD_FORTRAN_MPIFH_BINDINGS) is gone.

register_datarep_f.c needed a fix: its OBJ_CLASS_INSTANCE was guarded on
"!OMPI_BUILD_MPI_PROFILING || OPAL_HAVE_WEAK_ALIASES", i.e. it was emitted
by whichever compile was not the profiling one.  With only one compile left
that guard emitted it nowhere, and the Fortran bindings failed at run time
with a missing ompi_intercept_extra_state_t_class.  The bindings are now
compiled exactly once, so the guard is gone.

With C and Fortran both converted, OMPI_PROFILING_COMPILE_SEPARATELY is
always 0 and its three AM_CONDITIONALs (BUILD_MPI_BINDINGS_LAYER,
COMPILE_PROFILING_SEPARATELY, BUILD_MPI_FORTRAN_MPIFH_BINDINGS_LAYER) are
gone.  A compiler with neither weak symbols nor weak aliases can no longer
build the profiling interface, so configure now says so explicitly rather
than silently producing a broken library.

ompi/test/mpi-abi: the mpif.h ABI helper check looks for each helper's
source in mpif-h/Makefile.am, which once again lists every binding
source directly.

Verified on macOS (mpif.h MPI_* now weak: 457 weak entry points, 613
strong PMPI_*) and Linux (unchanged; the weak wrapper is correctly
suppressed where weak aliases exist): fresh VPATH builds on both, the
Fortran examples under mpirun -np 2, and a "make dist" tarball that
ships exactly the 441 binding sources with no profile/ remnants.

ftmpi: compile the MPIX_* bindings once; remove the profile/ dirs

These were the last two profile/ subdirectories in the tree.

ompi/mpiext/ftmpi/c compiled every binding twice: once in c/ with
OMPI_BUILD_MPI_PROFILING=0 to emit a strong MPIX_* symbol, and once in
c/profile/ (via p*.c symlinks) with OMPI_BUILD_MPI_PROFILING=1 to emit
the strong PMPIX_* symbol plus a weak MPIX_* alias.  Both copies were
always built and both linked into libmpiext_ftmpi_c.la, so the strong
MPIX_* from the non-profiling compile always won over the weak alias
-- which also meant PMPI-based tools could never actually intercept
the MPIX_* calls.

Convert to the same single-compile scheme the generated C bindings
use: compile once with OMPI_BUILD_MPI_PROFILING=1, emitting the strong
PMPIX_* entry point and a weak MPIX_* -- via a weak alias where the
platform has them, and via a weak forwarding definition where it does
not (e.g., macOS).  Interception now works for MPIX_* too.

ompi/mpiext/ftmpi/use-mpi-f08/profile/ held hand-written PMPIX_*_f08
wrapper copies of the MPIX_*_f08 wrappers.  Fortran has no weak
symbols, so both wrappers must be spelled out; fold each PMPIX_*_f08
subroutine into the same file as its MPIX_*_f08 sibling and delete the
subdirectory.

Also remove the now-stale profile/ .gitignore entries for the ompi
tree (ompi/mpi/c/profile, ompi/mpi/tool/profile, and the
use-mpi-f08/profile leftovers referred to directories that no longer
exist; the oshmem entries remain because that build still creates its
profile/ directories at build time).

Signed-off-by: Jeff Squyres <jeff@squyres.com>
The nineteen MPI_T tool sources whose C prototypes are ABI-neutral
(category_*, *_get_index, *_get_num, finalize, init_thread,
source_get_timestamp) were compiled verbatim into libmpi_abi.  Their
prototypes need no translation, but their values do: they returned
OMPI-valued MPI_T error classes (e.g. MPI_T_ERR_NOT_INITIALIZED is 55
internally but 1003 in the MPI Forum ABI), and MPI_T_init_thread
passed thread levels through unconverted in both directions (OMPI
uses 0..3 where the ABI defines 1024/2048/4096).  An ABI application
comparing returns against the standard constants, or reading
"provided", got wrong values.

Convert them to .c.in templates like the rest of ompi/mpi/tool: the
bodies are unchanged, the PROTOTYPE lines use the existing ERROR_CLASS,
TS_LEVEL/TS_LEVEL_OUT, PVAR_CLASS, and ELEMENT_COUNT types, and the
generated MPI Forum ABI wrappers now perform the error-class, thread-
level, and pvar-class conversions.  This retires the
interface_profile_sources_np list entirely (and, while touching the
list plumbing, replaces an AI-tooling-addressed comment with a normal
explanation of why the generated list is spelled out for BSD make).

Verified on macOS and Linux: full build, make check, make check-abi
(85 PASS / 0 FAIL / 3 SKIP), and make check-abi-mpich (154/0/3).

Signed-off-by: Jeff Squyres <jeff@squyres.com>
In Open MPI v6.0, 138 predefined MPI symbols (e.g., ompi_mpi_comm_world,
ompi_mpi_comm_self, predefined datatypes, and operators) were relocated
from libmpi to the new libopen_mpi library to support the MPI standard
ABI implementation. This change broke backward compatibility on Darwin with
applications compiled against Open MPI v5.x that directly reference
these symbols through libmpi.

This commit implements platform-specific symbol re-export mechanisms to
restore backward compatibility:

- macOS: Uses -Wl,-reexport-lopen_mpi to create LC_REEXPORT_DYLIB
  dependency, making libopen_mpi symbols available through libmpi
  at runtime via the dynamic linker.

configure.ac now detects the platform and linker capabilities, setting
Automake conditionals (OMPI_REEXPORT_MACOS and
OMPI_REEXPORT_VERSION_SCRIPT) that control the linking strategy in
ompi/Makefile.am.  The -reexport_library probe runs only on Darwin.

A new test under ompi/test/reexport/ verifies that symbols resolve
correctly through libmpi at runtime; it validates the exact shared
library produced by the current build (passed through the environment
by the test harness) instead of hard-coding sonames.

This allows v5.x-compiled applications to run with v6.0 libraries
without recompilation, while maintaining the architectural benefits
of the libopen_mpi separation.

On macOS, libopen_mpi is deliberately NOT in libmpi's LIBADD: the
-reexport-lopen_mpi flag both links and re-exports it, and if the
library also appeared in LIBADD, ld64 would record an ordinary
LC_LOAD_DYLIB reference instead of LC_REEXPORT_DYLIB.  Because that
flag is invisible to libtool, libmpi.la must list libopen-pal.la
explicitly; otherwise libtool omits opal/.libs from the runtime search
path of uninstalled programs that link only libmpi.la, and "make
check" fails before "make install" (which also breaks the inner check
run of "make distcheck").

Drop the symbol re-export machinery on ELF platforms

The v5.x-compatibility re-export of libopen_mpi's predefined MPI
handle symbols (ompi_mpi_comm_world and friends) through libmpi is
only needed on macOS, where the Mach-O two-level namespace records
which library each symbol was found in at link time.  On ELF
platforms the global symbol namespace is flat: at run time the
dynamic linker resolves a v5.x binary's references from any loaded
library, and libmpi's DT_NEEDED dependency on libopen_mpi guarantees
libopen_mpi is loaded.  No re-export mechanism is needed at all, and
ompi/test/reexport's symbol-resolution test passes unchanged with the
version script removed.

The --version-script implementation was also actively harmful: it
attached an OMPI_6.0 version tag to every symbol libmpi exports.
Open MPI has never shipped versioned symbols, so this broke consumers
that parse symbol tables (mpi4py's test_mpiapi.py fails on the
"MPI_Send@OMPI_6.0" references of anything compiled against the
library), and it meant that libraries built against a versioned
libmpi could not link against an unversioned rebuild.  Introducing
symbol versioning would be an ABI policy decision in its own right
and should not ride in as a side effect of a compatibility mechanism.

Remove the version-script configure probe, the
OMPI_REEXPORT_VERSION_SCRIPT conditional, and the
libmpi_symbol_exports.ver script (whose curated symbol list had
already been reduced to an export-everything placeholder).  The macOS
-Wl,-reexport-lopen_mpi mechanism is unchanged.

Signed-off-by: Howard Pritchard <howardp@lanl.gov>
Signed-off-by: Jeff Squyres <jeff@squyres.com>
@dalcinl

dalcinl commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

@jsquyres Given that you guys are dealing with macOS and are not afraid of adding platform-specific stuff, there is a thing I would like to point out.

As a consequence of using libtool versioning with 1:0:0, the ABI library will get a current_version and compatibility_version of 2.0.0. You can double-check running otool -L libmpi_abi.1.dylib. IMHO, the ABI library should be built with -Wl,-compatibility_version,0 and -Wl,-current_version,0 , see here what I did in the ABI stubs in the three build systems I support:

https://github.com/mpi-forum/mpi-abi-stubs/blob/main/Makefile#L48C13-L48C41
https://github.com/mpi-forum/mpi-abi-stubs/blob/main/meson.build#L86
https://github.com/mpi-forum/mpi-abi-stubs/blob/main/CMakeLists.txt#L84

Using 0 (zero) for the version numbers basically tells the dynamic linker to not perform any version compatibility check when the library is loaded at runtime. I think that is what we want, major version compatibility is enforced via SONAME, just like in Linux.

Recent versions of macOS (circa 2020) removed the compatibility version checks for shared libraries, the dynamic linker simply ignores these version numbers. Still, it may be good to set these current/compatibility versions to 0.0.0 for cleanliness, such that these numbers to not propagate to other executables and libraries linked against libmpi_abi.1.dylib.

@jsquyres

jsquyres commented Aug 2, 2026

Copy link
Copy Markdown
Member

@dalcinl Gotcha. I understand what you're asking for, and why.

The problem is that Libtool unconditionally emits its own computed -compatibility_version 2 -current_version 2.0, so the only way to get zeros without patching Libtool is to append -Wl,-compatibility_version,0 -Wl,-current_version,0 and rely on the linker resolving the repeated option in our favor. I tested this on current Xcode (ld-prime): it works — the dylib comes out 0.0.0/0.0.0, no diagnostics, and client binaries record compatibility 0. But that "last one wins" behavior is not documented anywhere in the ld man page (the only duplicate-related text there is about duplicate libraries, not repeated options). In classic ld64, it apparently happened to fall out of the option parser overwriting the stored value as it scanned left to right. But Claude tells me ld-prime is closed source and just happens to behave the same today.

So we'd be building on undocumented linker behavior to fix something that, as you note yourself, has been cosmetic since dyld stopped enforcing these checks circa macOS 11. If a future ld starts hard-erroring on the repeated option (as it already does for some others), every macOS build breaks for a hygiene win. That makes this feel a little shaky.

@dalcinl

dalcinl commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

If a future ld starts hard-erroring ... That makes this feel a little shaky.

I understand. However, I would still go for it. If things change in a different path (e.g. version checks being somehow reintroduced but Apple or third party tools), then we end up with published broken binaries.

Maybe a configure test can be run in advance to check whether the linker accepts the duplicated -xxx_version options with different values?

What about patching the generated <topdir>/libtool ? (not sure that one is always generated and used)

It is unfortunate that I'm not so good at autotools to experiment and provide a working patch.

@jsquyres

jsquyres commented Aug 3, 2026

Copy link
Copy Markdown
Member

We do carry a few Libtool patches, but I admit that I'm fairly wary about adding any more. Libtool even had a release recently (July 2026) -- we haven't tried to port our existing patches to it (I admit I'm a little scared).

@hppritcha

Copy link
Copy Markdown
Member Author

@dalcinl i think we need to pursue your points in a discussion about a MPI forum side document.

@dalcinl

dalcinl commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

@hppritcha I you could keep track of all the points I already forgot, I'm happy to help.

@hppritcha

Copy link
Copy Markdown
Member Author

I am thinking claude or other agent may be able to write a draft side document for MPI implementors to iterate on, the trick will be finding the various places in issues and PR comments that points of concern were raised.

@jsquyres

jsquyres commented Aug 6, 2026

Copy link
Copy Markdown
Member

To backport this PR to v6.0.x:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MPI ABI: Type MPI_2INTEGER to mapped correctly MPI-5.0 standard ABI (Chapter 20)