Skip to content

Regenerate the API against MPI 5.0, and add the large-count entry points - #990

Merged
eschnett merged 3 commits into
masterfrom
eschnett/mpi-5.0-bindings
Sep 23, 2026
Merged

eschnett merged 3 commits into
masterfrom
eschnett/mpi-5.0-bindings

Conversation

@eschnett

Copy link
Copy Markdown
Contributor

This is the first part of #989 (large count functions).

Update the API generator to handle MPI 4.0, 4.1, and 5.0. This is mainly a mechanical change.

The large-count function (e.g. MPI_Send_c) take a 64-bit count argument whereas the narrow functions (e.g. MPI_Send) only take a 32-bit count. On a 64-bit system, 64-bit counts are clearly desirable.

To handle older MPI libraries which do not provide the large-count functions, I decided to do this in the code generator: The large-count functions (MPI_Send_c) are always provided. If the MPI library provides them, they are called. If not – and that is known at precompile time – then the narrow function (MPI_Send) is called instead. This means that the high-level functions do not need to care about this distinction, they can always call the large-count functions. The type conversion for counts (from Int to either Int64 or Cint) are handled by @CCall, efficiently, without a run-time check. Using a count argument that is too large for the MPI library throws an error.

This PR only regenerates the bindings. Existing code should not see any change in behaviour yet.

src/api/generated_api.jl was last regenerated in 2022 from MPICH 4.0 headers, so the
bindings covered MPI 4.0. Regenerate from MPICH 5.0: 29 procedures added (the MPI 4.1
buffer-attach, MPI_Status_get_*/set_* and MPI_Request_get_status_* families,
MPI_Type_get_value_index, MPI_Get_hw_resource_info, MPI_Remove_error_*, and MPI 5.0's
MPI_Abi_*), 2 removed.

gen/ is reworked so this is reproducible. gen.sh followed whatever MPIPreferences the
surrounding environment resolved to -- which need not even be a supported source -- so
pin the choice before Pkg runs, since MPICH_jll's platform augmentation consults it at
resolve time; and drop a stale gen/Manifest.toml, which is not committed and so is not
what makes this reproducible, but whose reuse silently generates against the wrong MPI
version. Clang.jl drives the formatting of the whole output file and only runs on
Julia 1.10, so both are pinned. The instructions now live at the top of gen.sh, since
regenerating is a deliberate act rather than something CI should enforce.

@mpichk now always probes for the symbol, not just for the eight procedures that
happened to carry a minimum version, so the whole MPI 4.1/5.0 surface degrades to a
FeatureLevelError rather than an unresolved symbol. min_version is
Union{Nothing,VersionNumber}: procedures predating MPI 3.1 get none, since MPI.jl
requires MPI 3.0 anyway and there is no public machine-readable way to separate 1.0
through 3.1. The version table itself is derived from the MPI standard's own
machine-readable bindings rather than hand-listed; see gen/versions/README.md.

The 155 large-count MPI_*_c entry points become usable from any supported library:
where one does not provide them, each falls back to its narrow counterpart, so a
caller names a single entry point everywhere and an over-large count raises
InexactError instead of being truncated. That is all-or-nothing, because API.Count,
API.Displ and API.TypeDispl are a single choice for the whole package and an
implementation may ship only part of the set -- Intel MPI 2021.11 has MPI_Send_c but
not MPI_Type_size_c, and deciding per function let a Ref{MPI_Count} reach an entry
point wanting a Ptr{Cint}. MPI_Op_create_c and MPI_Register_datarep_c are excluded
from the fallback: their callback's len argument widens with the counts, so swapping
the creators would read it at the wrong width.

Nothing above src/api/ uses any of this yet; that follows separately.

Two fixes fall out. MPI_Aint_add and MPI_Aint_diff return an MPI_Aint, not an error
code, but were wrapped in @mpichk, so any nonzero result was thrown as an MPIError;
error checking is now derived from the ccall return type rather than a hand-kept list.
And the handle conversions are ignored rather than generated: they return a handle,
which @mpichk would likewise mistake for an errcode, and api.jl writes them by hand.

MPICH's docs have caught up since the comment in _doc_external was written, so point
the generated man-page links at the current release rather than v4.0, which has no
pages for anything added since.
Comment thread gen/src/generator.toml Outdated
Comment thread gen/src/generator.toml Outdated
Comment thread gen/versions/extract_versions.py
Comment thread src/MPI.jl Outdated
MPI_Pcontrol can be called, so make it callable. My ignorelist entry claimed Clang's
rendering was not usable from Julia, which was wrong twice over: Clang.jl does not
generate a wrapper for a variadic declaration at all, so the entry never did anything,
and the procedure is perfectly callable. It is the MPI C API's only variadic procedure,
so write it by hand in src/api/api.jl alongside the handle conversions. The arguments
after `level` are implementation-defined and are not exposed; passing none means the
call needs no variadic argument passing, so it goes through the ordinary `@mpichk`
path, which also keeps a profiler able to intercept it -- the whole point of this one.
test_featurelevel.jl now calls it, so every CI configuration exercises it.

Pin the generated man-page links. "latest" and "main" move under us, so links written
today silently rot. MPICH's tree is pinned to v5.0.1, the version the bindings are
generated from, which documents every procedure here. Open MPI is pinned to v5.0.x,
which documents MPI 4.0 and earlier but neither the large-count `_c` procedures nor
anything added in 4.1 -- so `_doc_external` now takes the introducing MPI version and
links to Open MPI only where it has a page. The generator passes that version through
from the same INTRODUCED table it already uses for the feature-level gating.

Checked a sample from each category -- pre-3.1, 4.0, 4.1+, and `_c` -- and every
resulting URL resolves.
Comment thread src/MPI.jl Outdated
CI showed the previous commit's MPI_Pcontrol crashing: Intel MPI 2019.9 and 2021.11
segfaulted, and all three 32-bit Windows jobs died with 0xC0000005. I had claimed that
passing no variadic argument made a plain non-variadic call safe. It does not.

On x86-64 System V a variadic callee reads `al` for the number of vector registers
used, which a non-variadic call never sets, so `va_start` spills against garbage. On
32-bit Windows a variadic function is `cdecl` even though the rest of MS-MPI is
`stdcall`, so `@mpicall`'s `stdcall` fixup corrupts the stack. Use `@ccall`'s `;` form,
which emits a genuinely variadic call, with the bare symbol name on Unix so that an
`LD_PRELOAD` profiler can still intercept it. Both crash modes are recorded in the
docstring, since the code looks needlessly elaborate without them.

Also from review:

The ignorelist comment claimed these were "not a C binding MPI.jl can call", which
described nothing -- `MPI_T_*`, `MPI_Session_*` and the Fortran 2008 status conversions
are all callable C functions. Say what the group actually is: parts of MPI that MPI.jl
does not wrap, plus the profiling and vendor-extension prefixes.

The two manual-page URL bases were module-level constants used by one function and
never at run time, since the docstrings are built during precompilation. They are
locals in `_doc_external` now.
@giordano
giordano added this pull request to stack #992 September 23, 2026 13:27
@eschnett
eschnett merged commit abbbe7b into master Sep 23, 2026
64 of 66 checks passed
@eschnett
eschnett deleted the eschnett/mpi-5.0-bindings branch September 23, 2026 16:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants