Skip to content

Commit

Permalink
binding/python: generate io abi bindings
Browse files Browse the repository at this point in the history
Because ROMIO is built on top of the rest of MPI, it is difficult to use
the abi wrappers because we need either -

a. convert from ABI into MPICH abi and then convert back to ABI before
   calling into ROMIO. This seems silly.

Or -

b. build ROMIO on MPICH abi. But in libmpi_abi.so, MPICH abi is not
   available.

Thus we need -

c. Switch ROMIO to call MPICH impl functions instead of `MPI/PMPI`
   functions. We'll need to build a layer in ROMIO switch between using
   `MPI/PMPI` or using `MPIR_Xxx_impl` depending on build options.
   This will result in ROMIO having full access to MPICH internals!
   But let's delay work until we have a convincing need.

Thus, this commit implements io abi bindings without the abi wrappers.
We implemented a separate path rather than making the existing code
more complex.
  • Loading branch information
hzhou committed Dec 6, 2024
1 parent 46ebe51 commit a4c17e9
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 6 deletions.
49 changes: 48 additions & 1 deletion maint/gen_abi.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def search(pat, str, flags=0):
def main():
load_mpi_abi_h("src/binding/abi/mpi_abi.h")
dump_mpi_abi_internal_h("src/binding/abi/mpi_abi_internal.h")
dump_io_abi_internal_h("src/binding/abi/io_abi_internal.h")
dump_romio_abi_internal_h("src/mpi/romio/include/romio_abi_internal.h")
dump_mpi_abi_util_c("src/binding/abi/mpi_abi_util.c")

Expand Down Expand Up @@ -101,7 +102,7 @@ def gen_mpi_abi_internal_h(out):
for line in output_lines:
print(line, file=Out)
print("", file=Out)

print("#endif /* MPI_ABI_INTERNAL_H_INCLUDED */", file=Out)

def dump_romio_abi_internal_h(romio_abi_internal_h):
Expand Down Expand Up @@ -181,6 +182,52 @@ def add_other(out):
print("#define ROMIO_ABI_INTERNAL_H_INCLUDED", file=Out)
print("", file=Out)

for line in output_lines:
print(line, file=Out)
print("", file=Out)

print("#endif /* ROMIO_ABI_INTERNAL_H_INCLUDED */", file=Out)

# similar to romio_abi_internal.h but for use in the mpich io binding
def dump_io_abi_internal_h(io_abi_internal_h):
def gen_io_abi_internal_h(out):
for line in G.abi_h_lines:
if RE.search(r'MPI_ABI_H_INCLUDED', line):
# skip the include guard, harmless
pass
elif RE.match(r'typedef struct.*\bMPI_File;\s*$', line):
out.append("typedef struct ADIOI_FileD *MPI_File;")
elif RE.match(r'(int|double|MPI_\w+) (P?MPI\w+)\((.*)\);', line):
# prototypes, rename param prefix, add MPICH_API_PUBLIC
(T, name, param) = RE.m.group(1,2,3)
if RE.match(r'P?MPI_(File_\w+|Register_datarep\w*)', name):
out.append("%s %s(%s) MPICH_API_PUBLIC;" % (T, name, param))
else:
out.append("%s %s(%s);" % (T, name, param))
else:
# replace param prefix
out.append(line.rstrip())

def add_mpich_visibility(out):
out.append("#if defined(HAVE_VISIBILITY)")
out.append("#define MPICH_API_PUBLIC __attribute__((visibility (\"default\")))")
out.append("#else")
out.append("#define MPICH_API_PUBLIC")
out.append("#endif")
out.append("")

# ----
output_lines = []
add_mpich_visibility(output_lines)
gen_io_abi_internal_h(output_lines)

print(" --> [%s]" % io_abi_internal_h)
with open(io_abi_internal_h, "w") as Out:
dump_copyright(Out)
print("#ifndef ROMIO_ABI_INTERNAL_H_INCLUDED", file=Out)
print("#define ROMIO_ABI_INTERNAL_H_INCLUDED", file=Out)
print("", file=Out)

for line in output_lines:
print(line, file=Out)
print("", file=Out)
Expand Down
16 changes: 16 additions & 0 deletions maint/gen_binding_c.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,10 +158,26 @@ def dump_io_funcs():

dump_out(c_dir + "/io.c")

def dump_io_funcs_abi():
G.out = []
G.out.append("#include \"mpichconf.h\"")
G.out.append("#include \"io_abi_internal.h\"")
G.out.append("#include \"mpir_io_impl.h\"")
G.out.append("#include <limits.h>")
G.out.append("")

for func in io_func_list:
dump_func_abi(func)

abi_file_path = abi_dir + "/io_abi.c"
G.check_write_path(abi_file_path)
dump_c_file(abi_file_path, G.out)

# ----
dump_c_binding()
dump_c_binding_abi()
dump_io_funcs()
dump_io_funcs_abi()

if 'output-mansrc' in G.opts:
f = c_dir + '/mansrc/' + 'poly_aliases.lst'
Expand Down
63 changes: 58 additions & 5 deletions maint/local_python/binding_c.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,22 +81,28 @@ def dump_romio_reference(name):

dump_profiling(func)

if 'polymorph' in func:
skip_wrappers = False
if func['dir'] == 'io' and '_is_abi' in func:
# The mpi-abi version of io bindings does not have access to MPICH internals (i.e. mpiimpl.h)
dump_function_io(func)
skip_wrappers = True
elif 'polymorph' in func:
# MPII_ function to support C/Fortran Polymorphism, eg MPI_Comm_get_attr
G.out.append("#ifndef MPICH_MPI_FROM_PMPI")
dump_function_internal(func, kind="polymorph")
G.out.append("#endif /* MPICH_MPI_FROM_PMPI */")
G.out.append("")

if 'polymorph' in func:
dump_function_internal(func, kind="call-polymorph")
elif 'replace' in func and 'body' not in func:
pass
else:
dump_function_internal(func, kind="normal")
G.out.append("")

if '_is_abi' in func:
if skip_wrappers:
pass
elif '_is_abi' in func:
dump_abi_wrappers(func, func['_is_large'])
else:
# Create the MPI and QMPI wrapper functions that will call the above, "real" version of the
Expand Down Expand Up @@ -215,15 +221,20 @@ def dump_mpir_io_impl_h(f):
print(l, file=Out)
print("#ifndef MPIR_IO_IMPL_H_INCLUDED", file=Out)
print("#define MPIR_IO_IMPL_H_INCLUDED", file=Out)

print("", file=Out)

# io_abi.c doesn't have access to MPICH internal
print("#ifdef BUILD_MPI_ABI", file=Out)
print("#define MPIR_ERR_RECOVERABLE 0", file=Out)
print("#define MPIR_ERR_FATAL 1", file=Out)
print("int MPIR_Err_create_code(int, int, const char[], int, int, const char[], const char[], ...);", file=Out)
print("int MPIR_Err_return_comm(void *, const char[], int);", file=Out)
print("#endif", file=Out)
print("", file=Out)

print("void MPIR_Ext_cs_enter(void);", file=Out)
print("void MPIR_Ext_cs_exit(void);", file=Out)
print("#ifndef HAVE_ROMIO", file=Out)
print("int MPIR_Err_return_comm(void *, const char[], int);", file=Out)
print("#define MPIO_Err_return_file(fh, errorcode) MPIR_Err_return_comm((void *)0, __func__, errorcode)", file=Out)
print("#else", file=Out)
print("MPI_Fint MPIR_File_c2f_impl(MPI_File fh);", file=Out)
Expand Down Expand Up @@ -845,6 +856,48 @@ def dump_copy_right():
G.out.append(" */")
G.out.append("")

def dump_function_io(func):
is_large = func['_is_large']
parameters = ""
for p in func['c_parameters']:
parameters = parameters + ", " + p['name']

func_decl = get_declare_function(func, is_large)

dump_line_with_break(func_decl)
G.out.append("{")
G.out.append("INDENT")
if "impl" in func and func['impl'] == "direct":
dump_function_direct(func)
else:
G.out.append("int mpi_errno = MPI_SUCCESS;")
if not '_skip_global_cs' in func:
G.out.append("MPIR_Ext_cs_enter();")
G.out.append("")
G.out.append("#ifndef HAVE_ROMIO")
if not '_is_abi' in func:
G.out.append("mpi_errno = MPIR_Err_create_code(MPI_SUCCESS, MPIR_ERR_RECOVERABLE, __func__, __LINE__, MPI_ERR_OTHER, \"**notimpl\", 0);")
else:
G.out.append("mpi_errno = MPI_ERR_INTERN;")
G.out.append("goto fn_fail;");
G.out.append("#else")
dump_body_of_routine(func)
G.out.append("#endif")
G.out.append("")
G.out.append("fn_exit:")
if not '_skip_global_cs' in func:
G.out.append("MPIR_Ext_cs_exit();")
G.out.append("return mpi_errno;")
G.out.append("fn_fail:")
if '_has_file' in func:
G.out.append("mpi_errno = MPIO_Err_return_file(%s, mpi_errno);" % func['_has_file'])
else:
G.out.append("mpi_errno = MPIO_Err_return_file(MPI_FILE_NULL, mpi_errno);")
G.out.append("goto fn_exit;")
G.out.append("DEDENT")
G.out.append("}")
G.out.append("")

def dump_qmpi_wrappers(func, is_large):
parameters = ""
for p in func['c_parameters']:
Expand Down

0 comments on commit a4c17e9

Please sign in to comment.