Arquivos
Hui Zhou 42acceeb68 doc: fix mansrc generation
Just using the name without prefix, e.g. MPI_Send -> send.txt, resulting
in duplicated filenames between MPI_/MPI_T_/MPIX_ functions, and they
overwrite each other. This resulted in certain man pages missing. For
example, MPI_Finalize and MPI_Init_thread man pages were missing because
they were overwritten by the MPI_T_ correspondents.
2024-08-20 12:39:12 -05:00

2913 linhas
119 KiB
Python

##
## Copyright (C) by Argonne National Laboratory
## See COPYRIGHT in top-level directory
##
from local_python import MPI_API_Global as G
from local_python.binding_common import *
from local_python import RE
import sys
import re
import copy
# ---- top-level routines ----
def dump_mpi_c(func, is_large=False):
"""Dumps the function's C source code to G.out array"""
# whether it is a large count function
func['_is_large'] = is_large
check_func_directives(func)
filter_c_parameters(func)
check_params_with_large_only(func)
# for poly functions, decide impl interface
check_large_parameters(func)
# whether we need potentially swap the counts array argument
func["_need_coll_v_swap"] = False
func["_need_type_create_swap"] = False
if RE.search(r'((all)?gatherv|scatterv|alltoall[vw]|reduce_scatter)(_init)?\b', func['name'], re.IGNORECASE):
func["_need_coll_v_swap"] = True
elif RE.search(r'(h?indexed(_block)?|struct|(d|sub)array)', func['name'], re.IGNORECASE):
func["_need_type_create_swap"] = True
process_func_parameters(func)
# for mpi_proto.h. The abi prototypes are already in mpi_abi.h.
if "_is_abi" not in func:
G.mpi_declares.append(get_declare_function(func, is_large, "proto"))
# collect error codes additional from auto generated ones
if 'error' in func:
for a in func['error'].split(", "):
G.err_codes[a] = 1
# Some routines, e.g. MPIR_Comm_split_filesystem, are defined in romio or libromio.la, but are
# only referenced from libpmpi.la. This causes an issue in static linking when weak symbols are
# not available.
#
# As an hack, we reference these symbols in selected routines to force the inclusion of romio
# routines.
def dump_romio_reference(name):
if G.need_dump_romio_reference:
G.out.append("#if defined(HAVE_ROMIO) && defined(MPICH_MPI_FROM_PMPI)")
G.out.append("void MPIR_Comm_split_filesystem(void);")
G.out.append("void MPIR_ROMIO_Get_file_errhand(void);")
G.out.append("void MPIR_ROMIO_Set_file_errhand(void);")
G.out.append("void *dummy_refs_%s[] = {" % name)
G.out.append(" (void *) MPIR_Comm_split_filesystem,")
G.out.append(" (void *) MPIR_ROMIO_Get_file_errhand,")
G.out.append(" (void *) MPIR_ROMIO_Set_file_errhand,")
G.out.append("};")
G.out.append("#endif")
if 'single-source' in G.opts:
G.need_dump_romio_reference = False
# -- "dump" accumulates output lines in G.out
if not is_large:
# only include once (skipping "BIG")
if 'include' in func:
for a in func['include'].replace(',', ' ').split():
G.out.append("#include \"%s\"" % a)
# hack to get romio routines to work with static linking + no-weak-symbols
if re.match(r'mpi_(init|get_library_version|session_init|t_init_thread)', func['name'], re.IGNORECASE):
dump_romio_reference(func['name'])
G.out.append("")
dump_profiling(func)
if '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:
dump_abi_wrappers(func, func['_is_large'])
else:
# Create the MPI and QMPI wrapper functions that will call the above, "real" version of the
# function in the internal prefix
dump_qmpi_wrappers(func, func['_is_large'])
def get_func_file_path(func, root_dir):
file_path = None
dir_path = root_dir + '/' + func['dir']
if 'file' in func:
file_path = dir_path + '/' + func['file'] + ".c"
elif RE.match(r'MPI_T_(\w+)', func['name'], re.IGNORECASE):
name = RE.m.group(1)
file_path = dir_path + '/' + name.lower() + ".c"
elif RE.match(r'MPIX?_(\w+)', func['name'], re.IGNORECASE):
name = RE.m.group(1)
file_path = dir_path + '/' + name.lower() + ".c"
else:
raise Exception("Error in function name pattern: %s\n" % func['name'])
return file_path
def get_mansrc_file_path(func, root_dir):
file_path = None
dir_path = root_dir
if 'file' in func:
file_path = dir_path + '/' + func['file'] + ".txt"
elif RE.match(r'(MPI(X|_T)?_\w+)', func['name'], re.IGNORECASE):
name = RE.m.group(1)
file_path = dir_path + '/' + name.lower() + ".txt"
else:
raise Exception("Error in function name pattern: %s\n" % func['name'])
return file_path
def dump_c_file(f, lines):
print(" --> [%s]" % f)
with open(f, "w") as Out:
(indent, prev_empty) = (0, 0)
for l in G.copyright_c:
print(l, file=Out)
for l in lines:
# stripping the newline for consistency
l = l.rstrip()
# handle special directives etc.
if RE.match(r'(INDENT|DEDENT)', l):
# indentations
a = RE.m.group(1)
if a == "INDENT":
indent += 1
else:
indent -= 1
elif RE.match(r'fn_\w+:', l):
# goto labels, use 2-space indentation
print(" %s" % (l), file=Out)
elif RE.match(r'\s*$', l):
# empty lines, avoid double empty lines
if not prev_empty:
print("", file=Out)
prev_empty = 1
else:
# print the line with correct indentations
if indent > 0 and not RE.match(r'#(if|else|endif)', l):
print(" " * indent, end='', file=Out)
print(l, file=Out)
prev_empty = 0
def dump_Makefile_mk(f):
print(" --> [%s]" % f)
with open(f, "w") as Out:
for l in G.copyright_mk:
print(l, file=Out)
print("mpi_sources += \\", file=Out)
n = len(G.mpi_sources)
for i, f in enumerate(G.mpi_sources):
if i < n - 1:
print(" %s \\" % f, file=Out)
else:
print(" %s" % f, file=Out)
n = len(G.doc3_src_txt)
if n > 0:
print("doc3_src_txt += \\", file=Out)
for i, f in enumerate(G.doc3_src_txt):
if i < n - 1:
print(" %s \\" % f, file=Out)
else:
print(" %s" % f, file=Out)
def dump_mpir_impl_h(f):
def dump_mpix_symbols():
# define MPI symbols from future API to MPIX
print("", file=Out)
for a, t in G.mpix_symbols.items():
if t == "symbols":
print("#define %s %s" % (a, re.sub(r'^MPI_', 'MPIX_', a)), file=Out)
print(" --> [%s]" %f)
with open(f, "w") as Out:
for l in G.copyright_c:
print(l, file=Out)
print("#ifndef MPIR_IMPL_H_INCLUDED", file=Out)
print("#define MPIR_IMPL_H_INCLUDED", file=Out)
dump_mpix_symbols()
print("", file=Out)
for l in G.impl_declares:
print(l, file=Out)
print("", file=Out)
print("#endif /* MPIR_IMPL_H_INCLUDED */", file=Out)
def filter_out_abi():
funcname = None
for l in G.out:
if RE.match(r'int (\w+)\(.*', l):
funcname = RE.m.group(1)
elif RE.match(r'{'):
pass
elif RE.match(r'}'):
pass
def get_qmpi_decl_from_func_decl(func_decl, kind=""):
if RE.match(r'(.*) (MPIX?_\w+)\((.*?)\)(.*)', func_decl):
T, name, args, tail = RE.m.group(1,2,3,4)
else:
raise Exception("Bad pattern in declaration %s" % func_decl)
if args == 'void':
t = "%s Q%s(QMPI_Context context, int tool_id)" % (T, name)
else:
t = "%s Q%s(QMPI_Context context, int tool_id, %s)" % (T, name, args)
if kind == 'proto':
while RE.search(r'MPICH_ATTR_POINTER_WITH_TYPE_TAG\((\d+),(\d+)\)(.*)', tail):
i1, i2, tail = RE.m.group(1, 2, 3)
t += " MPICH_ATTR_POINTER_WITH_TYPE_TAG(%d,%d)" % (int(i1) + 2, int(i2) + 2)
t += " MPICH_API_PUBLIC"
return t
def get_qmpi_typedef_from_func_decl(func_decl):
if RE.match(r'(.*) (MPIX?_\w+)\((.*?)\)', func_decl):
T, name, args = RE.m.group(1,2,3)
else:
raise Exception("Bad pattern in declaration %s" % func_decl)
if args == 'void':
t = "typedef %s (Q%s_t) (QMPI_Context context, int tool_id);" % (T, name)
else:
t = "typedef %s (Q%s_t) (QMPI_Context context, int tool_id, %s);" % (T, name, args)
return t
def need_skip_qmpi(func_name):
# If this is a large count function, it needs to have the suffix removed since we add that
# later, but it's not in the G.FUNCS list below
if func_name.lower()[-2:] == "_c":
func_name = func_name[:-2]
# Some of the MPIX_ functions are not in the list because they are functions to be added in a
# future MPI standard. Internally, we treat them as MPI_ functions and add the X later, but they
# will be in G.FUNCS without the X.
no_x_func_name = func_name
if no_x_func_name.lower()[:4] == "mpix":
no_x_func_name = "mpi" + no_x_func_name[4:]
if func_name.lower() in G.FUNCS or no_x_func_name.lower() in G.FUNCS:
if func_name.lower() in G.FUNCS:
func = G.FUNCS[func_name.lower()];
elif no_x_func_name.lower() in G.FUNCS:
func = G.FUNCS[no_x_func_name.lower()];
if 'dir' not in func or 'not_implemented' in func:
return True
elif re.match(r'MPI_DUP_FN', func['name']):
return True
else:
return False
else:
# Warn?
return True
def dump_mpi_proto_h(f):
def dump_line(s, tail, Out):
tlist = split_line_with_break(s, tail, 100)
for l in tlist:
print(l, file=Out)
def dump_proto_line(l, Out):
if RE.match(r'(.*?\))\s+(MPICH.*)', l):
s, tail = RE.m.group(1,2)
dump_line(s, tail + ';', Out)
# -- sort the prototypes into groups --
list_a = [] # prototypes the fortran needs
list_b = [] # tool prototypes
list_c = [] # large prototypes
for l in G.mpi_declares:
if re.match(r'int (MPIX?_T_|MPIX_Grequest_)', l):
list_b.append(l)
elif re.match(r'int MPIX?_\w+_c\(', l):
list_c.append(l)
else:
list_a.append(l)
# -- dump the file --
print(" --> [%s]" %f)
with open(f, "w") as Out:
for l in G.copyright_c:
print(l, file=Out)
print("#ifndef MPI_PROTO_H_INCLUDED", file=Out)
print("#define MPI_PROTO_H_INCLUDED", file=Out)
print("", file=Out)
# -- mpi prototypes --
print("/* Begin Prototypes */", file=Out)
for l in list_a:
dump_proto_line(l, Out)
print("/* End Prototypes */", file=Out)
print("", file=Out)
# -- tool prototypes --
for l in list_b:
dump_proto_line(l, Out)
print("", file=Out)
# -- large prototypes --
for l in list_c:
dump_proto_line(l, Out)
print("", file=Out)
# -- PMPI prototypes --
for l in G.mpi_declares:
if re.match(r'int MPI_DUP_FN', l):
continue
dump_proto_line(re.sub(' MPI', ' PMPI', l, 1), Out)
print("", file=Out)
# -- QMPI function enum --
# We need this all the time to avoid unknown types
print("enum QMPI_Functions_enum {", file=Out)
for l in G.mpi_declares:
func_name = get_funcname_from_decl(l)
if need_skip_qmpi(func_name):
continue
print(" " + func_name.upper() + "_T,", file=Out)
print(" MPI_LAST_FUNC_T", file=Out)
print("};", file=Out)
print("", file=Out)
# -- QMPI prototypes --
for func_decl in G.mpi_declares:
func_name = get_funcname_from_decl(func_decl)
if need_skip_qmpi(func_name):
continue
func_decl = get_qmpi_decl_from_func_decl(func_decl, 'proto')
dump_proto_line(func_decl, Out)
print("", file=Out)
# -- QMPI function typedefs --
for func_decl in G.mpi_declares:
func_name = get_funcname_from_decl(func_decl)
if need_skip_qmpi(func_name):
continue
func_decl = get_qmpi_typedef_from_func_decl(func_decl)
dump_line(func_decl, '', Out)
print("", file=Out)
print("#endif /* MPI_PROTO_H_INCLUDED */", file=Out)
def dump_errnames_txt(f):
print(" --> [%s]" % f)
with open(f, "w") as Out:
for l in G.copyright_mk:
print(l, file=Out)
for l in G.mpi_errnames:
print(l, file=Out)
def dump_mtest_mpix_h(f):
print(" --> [%s]" % f)
with open(f, "w") as Out:
print("#ifndef MTEST_MPIX_H_INCLUDED", file=Out)
print("#define MTEST_MPIX_H_INCLUDED", file=Out)
print("", file=Out)
for a in G.mpix_symbols:
print("#define %s %s" % (a, re.sub(r'MPI_', 'MPIX_', a)), file=Out)
print("", file=Out)
print("#endif /* MTEST_MPIX_H_INCLUDED */", file=Out)
def dump_qmpi_register_h(f):
print(" --> [%s]" %f)
with open(f, "w") as Out:
for l in G.copyright_c:
print(l, file=Out)
print("#ifndef QMPI_REGISTER_H_INCLUDED", file=Out)
print("#define QMPI_REGISTER_H_INCLUDED", file=Out)
print("", file=Out)
print("#ifdef ENABLE_QMPI", file=Out)
print("", file=Out)
print("static inline int MPII_qmpi_register_internal_functions(void)", file=Out)
print("{", file=Out)
for l in G.mpi_declares:
func_name = get_funcname_from_decl(l)
if need_skip_qmpi(func_name):
continue
print(" MPIR_QMPI_pointers[%s_T] = (void (*)(void)) &Q%s;" % (func_name.upper(), func_name), file=Out)
print("", file=Out)
print(" return MPI_SUCCESS;", file=Out)
print("}", file=Out)
print("", file=Out)
print("#endif /* ENABLE_QMPI */", file=Out)
print("", file=Out)
print("#endif /* QMPI_REGISTER_H_INCLUDED */", file=Out)
# ---- pre-processing ----
def check_func_directives(func):
# add default to ease the check later
if 'skip' not in func:
func['skip'] = ""
if 'extra' not in func:
func['extra'] = ""
if func['dir'] == "mpit":
func['_skip_Fortran'] = 1
if RE.search(r'ThreadSafe', func['skip'], re.IGNORECASE):
func['_skip_ThreadSafe'] = 1
if RE.search(r'Fortran', func['skip'], re.IGNORECASE):
func['_skip_Fortran'] = 1
if RE.search(r'(global_cs|initcheck)', func['skip'], re.IGNORECASE):
func['_skip_global_cs'] = 1
if RE.search(r'initcheck', func['skip'], re.IGNORECASE):
func['_skip_initcheck'] = 1
if RE.search(r'Errors', func['skip'], re.IGNORECASE):
func['_skip_err_codes'] = 1
if RE.search(r'ignore_revoked_comm', func['extra'], re.IGNORECASE):
func['_comm_valid_ptr_flag'] = 'TRUE'
else:
func['_comm_valid_ptr_flag'] = 'FALSE'
if RE.search(r'errtest_comm_intra', func['extra'], re.IGNORECASE):
func['_errtest_comm_intra'] = 1
func['_skip_validate'] = {}
for a in re.findall(r'validate-(\w+)', func['skip']):
func['_skip_validate'][a] = 1
if 'code-error_check-tail' in func:
for a in func['code-error_check-tail']:
func['_skip_validate'][a] = 1
# additional docnotes
func['_docnotes'] = []
if 'docnotes' in func:
func['_docnotes'] = func['docnotes'].replace(' ', '').split(',')
if RE.search(r'SignalSafe', func['docnotes'], re.IGNORECASE):
print("Function %s is declared \"SignalSafe\", consider switch to `.skip: global_cs`" % func['name'], file=sys.stderr)
if RE.search(r'(NotThreadSafe|ThreadSafeNoUpdate)', func['docnotes']):
func['_skip_ThreadSafe'] = 1
if RE.search(r'(NotThreadSafe)', func['docnotes']):
func['_skip_global_cs'] = 1
if not '_skip_ThreadSafe' in func:
func['_docnotes'].append('ThreadSafe')
if not '_skip_Fortran' in func:
func['_docnotes'].append('Fortran')
# additional error codes
if 'errorcodes' in func:
for a in func['errorcodes'].replace(' ', '').split(','):
G.err_codes[a] = 1
# additional exit_routines (clean up)
func['_clean_up'] = []
if 'code-clean_up' in func:
func['_clean_up'].extend(func['code-clean_up'])
# cleanup internal states
func.pop('_got_comm_size', None)
func.pop('_got_comm_rank', None)
func.pop('_got_topo_size', None)
def filter_c_parameters(func):
if "c_parameters" not in func:
c_params = []
for p in func['parameters']:
if RE.search(r'c_parameter', p['suppress']):
pass
else:
c_params.append(p)
func['c_parameters'] = c_params
def check_params_with_large_only(func):
if '_has_large_only' not in func:
func['_has_large_only'] = 0
for p in func['c_parameters']:
if p['large_only']:
func['_has_large_only'] += 1
if func['_has_large_only']:
func['params_large'] = func['c_parameters']
func['params_small'] = []
for p in func['c_parameters']:
if not p['large_only']:
func['params_small'].append(p)
if func['_has_large_only']:
if not func['_is_large']:
func['c_parameters'] = func['params_small']
else:
func['c_parameters'] = func['params_large']
def process_func_parameters(func):
""" Scan parameters and populate a few lists to facilitate generation."""
# Note: we'll attach the lists to func at the end
validation_list, handle_ptr_list, impl_arg_list, impl_param_list = [], [], [], []
pointertag_list = [] # needed to annotate MPICH_ATTR_POINTER_WITH_TYPE_TAG
# init to empty list or we will have double entries due to being called twice (small and large)
func['_has_handle_out'] = []
func_name = func['name']
n = len(func['c_parameters'])
i = 0
while i < n:
p = func['c_parameters'][i]
(group_kind, group_count) = ("", 0)
if i + 3 <= n and RE.search(r'BUFFER', p['kind']):
group_kind, group_count = get_userbuffer_group(func_name, func['c_parameters'], i)
if group_count > 0:
t = ''
for j in range(group_count):
temp_p = func['c_parameters'][i + j]
if t:
t += ","
t += temp_p['name']
impl_arg_list.append(temp_p['name'])
impl_param_list.append(get_impl_param(func, temp_p))
validation_list.append({'kind': group_kind, 'name': t})
# -- pointertag_list
if re.search(r'alltoallw', func_name, re.IGNORECASE):
pass
elif group_count == 3:
pointertag_list.append("%d,%d" % (i + 1, i + 3))
elif group_count == 4:
pointertag_list.append("%d,%d" % (i + 1, i + 4))
elif group_count == 5:
pointertag_list.append("%d,%d" % (i + 1, i + 4))
pointertag_list.append("%d,%d" % (i + 2, i + 4))
# -- skip to next
i += group_count
continue
do_handle_ptr = 0
(kind, name) = (p['kind'], p['name'])
if '_has_comm' not in func and kind == "COMMUNICATOR" and p['param_direction'] == 'in':
func['_has_comm'] = name
elif name == "win":
func['_has_win'] = name
elif name == "session":
func['_has_session'] = name
elif name == "group":
func['_has_group'] = name
if 'ANY' in func['_skip_validate'] or kind in func['_skip_validate'] or name in func['_skip_validate']:
# -- user bypass --
pass
elif p['param_direction'] == 'out':
# -- output parameter --
if p['kind'] == 'STATUS':
if p['length']:
length = p['length']
if length == '*':
if RE.match(r'MPI_(Test|Wait|Request_get_status_)all', func_name, re.IGNORECASE):
length = "count"
elif RE.match(r'MPI_(Test|Wait|Request_get_status_)some', func_name, re.IGNORECASE):
length = "incount"
else:
raise Exception("Unexpected")
validation_list.append({'kind': "STATUS-length", 'name': name, 'length': length})
else:
validation_list.append({'kind': "STATUS", 'name': name})
elif p['length']:
validation_list.append({'kind': "ARGNULL-length", 'name': name, 'length': p['length']})
else:
validation_list.append({'kind': "ARGNULL", 'name': name})
if RE.search(r'(get_errhandler|mpi_comm_get_parent)$', func_name, re.IGNORECASE):
# we may get the built-in handler, which doesn't have pointer
pass
elif kind == "GREQUEST_CLASS" or kind == "DATATYPE":
pass
elif kind in G.handle_out_do_ptrs:
do_handle_ptr = 2
elif p['length']:
# -- array parameter --
if kind == "REQUEST":
if RE.match(r'mpi_startall', func_name, re.IGNORECASE):
do_handle_ptr = 3
elif RE.match(r'mpix?_(wait|test|request_get_status)', func_name, re.IGNORECASE):
do_handle_ptr = 3
elif kind == "RANK":
validation_list.append({'kind': "RANK-ARRAY", 'name': name})
elif RE.match(r'\w+$', p['length']):
if RE.match(r'(POLY)?DTYPE_NUM_ELEM_NNI', kind):
validation_list.append({'kind':"COUNT-ARRAY", 'name': name, 'length': p['length']})
elif RE.match(r'DATATYPE', kind):
validation_list.append({'kind':"TYPE-ARRAY", 'name': name, 'length': p['length']})
elif RE.match(r'(POLY)?DISPLACEMENT.*_COUNT', kind):
validation_list.append({'kind':"DISP-ARRAY", 'name': name, 'length': p['length']})
else:
# FIXME
pass
else:
# FIXME
pass
elif kind == "DATATYPE":
if RE.match(r'mpi_type_(get|set|delete)_attr|mpi_type_(set_name|lb|ub|extent)', func_name, re.IGNORECASE):
do_handle_ptr = 1
elif RE.match(r'mpi_type_get_name', func_name, re.IGNORECASE):
p['can_be_null'] = "MPI_DATATYPE_NULL"
do_handle_ptr = 1
else:
if is_pointer_type(p):
validation_list.append({'kind': "datatype_and_ptr", 'name': '*' + name})
else:
validation_list.append({'kind': "datatype_and_ptr", 'name': name})
elif kind == "OPERATION":
if RE.match(r'mpi_op_(free)', func_name, re.IGNORECASE):
do_handle_ptr = 1
elif RE.match(r'mpi_r?accumulate', func_name, re.IGNORECASE):
validation_list.append({'kind': "OP_ACC", 'name': name})
elif RE.match(r'mpi_(r?get_accumulate|fetch_and_op)', func_name, re.IGNORECASE):
validation_list.append({'kind': "OP_GACC", 'name': name})
elif RE.match(r'mpi_op_(commutative)', func_name, re.IGNORECASE):
validation_list.append({'kind': "op_ptr", 'name': name})
else:
validation_list.append({'kind': "op_and_ptr", 'name': name})
elif kind == "MESSAGE" and p['param_direction'] == 'inout':
do_handle_ptr = 1
elif kind == "KEYVAL":
if RE.search(r'_(set_attr|delete_attr|free_keyval)$', func_name):
do_handle_ptr = 1
if is_pointer_type(p):
validation_list.append({'kind': "KEYVAL", 'name': '*' + name})
else:
validation_list.append({'kind': "KEYVAL", 'name': name})
elif kind == "GREQUEST_CLASS":
# skip validation for now
pass
elif kind in G.handle_mpir_types:
do_handle_ptr = 1
if kind == "INFO" and not RE.match(r'mpi_(info_.*|.*_set_info)$', func_name, re.IGNORECASE):
p['can_be_null'] = "MPI_INFO_NULL"
elif kind == "REQUEST" and RE.match(r'mpix?_(wait|test|request_get_status|parrived)', func_name, re.IGNORECASE):
p['can_be_null'] = "MPI_REQUEST_NULL"
elif kind == "STREAM" and RE.match(r'mpix?_(stream_(comm_create|progress)|async_(start|spawn))', func_name, re.IGNORECASE):
p['can_be_null'] = "MPIX_STREAM_NULL"
elif kind == "COMMUNICATOR" and RE.match(r'mpi_comm_get_name', func_name, re.IGNORECASE):
p['can_be_null'] = "MPI_COMM_NULL"
elif kind == "WINDOW" and RE.match(r'mpi_win_get_name', func_name, re.IGNORECASE):
p['can_be_null'] = "MPI_WIN_NULL"
elif kind == "RANK" and name == "root":
validation_list.insert(0, {'kind': "ROOT", 'name': name})
elif RE.match(r'(COUNT|TAG)$', kind):
validation_list.append({'kind': RE.m.group(1), 'name': name})
elif RE.match(r'RANK(_NNI)?$', kind):
if RE.match(r'mpi_intercomm_create_from_groups', func_name, re.IGNORECASE):
# TODO: add validation
pass
else:
validation_list.append({'kind': 'RANK', 'name': name})
elif RE.match(r'(POLY)?(XFER_NUM_ELEM|DTYPE_NUM_ELEM_NNI|DTYPE_PACK_SIZE)', kind):
validation_list.append({'kind': "COUNT", 'name': name})
elif RE.match(r'(POLY)?DTYPE_NUM_ELEM', kind):
if name != 'stride': # NOTE: fragile
validation_list.append({'kind': "COUNT", 'name': name})
elif RE.match(r'WINDOW_SIZE|WIN_ATTACH_SIZE', kind):
validation_list.append({'kind': "WIN_SIZE", 'name': name})
elif RE.match(r'(ALLOC_MEM_NUM_BYTES|(POLY)?NUM_PARAM_VALUES)', kind):
validation_list.append({'kind': "ARGNEG", 'name': name})
elif RE.match(r'(C_)?BUFFER', kind) and RE.match(r'MPI_Win_(allocate|create|attach)', func_name):
validation_list.append({'kind': "WINBUFFER", 'name': name})
elif RE.match(r'(POLY)?(RMA_DISPLACEMENT)', kind):
if name == 'disp_unit':
validation_list.append({'kind': "WIN_DISPUNIT", 'name': name})
else:
validation_list.append({'kind': "RMADISP", 'name': name})
elif RE.match(r'(.*_NNI|ARRAY_LENGTH|INFO_VALUE_LENGTH|KEY_INDEX|INDEX|NUM_DIMS|DIMENSION|COMM_SIZE|DISPLACEMENT_COUNT)', kind):
if p['param_direction'] == 'inout':
validation_list.append({'kind': "ARGNULL", 'name': name})
validation_list.append({'kind': "ARGNEG", 'name': "*" + name})
elif RE.search(r'count', name):
validation_list.append({'kind': "COUNT", 'name': name})
else:
validation_list.append({'kind': "ARGNEG", 'name': name})
elif RE.match(r'(.*_PI)', kind):
validation_list.append({'kind': "ARGNONPOS", 'name': name})
elif kind == "STRING" and name == "key":
validation_list.append({'kind': "infokey", 'name': name})
elif RE.match(r'(CAT|CVAR|PVAR)_INDEX', kind):
validation_list.append({'kind': "mpit_%s_index" % RE.m.group(1), 'name': name})
elif RE.match(r'(CVAR|PVAR|TOOLS_ENUM)$', kind):
if kind == "CVAR" or kind == "PVAR":
t_kind = "mpit_" + kind.lower() + "_handle"
else:
t_kind = "mpit_enum_handle"
t_name = name
if RE.search(r'direction=inout', p['t']):
t_name = '*' + name
validation_list.append({'kind': t_kind, 'name': t_name})
elif kind == "TOOLENUM_INDEX":
# MPI_T_enum_get_item, assume 1st param is enumtype
validation_list.append({'kind': "mpit_enum_item", 'name': "enumtype, " + name})
elif kind == "PVAR_SESSION":
t_name = name
if RE.search(r'direction=inout', p['t']):
t_name = '*' + name
validation_list.append({'kind': "mpit_pvar_session", 'name': t_name})
elif kind == "PVAR_CLASS":
validation_list.append({'kind': "mpit_pvar_class", 'name': name})
elif kind == "EVENT_REGISTRATION":
validation_list.append({'kind': "mpit_event_registration", 'name': name})
elif kind == "EVENT_INSTANCE":
validation_list.append({'kind': "mpit_event_instance", 'name': name})
elif RE.match(r'(FUNCTION)', kind):
if RE.match(r'mpi_(keyval_create|\w+_create_keyval)', func_name, re.IGNORECASE):
# MPI_NULL_COPY_FN etc are defined as NULL
pass
else:
validation_list.append({'kind': "ARGNULL", 'name': name})
elif RE.match(r'(ERROR_CLASS|ERROR_CODE|FILE|ATTRIBUTE_VAL|EXTRA_STATE|LOGICAL|MATH)', kind):
# no validation for these kinds
pass
elif RE.match(r'F90_(COMM|ERRHANDLER|FILE|GROUP|INFO|MESSAGE|OP|REQUEST|SESSION|DATATYPE|WIN)', kind):
# no validation for these kinds
pass
elif RE.match(r'(POLY)?(DTYPE_STRIDE_BYTES|DISPLACEMENT_AINT_COUNT)$', kind):
# e.g. stride in MPI_Type_vector, MPI_Type_create_resized
pass
elif is_pointer_type(p):
validation_list.append({'kind': "ARGNULL", 'name': name})
else:
print("Missing error checking: func=%s, name=%s, kind=%s" % (func_name, name, kind), file=sys.stderr)
if do_handle_ptr == 1:
if p['param_direction'] == 'inout':
# assume only one such parameter
func['_has_handle_inout'] = p
if (p['param_direction'] == 'inout' or p['pointer']) and not p['length']:
p['_pointer'] = 1
handle_ptr_list.append(p)
impl_arg_list.append(name + "_ptr")
impl_param_list.append("%s *%s_ptr" % (G.handle_mpir_types[kind], name))
elif do_handle_ptr == 2:
# Just MPI_Comm_idup[_with_info] have 2 handle output, but use a list anyway
func['_has_handle_out'].append(p)
impl_arg_list.append('&' + name + "_ptr")
impl_param_list.append("%s **%s_ptr" % (G.handle_mpir_types[kind], name))
elif do_handle_ptr == 3:
# arrays
handle_ptr_list.append(p)
if kind == "REQUEST":
ptrs_name = "request_ptrs"
p['_ptrs_name'] = ptrs_name
if RE.match(r'mpi_startall', func['name'], re.IGNORECASE):
impl_arg_list.append(ptrs_name)
impl_param_list.append("MPIR_Request **%s" % ptrs_name)
else:
impl_arg_list.append(name)
impl_param_list.append("MPI_Request %s[]" % name)
else:
print("Unhandled handle array: " + name, file=sys.stderr)
elif "code-handle_ptr-tail" in func and name in func['code-handle_ptr-tail']:
mpir_type = G.handle_mpir_types[kind]
if p['length']:
impl_arg_list.append(name + "_ptrs")
impl_param_list.append("%s **%s_ptrs" % (mpir_type, name))
else:
impl_arg_list.append(name + "_ptr")
impl_param_list.append("%s *%s_ptr" % (mpir_type, name))
else:
impl_arg_list.append(name)
impl_param_list.append(get_impl_param(func, p))
i += 1
if RE.match(r'MPI_(Wait|Test)$', func_name):
func['_has_comm'] = "comm"
func['_comm_from_request'] = 1
func['_need_validation'] = 0
if len(validation_list):
func['_validation_list'] = validation_list
func['_need_validation'] = 1
if len(handle_ptr_list):
func['_handle_ptr_list'] = handle_ptr_list
func['_need_validation'] = 1
if 'code-error_check' in func:
func['_need_validation'] = 1
func['_impl_arg_list'] = impl_arg_list
func['_impl_param_list'] = impl_param_list
if len(pointertag_list):
func['_pointertag_list'] = pointertag_list
# ---- simple parts ----
def dump_copy_right():
G.out.append("/*")
G.out.append(" * Copyright (C) by Argonne National Laboratory")
G.out.append(" * See COPYRIGHT in top-level directory")
G.out.append(" */")
G.out.append("")
def dump_qmpi_wrappers(func, is_large):
parameters = ""
for p in func['c_parameters']:
parameters = parameters + ", " + p['name']
func_name = get_function_name(func, is_large)
func_decl = get_declare_function(func, is_large)
qmpi_decl = get_qmpi_decl_from_func_decl(func_decl)
static_call = get_static_call_internal(func, is_large)
G.out.append("#ifdef ENABLE_QMPI")
G.out.append("#ifndef MPICH_MPI_FROM_PMPI")
dump_line_with_break(qmpi_decl)
G.out.append("{")
if func_name == "MPI_Pcontrol":
G.out.append(" va_list varargs;")
G.out.append(" va_start(varargs, level);")
G.out.append("")
G.out.append(" return " + static_call + ";")
G.out.append("}")
G.out.append("#endif /* MPICH_MPI_FROM_PMPI */")
dump_line_with_break(func_decl)
G.out.append("{")
G.out.append(" QMPI_Context context;")
G.out.append(" Q%s_t *fn_ptr;" % (func_name))
G.out.append("")
G.out.append(" context.storage_stack = NULL;")
G.out.append("")
if func_name == "MPI_Pcontrol":
G.out.append(" va_list varargs;")
G.out.append(" va_start(varargs, level);")
G.out.append("")
if "initcheck" in func['skip']:
G.out.append(" int mpi_errno = MPI_SUCCESS;")
G.out.append(" mpi_errno = MPII_qmpi_init();")
G.out.append(" if (mpi_errno != MPI_SUCCESS) {")
G.out.append(" return mpi_errno;")
G.out.append(" }")
G.out.append("")
G.out.append(" if (MPIR_QMPI_num_tools == 0)")
dump_line_with_break(" return Q%s(context, 0%s);" % (func_name, parameters))
G.out.append("")
dump_line_with_break(" fn_ptr = (Q%s_t *) MPIR_QMPI_first_fn_ptrs[%s_T];" % (func_name, func_name.upper()))
G.out.append("")
dump_line_with_break(" return (*fn_ptr) (context, MPIR_QMPI_first_tool_ids[%s_T]%s);" % (func_name.upper(), parameters));
G.out.append("}")
G.out.append("#else /* ENABLE_QMPI */")
dump_line_with_break(func_decl)
G.out.append("{")
if func_name == "MPI_Pcontrol":
G.out.append(" va_list varargs;")
G.out.append(" va_start(varargs, level);")
G.out.append("")
G.out.append(" return " + static_call + ";")
G.out.append("}")
G.out.append("")
G.out.append("#endif /* ENABLE_QMPI */")
def dump_abi_wrappers(func, is_large):
func_name = get_function_name(func, is_large)
mapping = get_kind_map('C', is_large)
param_list = []
already_done = set()
assertion_types = {}
pre_filters = []
post_filters = []
# prepare for array of handles
array_type = None
array_size = None
is_alltoallw = False
is_neighbor_alltoallw = False
if re.match(r'mpi_i?alltoallw(_init)?', func['name'], re.IGNORECASE):
array_type = "Datatype"
array_size = "peer_size"
is_alltoallw = True
pre_filters.append("int peer_size = ABI_Comm_peer_size(comm_abi);")
elif re.match(r'mpi_i?neighbor_alltoallw(_init)?', func['name'], re.IGNORECASE):
array_type = "Datatype"
is_neighbor_alltoallw = True
# array_size set per parameters
pre_filters.append("int indegree, outdegree;")
pre_filters.append("ABI_Comm_neighbors_count(comm_abi, &indegree, &outdegree);")
elif re.match(r'mpi_type_(create_struct|struct)', func['name'], re.IGNORECASE):
array_type = "Datatype"
array_size = "count"
elif re.match(r'mpi_type_get_contents', func['name'], re.IGNORECASE):
array_type = "Datatype"
array_size = "max_datatypes"
elif re.match(r'mpi_t_event_get_info', func['name'], re.IGNORECASE):
# special treatment below because it is optional
array_type = "Datatype"
array_size = "(*num_elements)"
elif re.match(r'mpi_comm_spawn$', func['name'], re.IGNORECASE):
pre_filters.append("MPI_Info info = MPI_INFO_NULL;")
pre_filters.append("if (ABI_Comm_rank(comm_abi) == root) {")
pre_filters.append(" info = ABI_Info_to_mpi(info_abi);")
pre_filters.append("}")
already_done.add("info")
elif re.match(r'mpi_comm_spawn_multiple', func['name'], re.IGNORECASE):
array_type = "Info"
array_size = "count_info"
pre_filters.append("int count_info = 0;")
pre_filters.append("if (ABI_Comm_rank(comm_abi) == root) {")
pre_filters.append(" count_info = count;")
pre_filters.append("}")
elif re.match(r'mpi_((wait|test|request_get_status_)(all|any)|startall)', func['name'], re.IGNORECASE):
array_type = "Request"
array_size = "count"
elif re.match(r'mpi_(wait|test|request_get_status_)some', func['name'], re.IGNORECASE):
array_type = "Request"
array_size = "incount"
# ----
def process_handle(p):
nonlocal array_size
name = p['name']
if name in already_done:
return
T = re.sub(r'MPI_', '', mapping[p['kind']])
# translate to internal parameters
if p['length']:
# neighbor_alltoallw
if is_neighbor_alltoallw:
if name == "recvtypes":
array_size = "indegree"
elif name == "sendtypes":
array_size = "outdegree"
if re.match(r'mpi_t_event_get_info', func['name'], re.IGNORECASE):
# array_of_datatypes[]
pre_filters.append("MPI_%s *%s = NULL;" % (array_type, name))
pre_filters.append("if (num_elements && %s > 0) {" % array_size)
pre_filters.append(" %s = MPL_malloc(sizeof(MPI_%s) * %s, MPL_MEM_OTHER);" % (name, array_type, array_size))
pre_filters.append("}")
post_filters.append("if (num_elements) {")
post_filters.append(" for (int i = 0; i < %s; i++) {" % array_size)
post_filters.append(" %s_abi[i] = ABI_%s_from_mpi(%s[i]);" % (name, array_type, name))
post_filters.append(" }")
post_filters.append("}")
elif array_size:
# assume input only
pre_filters.append("MPI_%s *%s = NULL;" % (array_type, name))
if is_alltoallw:
if name == "sendtypes":
pre_filters.append("if (sendbuf != MPI_IN_PLACE && sendtypes_abi != NULL) {")
pre_filters.append("INDENT")
elif name == "recvtypes":
# if sendtypes == recvtypes, imitate to facilitate ALIAS check
pre_filters.append("if (recvtypes_abi == sendtypes_abi && sendbuf != MPI_IN_PLACE) {")
pre_filters.append(" recvtypes = sendtypes;")
pre_filters.append("} else if (recvtypes_abi != NULL) {")
pre_filters.append("INDENT")
pre_filters.append("if (%s > 0) {" % array_size)
pre_filters.append(" %s = MPL_malloc(sizeof(MPI_%s) * %s, MPL_MEM_OTHER);" % (name, array_type, array_size))
pre_filters.append("}")
if p['param_direction'] == 'in' or p['param_direction'] == 'inout':
pre_filters.append("for (int i = 0; i < %s; i++) {" % array_size)
pre_filters.append(" %s[i] = ABI_%s_to_mpi(%s_abi[i]);" % (name, array_type, name))
pre_filters.append("}")
if p['param_direction'] == 'out' or p['param_direction'] == 'inout':
if RE.match(r'mpi_(Wait|Test|Request_get_status_)any', func['name'], re.IGNORECASE):
if RE.m.group(1) != 'Wait':
post_filters.append("if (*flag && *indx != MPI_UNDEFINED) {")
else:
post_filters.append("if (*indx != MPI_UNDEFINED) {")
post_filters.append(" %s_abi[*indx] = ABI_%s_from_mpi(%s[*indx]);" % (name, array_type, name))
post_filters.append("}")
elif re.match(r'mpi_(Wait|Test|Request_get_status_)some', func['name'], re.IGNORECASE):
post_filters.append("for (int i = 0; i < *outcount; i++) {")
post_filters.append(" int idx = array_of_indices[i];")
post_filters.append(" %s_abi[idx] = ABI_%s_from_mpi(%s[idx]);" % (name, array_type, name))
post_filters.append("}")
else:
post_filters.append("for (int i = 0; i < %s; i++) {" % array_size)
post_filters.append(" %s_abi[i] = ABI_%s_from_mpi(%s[i]);" % (name, array_type, name))
post_filters.append("}")
if is_alltoallw:
if name == "sendtypes":
pre_filters.append("DEDENT")
pre_filters.append("}")
post_filters.append("if (sendbuf != MPI_IN_PLACE) {")
post_filters.append("INDENT")
elif name == "recvtypes":
pre_filters.append("DEDENT")
pre_filters.append("}")
post_filters.append("if (recvtypes != sendtypes) {")
post_filters.append("INDENT")
post_filters.append("MPL_free(%s);" % name)
if is_alltoallw and (name == "sendtypes" or name == "recvtypes"):
post_filters.append("DEDENT")
post_filters.append("}")
else:
# show what we are missing (hopefully none)
print("Function %s - array param %s" % (func['name'], p['name']))
else:
if p['param_direction'] == 'out':
# pass NULL thru for error-checking
pre_filters.append("MPI_%s %s_i = MPI_%s_NULL;" % (T, name, T.upper()))
pre_filters.append("MPI_%s *%s = NULL;" % (T, name))
pre_filters.append("if (%s_abi != NULL) {" % name)
pre_filters.append(" %s = &%s_i;" % (name, name))
pre_filters.append("}")
post_filters.append("if (%s_abi != NULL) {" % name)
post_filters.append(" *%s_abi = ABI_%s_from_mpi(%s_i);" % (name, T, name));
post_filters.append("}")
elif p['param_direction'] == 'inout' or func['name'] == "MPI_Cancel":
# pass NULL thru for error-checking
pre_filters.append("MPI_%s %s_i;" % (T, name))
pre_filters.append("MPI_%s *%s = NULL;" % (T, name))
pre_filters.append("if (%s_abi != NULL) {" % name)
pre_filters.append(" %s_i = ABI_%s_to_mpi(*%s_abi);" % (name, T, name))
pre_filters.append(" %s = &%s_i;" % (name, name))
pre_filters.append("}")
post_filters.append("if (%s_abi != NULL) {" % name)
post_filters.append(" *%s_abi = ABI_%s_from_mpi(%s_i);" % (name, T, name));
post_filters.append("}")
else:
pre_filters.append("MPI_%s %s = ABI_%s_to_mpi(%s_abi);" % (T, name, T, name))
def out_can_be_undefined(p):
if p['param_direction'] == 'out':
if 'desc' in p and re.search(r'MPI_UNDEFINED', p['desc']):
return True
elif func['name'] == "MPI_Get_count":
return True
return False
# ----
re_Handle = r'MPI_(Comm|Datatype|Errhandler|Group|Info|Message|Op|Request|Session|Win|File)\b'
for p in func['c_parameters']:
skip_abi_swap = False
param_type = mapping[p['kind']]
name = p['name']
if RE.match(re_Handle, param_type):
process_handle(p)
elif p['kind'] == 'KEYVAL' and p['param_direction'] == 'in':
pre_filters.append("int %s = ABI_KEYVAL_to_mpi(%s_abi);" % (name, name))
elif p['kind'] == 'KEYVAL' and p['param_direction'] == 'inout':
pre_filters.append("int %s_i;" % (name))
pre_filters.append("int *%s = NULL;" % (name))
pre_filters.append("if (%s_abi != NULL) {" % (name))
pre_filters.append(" %s_i = ABI_KEYVAL_to_mpi(*%s_abi);" % (name, name))
pre_filters.append(" %s = &%s_i;" % (name, name))
pre_filters.append("}")
post_filters.append("if (%s_abi != NULL) {" % (name))
post_filters.append(" *%s_abi = ABI_KEYVAL_from_mpi(%s_i);" % (name, name))
post_filters.append("}")
else:
skip_abi_swap = True
# MPI_Comm comm -> ABI_Comm comm_abi
param = get_C_param(p, func, mapping)
param = re.sub(re_Handle, r'ABI_\1', param)
if not skip_abi_swap:
param = re.sub(r'\b' + name, name+"_abi", param)
param_list.append(param)
if not len(param_list):
param_list.append("void")
ret = "int"
if 'return' in func:
ret = mapping[func['return']]
ret = re.sub(re_Handle, r'ABI_\1', ret)
static_call = get_static_call_internal(func, is_large)
s_param = ', '.join(param_list)
func_decl = "%s %s(%s)" % (ret, func_name, s_param)
dump_line_with_break(func_decl)
G.out.append("{")
G.out.append("INDENT")
for T in assertion_types:
G.out.append("MPIR_Assert(sizeof(ABI_%s) == sizeof(MPI_%s));" % (T, T))
for l in pre_filters:
G.out.append(l)
if ret != 'int':
# MPI_Wtime, MPI_Aint_{add,diff}, MPI_{Comm,...}_{c2f,f2c}
if RE.match(r'..._(Comm|Datatype|Errhandler|Group|Info|Message|Op|Request|Session|Win|File)\b', ret):
G.out.append("return ABI_%s_from_mpi(%s);" % (RE.m.group(1), static_call))
else:
G.out.append("return " + static_call + ";")
else:
if func_name == "MPI_Pcontrol":
G.out.append("va_list varargs;")
G.out.append("va_start(varargs, level);")
G.out.append("")
G.out.append("int ret = " + static_call + ";")
for l in post_filters:
G.out.append(l)
if re.match(r'MPI_(Init|Init_thread|Session_init)$', func_name, re.IGNORECASE):
G.out.append("ABI_init_builtins();")
G.out.append("return ret;")
G.out.append("DEDENT")
G.out.append("}")
G.out.append("")
def dump_profiling(func):
func_name = get_function_name(func, func['_is_large'])
G.out.append("/* -- Begin Profiling Symbol Block for routine %s */" % func_name)
G.out.append("#if defined(HAVE_PRAGMA_WEAK)")
G.out.append("#pragma weak %s = P%s" % (func_name, func_name))
G.out.append("#elif defined(HAVE_PRAGMA_HP_SEC_DEF)")
G.out.append("#pragma _HP_SECONDARY_DEF P%s %s" % (func_name, func_name))
G.out.append("#elif defined(HAVE_PRAGMA_CRI_DUP)")
G.out.append("#pragma _CRI duplicate %s as P%s" % (func_name, func_name))
G.out.append("#elif defined(HAVE_WEAK_ATTRIBUTE)")
kind = None
if "_is_abi" in func:
kind = "abi"
s = get_declare_function(func, func['_is_large'], kind)
dump_line_with_break(s, " __attribute__ ((weak, alias(\"P%s\")));" % (func_name))
G.out.append("#endif")
G.out.append("/* -- End Profiling Symbol Block */")
G.out.append("")
G.out.append("/* Define MPICH_MPI_FROM_PMPI if weak symbols are not supported to build")
G.out.append(" the MPI routines */")
G.out.append("#ifndef MPICH_MPI_FROM_PMPI")
G.out.append("#undef %s" % func_name)
G.out.append("#define %s P%s" % (func_name, func_name))
G.out.append("#endif /* MPICH_MPI_FROM_PMPI */")
G.out.append("")
def dump_manpage(func, out):
def dump_description(s):
words = s.split()
n = len(words)
i0 = 0
i = 0
l = 0
for w in words:
if l == 0:
l += len(w)
else:
l += len(w) + 1
if l > 80:
out.append(' ' + ' '.join(words[i0:i]))
i0 = i
l = len(w)
i += 1
continue
if l > 0:
out.append(' ' + ' '.join(words[i0:]))
# ----
if not func['desc']:
# place holder to make the man page render
func['desc'] = "[short description]"
out.append("/*D")
out.append(" %s - %s" % (get_function_name(func, False), func['desc']))
out.append("")
# Synopsis
out.append("Synopsis:")
func_decl = get_declare_function(func, False)
tlist = split_line_with_break(func_decl, '', 80)
out.append(".vb")
out.extend(tlist)
out.append(".ve")
if func['_has_poly']:
func_decl = get_declare_function(func, True)
tlist = split_line_with_break(func_decl, '', 80)
out.append(".vb")
out.extend(tlist)
out.append(".ve")
out.append("")
lis_map = G.MAPS['LIS_KIND_MAP']
for p in func['c_parameters']:
lis_type = lis_map[p['kind']]
if 'desc' not in p:
if p['kind'] in G.default_descriptions:
p['desc'] = G.default_descriptions[p['kind']]
else:
p['desc'] = p['name']
if not re.search(r'\)$', p['desc']):
p['desc'] += " (%s)" % (lis_type)
input_list, output_list, inout_list = [], [], []
for p in func['c_parameters']:
if p['param_direction'] == 'out':
output_list.append(p)
elif p['param_direction'] == 'inout':
inout_list.append(p)
else:
input_list.append(p)
dump_manpage_list(input_list, "Input Parameters", out)
dump_manpage_list(inout_list, "Input/Output Parameters", out)
dump_manpage_list(output_list, "Output Parameters", out)
# Add the custom notes (specified in e.g. pt2pt_api.txt) as is.
if 'notes' in func:
for l in func['notes']:
out.append(l)
out.append("")
if 'replace' in func:
if RE.match(r'\s*(deprecated|removed)', func['replace'], re.IGNORECASE):
out.append(".N %s" % RE.m.group(1).capitalize())
else:
print("Missing reasons in %s .replace" % func['name'], file=sys.stderr)
if RE.search(r'with\s+(\w+)', func['replace']):
out.append(" The replacement for this routine is '%s'." % RE.m.group(1))
out.append("")
# document info keys
if G.hints and func['name'] in G.hints:
print("Got info hints in %s" % func['name'])
out.append("Info hints:")
for a in G.hints[func['name']]:
out.append(". %s - %s, default = %s." % (a['name'], a['type'], a['default']))
dump_description(a['description'])
out.append("")
for note in func['_docnotes']:
out.append(".N %s" % note)
if note == "Fortran":
has = {}
for p in func['c_parameters']:
if p['kind'] == "status":
if p['length']:
has['FortStatusArray'] = 1
else:
has['FortranStatus'] = 1
for k in has:
out.append(".N %s" % k)
out.append("")
if 'notes2' in func:
for l in func['notes2']:
out.append(l)
out.append("")
if '_skip_err_codes' not in func:
out.append(".N Errors")
out.append(".N MPI_SUCCESS")
for err in sorted (G.err_codes.keys()):
out.append(".N %s" % (err))
out.append(".N MPI_ERR_OTHER")
out.append("")
if 'seealso' in func:
out.append(".seealso: %s" % func['seealso'])
out.append("D*/")
out.append("")
def dump_manpage_list(list, header, out):
count = len(list)
if count == 0:
return
out.append("%s:" % header)
if count == 1:
p = list[0]
out.append(". %s - %s" % (p['name'], p['desc']))
else:
for i, p in enumerate(list):
lead = "."
if i == 0:
lead = "+"
elif i == count - 1:
lead = "-"
out.append("%s %s - %s" % (lead, p['name'], p['desc']))
out.append("")
def get_function_internal_prototype(func_decl):
func_decl = re.sub(r'MPI(X?)_([a-zA-Z0-9_]*\()', r'internal\1_\2', func_decl, 1)
func_decl = "static " + func_decl
return func_decl
# ---- the function part ----
def dump_function_internal(func, kind):
"""Appends to G.out array the MPI function implementation."""
func_name = get_function_name(func, func['_is_large']);
s = get_declare_function(func, func['_is_large'])
if kind == "polymorph":
(extra_param, extra_arg) = get_polymorph_param_and_arg(func['polymorph'])
s = re.sub(r'MPI(X?)_([a-zA-Z0-9_]*\()', r'MPI\1I_\2', s, 1)
s = re.sub(r'\)$', ', '+extra_param+')', s)
# prepare for the latter body of routines calling MPIR impl
RE.search(r'(\w+)$', extra_param)
func['_impl_arg_list'].append(RE.m.group(1))
func['_impl_param_list'].append(extra_param)
else:
G.out.append("")
s = get_function_internal_prototype(s)
dump_line_with_break(s)
G.out.append("{")
G.out.append("INDENT")
if "impl" in func and func['impl'] == "direct":
# e.g. MPI_Aint_add
dump_function_direct(func)
elif kind == 'call-polymorph':
(extra_param, extra_arg) = get_polymorph_param_and_arg(func['polymorph'])
repl_name = re.sub(r'MPI(X?)_', r'MPI\1I_', func_name, 1)
repl_args = get_function_args(func) + ', ' + extra_arg
repl_call = "mpi_errno = %s(%s);" % (repl_name, repl_args)
dump_function_replace(func, repl_call)
else:
dump_function_normal(func)
G.out.append("DEDENT")
G.out.append("}")
if func['_has_poly'] and func['_poly_impl'] != "separate" and func['_is_large']:
pass
else:
if "decl" in func:
push_impl_decl(func, func['decl'])
elif 'impl' in func:
if RE.match(r'topo_fns->', func['impl']):
push_impl_decl(func)
elif 'body' in func:
pass
else:
push_impl_decl(func)
# ref. dump_{qmpi,abi}_wrappers, call the internal function
def get_static_call_internal(func, is_large):
func_name = get_function_name(func, is_large)
if 'replace' in func and 'body' not in func:
m = re.search(r'with\s+(MPI_\w+)', func['replace'])
func_name = m.group(1)
static_call = re.sub(r'MPI(X?)_', r'internal\1_', func_name, 1)
static_call = static_call + "(" + get_function_args(func) + ")"
return static_call
def check_large_parameters(func):
if not func['_has_poly']:
func['_poly_impl'] = "separate"
return
# Set func['_poly_impl']
if 'poly_impl' in func:
# always prefer explicit .poly_impl directive
func['_poly_impl'] = func['poly_impl']
elif 'code-large_count' in func:
# { -- large_count } code block exist
func['_poly_impl'] = "separate"
elif func['dir'] == 'datatype':
func['_poly_impl'] = "separate"
else:
func['_poly_impl'] = "use-aint"
# Gather large parameters. Potentially we need copy in & out
func['_poly_in_list'] = []
func['_poly_in_arrays'] = []
func['_poly_out_list'] = []
func['_poly_inout_list'] = []
func['_poly_need_filter'] = False
for p in func['c_parameters']:
if RE.match(r'POLY', p['kind']):
if p['param_direction'] == 'out':
func['_poly_out_list'].append(p)
func['_poly_need_filter'] = True
elif p['param_direction'] == 'inout':
# MPI_{Pack,Unpack}[_external]
func['_poly_inout_list'].append(p)
func['_poly_need_filter'] = True
elif p['length']:
func['_poly_in_arrays'].append(p)
func['_poly_need_filter'] = True
elif p['kind'] == "POLYFUNCTION":
# we'll have separate versions that doesn't need cast or swap
pass
else:
func['_poly_in_list'].append(p)
def dump_function_normal(func):
G.out.append("int mpi_errno = MPI_SUCCESS;")
if '_handle_ptr_list' in func:
for p in func['_handle_ptr_list']:
dump_handle_ptr_var(func, p)
if '_comm_from_request' in func:
G.out.append("MPIR_Comm *comm_ptr = NULL;")
if 'code-declare' in func:
for l in func['code-declare']:
G.out.append(l)
if not '_skip_initcheck' in func:
G.out.append("")
if func['dir'] == "mpit":
G.err_codes['MPI_T_ERR_NOT_INITIALIZED'] = 1
G.out.append("MPIT_ERRTEST_MPIT_INITIALIZED();")
else:
G.out.append("MPIR_ERRTEST_INITIALIZED_ORDIE();")
if not '_skip_global_cs' in func:
G.out.append("")
if func['dir'] == 'mpit':
G.out.append("MPIR_T_THREAD_CS_ENTER();")
else:
G.out.append("MPID_THREAD_CS_ENTER(GLOBAL, MPIR_THREAD_GLOBAL_ALLFUNC_MUTEX);")
G.out.append("MPIR_FUNC_TERSE_ENTER;")
if '_handle_ptr_list' in func:
G.out.append("")
G.out.append("#ifdef HAVE_ERROR_CHECKING")
G.out.append("{")
G.out.append(" MPID_BEGIN_ERROR_CHECKS;")
G.out.append(" {")
G.out.append("INDENT")
G.out.append("INDENT")
for p in func['_handle_ptr_list']:
dump_validate_handle(func, p)
G.out.append("DEDENT")
G.out.append("DEDENT")
G.out.append(" }")
G.out.append(" MPID_END_ERROR_CHECKS;")
G.out.append("}")
G.out.append("#endif \x2f* HAVE_ERROR_CHECKING */")
G.out.append("")
for p in func['_handle_ptr_list']:
dump_convert_handle(func, p)
if 'code-handle_ptr' in func:
for l in func['code-handle_ptr']:
G.out.append(l)
if func['_need_validation']:
G.out.append("")
G.out.append("#ifdef HAVE_ERROR_CHECKING")
G.out.append("{")
G.out.append(" MPID_BEGIN_ERROR_CHECKS;")
G.out.append(" {")
G.out.append("INDENT")
G.out.append("INDENT")
if '_handle_ptr_list' in func:
for p in func['_handle_ptr_list']:
dump_validate_handle_ptr(func, p)
if '_validation_list' in func:
for t in func['_validation_list']:
dump_validation(func, t)
if 'code-error_check' in func:
for l in func['code-error_check']:
if RE.match(r'CHECK(ENUM|MASK):\s*(\w+),\s*(\w+),\s*(.+)', l):
dump_CHECKENUM(RE.m.group(2), RE.m.group(3), RE.m.group(4), RE.m.group(1))
else:
G.out.append(l)
G.out.append("DEDENT")
G.out.append("DEDENT")
G.out.append(" }")
G.out.append(" MPID_END_ERROR_CHECKS;")
G.out.append("}")
G.out.append("#endif \x2f* HAVE_ERROR_CHECKING */")
G.out.append("")
check_early_returns(func)
G.out.append("")
# ----
def dump_body_of_routine():
do_threadcomm = False
if RE.search(r'threadcomm', func['extra'], re.IGNORECASE):
do_threadcomm = True
G.out.append("#ifdef ENABLE_THREADCOMM")
dump_if_open("comm_ptr->threadcomm")
dump_body_threadcomm(func)
dump_else_open()
G.out.append("#endif")
dump_else_close()
if 'body' in func:
if func['_is_large'] and func['_poly_impl'] == "separate":
if 'code-large_count' not in func:
raise Exception("%s missing large count code block." % func['name'])
for l in func['code-large_count']:
G.out.append(l)
else:
for l in func['body']:
G.out.append(l)
elif 'impl' in func:
if RE.match(r'mpid', func['impl'], re.IGNORECASE):
dump_body_impl(func, "mpid")
elif RE.match(r'topo_fns->(\w+)', func['impl'], re.IGNORECASE):
dump_body_topo_fns(func, RE.m.group(1))
else:
print("Error: unhandled special impl: [%s]" % func['impl'])
elif func['dir'] == 'coll':
dump_body_coll(func)
else:
dump_body_impl(func, "mpir")
if do_threadcomm:
dump_if_close()
# ----
G.out.append("/* ... body of routine ... */")
# hijack MPIX_EQUAL
if re.match(r'mpi_(all)?reduce$', func['name'], re.IGNORECASE):
dump_if_open("op == MPIX_EQUAL")
dump_body_reduce_equal(func)
G.out.append("goto fn_exit;")
dump_if_close()
if func['_is_large'] and 'code-large_count' not in func:
# BIG but internally is using MPI_Aint
impl_args_save = copy.copy(func['_impl_arg_list'])
dump_if_open("sizeof(MPI_Count) == sizeof(MPI_Aint)")
# Same size, just casting the _impl_arg_list
add_poly_impl_cast(func)
dump_body_of_routine()
dump_else()
# Need filter to check limits and potentially swap args
func['_impl_arg_list'] = copy.copy(impl_args_save)
G.out.append("/* MPI_Count is bigger than MPI_Aint */")
dump_poly_pre_filter(func)
dump_body_of_routine()
dump_poly_post_filter(func)
dump_if_close()
elif not func['_is_large'] and func['_has_poly'] and func['_poly_impl'] != "separate":
# SMALL but internally is using MPI_Aint
dump_poly_pre_filter(func)
dump_body_of_routine()
dump_poly_post_filter(func)
else:
# normal
dump_body_of_routine()
G.out.append("/* ... end of body of routine ... */")
# ----
G.out.append("")
G.out.append("fn_exit:")
for l in func['_clean_up']:
G.out.append(l)
G.out.append("MPIR_FUNC_TERSE_EXIT;")
if not '_skip_global_cs' in func:
if func['dir'] == 'mpit':
G.out.append("MPIR_T_THREAD_CS_EXIT();")
else:
G.out.append("MPID_THREAD_CS_EXIT(GLOBAL, MPIR_THREAD_GLOBAL_ALLFUNC_MUTEX);")
G.out.append("return mpi_errno;")
G.out.append("")
G.out.append("fn_fail:")
if func['dir'] == 'mpit':
# MPI_T always return the mpi_errno
pass
else:
dump_mpi_fn_fail(func)
G.out.append("goto fn_exit;")
def replace_impl_arg_list(arg_list, old, new):
for i in range(len(arg_list)):
if arg_list[i] == old:
arg_list[i] = new
def check_aint_fits(is_large, val):
# only need check when swapping `MPI_Count` to `MPI_Aint`
if is_large:
dump_if_open("%s > MPIR_AINT_MAX" % val)
dump_error("too_big_for_input", "%s", '"%s"' % val)
dump_if_close()
def add_poly_impl_cast(func):
for p in func['_poly_in_list']:
replace_impl_arg_list(func['_impl_arg_list'], p['name'], "(MPI_Aint) " + p['name'])
for p in func['_poly_inout_list'] + func['_poly_out_list'] + func['_poly_in_arrays']:
replace_impl_arg_list(func['_impl_arg_list'], p['name'], "(MPI_Aint *) " + p['name'])
def check_poly_is_aint(func, p):
if func['_is_large']:
return G.MAPS['BIG_C_KIND_MAP'][p['kind']] == "MPI_Aint"
else:
return G.MAPS['SMALL_C_KIND_MAP'][p['kind']] == "MPI_Aint"
def dump_poly_pre_filter(func):
def filter_output():
# internal code will use MPI_Aint
for p in func['_poly_out_list']:
if not check_poly_is_aint(func, p):
G.out.append("MPI_Aint %s_c;" % p['name'])
for p in func['_poly_inout_list']:
if not check_poly_is_aint(func, p):
G.out.append("MPI_Aint %s_c = *%s;" % (p['name'], p['name']))
for p in func['_poly_out_list'] + func['_poly_inout_list']:
if not check_poly_is_aint(func, p):
replace_impl_arg_list(func['_impl_arg_list'], p['name'], "&%s_c" % p['name'])
def filter_array():
if func["_need_coll_v_swap"]:
dump_coll_v_swap(func)
elif func["_need_type_create_swap"]:
dump_type_create_swap(func)
else:
if func['_poly_in_arrays']:
# non-v-collectives should use separate impl
raise Exception("Unhandled POLY-array function %s" % func['name'])
# ----
if func['_is_large']:
for p in func['_poly_in_list']:
if not check_poly_is_aint(func, p):
check_aint_fits(True, p['name'])
for p in func['_poly_inout_list']:
if not check_poly_is_aint(func, p):
check_aint_fits(True, '*' + p['name'])
filter_output()
filter_array()
def dump_poly_post_filter(func):
def filter_output(int_max):
for p in func['_poly_out_list'] + func['_poly_inout_list']:
if not check_poly_is_aint(func, p):
val = p['name'] + "_c"
if int_max:
# Seems in all cases we can return MPI_UNDEFINED rather than
# error.
if True:
dump_if_open("%s > %s" % (val, int_max))
G.out.append("*%s = MPI_UNDEFINED;" % p['name'])
dump_else()
G.out.append("*%s = %s;" % (p['name'], val))
dump_if_close()
else:
dump_if_open("%s > %s" % (val, int_max))
dump_error("too_big_for_output", "%s", "\"%s\"" % val)
dump_if_close()
G.out.append("*%s = %s;" % (p['name'], val))
else:
G.out.append("*%s = %s;" % (p['name'], val))
def filter_array(int_max):
if func["_need_coll_v_swap"]:
dump_coll_v_exit(func)
elif func["_need_type_create_swap"]:
dump_type_create_exit(func)
# ----
int_max = None
if not func['_is_large']:
int_max = "INT_MAX"
filter_output(int_max)
filter_array(int_max)
def dump_type_create_swap(func):
for p in func['_poly_in_arrays']:
n = "count"
if RE.search(r'create_(d|sub)array', func['name'], re.IGNORECASE):
n = "ndims"
new_name = p['name'] + '_c'
G.out.append("MPI_Aint *%s = MPL_malloc(%s * sizeof(MPI_Aint), MPL_MEM_OTHER);" % (new_name, n))
dump_for_open("i", n)
check_aint_fits(func['_is_large'], "%s[i]" % p['name'])
G.out.append("%s[i] = %s[i];" % (new_name, p['name']))
dump_for_close()
replace_impl_arg_list(func['_impl_arg_list'], p['name'], new_name)
def dump_type_create_exit(func):
for p in func['_poly_in_arrays']:
G.out.append("MPL_free(%s_c);" % p['name'])
def push_impl_decl(func, impl_name=None):
if not impl_name:
impl_name = re.sub(r'^MPIX?_', 'MPIR_', func['name']) + "_impl"
if func['_is_large']:
# add suffix to differentiate
impl_name = re.sub(r'_impl$', '_large_impl', impl_name)
if func['_impl_param_list']:
params = ', '.join(func['_impl_param_list'])
if func['dir'] == 'coll':
# block collective use an extra errflag
if not RE.match(r'MPI_(I.*|Neighbor.*|.*_init)$', func['name']):
params = params + ", MPIR_Errflag_t errflag"
else:
params="void"
if func['dir'] == 'coll':
# collective also dump MPIR_Xxx(...)
mpir_name = re.sub(r'^MPIX?_', 'MPIR_', func['name'])
G.impl_declares.append("int %s(%s);" % (mpir_name, params))
# dump MPIR_Xxx_impl(...)
G.impl_declares.append("int %s(%s);" % (impl_name, params))
def push_threadcomm_impl_decl(func):
impl_name = re.sub(r'^mpix?_(comm_)?', 'MPIR_Threadcomm_', func['name'].lower())
impl_name += '_impl'
params = ', '.join(func['_impl_param_list'])
G.impl_declares.append("int %s(%s);" % (impl_name, params))
def dump_CHECKENUM(var, errname, t, type="ENUM"):
val_list = t.split()
if type == "ENUM":
cond_list = []
for val in val_list:
cond_list.append("%s != %s" % (var, val))
cond = ' && '.join(cond_list)
elif type == "MASK":
cond = "%s != (%s & (%s))" % (var, var, ' | '.join(val_list))
dump_if_open(cond)
G.out.append("MPIR_ERR_SET1(mpi_errno, MPI_ERR_ARG, \"**%s\", \"**%s %%d\", %s);" % (errname, errname, var))
G.out.append("goto fn_fail;")
dump_if_close()
def dump_body_coll(func):
# collectives call MPIR_Xxx
mpir_name = re.sub(r'^MPIX?_', 'MPIR_', func['name'])
args = ", ".join(func['_impl_arg_list'])
if RE.match(r'MPI_(I.*|.*_init)$', func['name'], re.IGNORECASE):
# non-blocking collectives
G.out.append("MPIR_Request *request_ptr = NULL;")
dump_line_with_break("mpi_errno = %s(%s);" % (mpir_name, args))
dump_error_check("")
G.out.append("if (!request_ptr) {")
G.out.append(" request_ptr = MPIR_Request_create_complete(MPIR_REQUEST_KIND__COLL);")
G.out.append("}")
G.out.append("*request = request_ptr->handle;")
elif RE.match(r'mpi_neighbor_', func['name'], re.IGNORECASE):
dump_line_with_break("mpi_errno = %s(%s);" % (mpir_name, args))
dump_error_check("")
else:
# blocking collectives
dump_line_with_break("mpi_errno = %s(%s, MPIR_ERR_NONE);" % (mpir_name, args))
dump_error_check("")
def dump_coll_v_swap(func):
# -- wrappers to make code cleaner
def replace_arg(old, new):
replace_impl_arg_list(func['_impl_arg_list'], old, new)
def check_fit(val):
check_aint_fits(func['_is_large'], val)
# -- swapping routines
def allocate_tmp_array(n):
G.out.append("MPI_Aint *tmp_array = MPL_malloc(%s * sizeof(MPI_Aint), MPL_MEM_OTHER);" % n)
def swap_one(n, counts):
dump_for_open("i", n)
check_fit("%s[i]" % counts)
G.out.append("tmp_array[i] = %s[i];" % counts)
dump_for_close()
def swap_next(base, n, counts):
dump_for_open("i", n)
check_fit("%s[i]" % counts)
G.out.append("tmp_array[%s + i] = %s[i];" % (base, counts))
dump_for_close()
# -------------------------
def get_comm_size_n(intra_only):
G.out.append("int n;")
if intra_only:
G.out.append("n = comm_ptr->local_size;")
else:
cond = "(comm_ptr->comm_kind == MPIR_COMM_KIND__INTERCOMM)"
G.out.append("n = %s ? comm_ptr->remote_size : comm_ptr->local_size;" % cond)
G.out.append("#ifdef ENABLE_THREADCOMM")
dump_if_open("comm_ptr->threadcomm")
G.out.append("int intracomm_size = comm_ptr->local_size;")
G.out.append("n = comm_ptr->threadcomm->rank_offset_table[intracomm_size - 1];")
dump_if_close()
G.out.append("#endif")
def get_comm_rank_r():
G.out.append("int r;")
G.out.append("r = comm_ptr->rank;")
G.out.append("#ifdef ENABLE_THREADCOMM")
dump_if_open("comm_ptr->threadcomm")
G.out.append("r = MPIR_THREADCOMM_TID_TO_RANK(comm_ptr->threadcomm, MPIR_threadcomm_get_tid(comm_ptr->threadcomm));")
dump_if_close()
G.out.append("#endif")
# -------------------------
if RE.match(r'mpi_i?neighbor_', func['name'], re.IGNORECASE):
# neighborhood collectives
G.out.append("int indegree, outdegree, weighted;")
G.out.append("mpi_errno = MPIR_Topo_canon_nhb_count(comm_ptr, &indegree, &outdegree, &weighted);")
if RE.search(r'allgatherv', func['name'], re.IGNORECASE):
allocate_tmp_array("indegree * 2")
swap_one("indegree", "recvcounts")
swap_next("indegree", "indegree", "displs")
replace_arg('recvcounts', 'tmp_array')
replace_arg('displs', 'tmp_array + indegree')
elif RE.search(r'alltoallv', func['name'], re.IGNORECASE):
allocate_tmp_array("(outdegree + indegree) * 2")
swap_one("outdegree", "sendcounts")
swap_next("outdegree", "outdegree", "sdispls")
swap_next("outdegree * 2", "indegree", "recvcounts")
swap_next("outdegree * 2 + indegree", "indegree", "rdispls")
replace_arg('sendcounts', 'tmp_array')
replace_arg('sdispls', 'tmp_array + outdegree')
replace_arg('recvcounts', 'tmp_array + outdegree * 2')
replace_arg('rdispls', 'tmp_array + outdegree * 2 + indegree')
else: # neighbor_alltoallw
allocate_tmp_array("(outdegree + indegree)")
swap_one("outdegree", "sendcounts")
swap_next("outdegree", "indegree", "recvcounts")
replace_arg('sendcounts', 'tmp_array')
replace_arg('recvcounts', 'tmp_array + outdegree')
# classical collectives
elif RE.match(r'(mpi_i?reduce_scatter(_init)?\b)', func['name'], re.IGNORECASE):
get_comm_size_n(True) # intracomm-only
allocate_tmp_array("n")
swap_one("n", "recvcounts")
replace_arg('recvcounts', 'tmp_array')
else:
get_comm_size_n(False) # not intra-only
if RE.search(r'alltoall[vw]', func['name'], re.IGNORECASE):
allocate_tmp_array("n * 4")
dump_if_open("sendbuf != MPI_IN_PLACE")
swap_one("n", "sendcounts")
swap_next("n", "n", "sdispls")
dump_if_close()
swap_next("n * 2", "n", "recvcounts")
swap_next("n * 3", "n", "rdispls")
replace_arg('sendcounts', 'tmp_array')
replace_arg('sdispls', 'tmp_array + n')
replace_arg('recvcounts', 'tmp_array + n * 2')
replace_arg('rdispls', 'tmp_array + n * 3')
elif RE.search(r'allgatherv', func['name'], re.IGNORECASE):
allocate_tmp_array("n * 2")
swap_one("n", "recvcounts")
swap_next("n", "n", "displs")
replace_arg('recvcounts', 'tmp_array')
replace_arg('displs', 'tmp_array + n')
else:
allocate_tmp_array("n * 2")
if RE.search(r'scatterv', func['name'], re.IGNORECASE):
counts = "sendcounts"
else: # gatherv
counts = "recvcounts"
# only root need the v-array
get_comm_rank_r()
cond_intra = "comm_ptr->comm_kind == MPIR_COMM_KIND__INTRACOMM"
cond_inter = "comm_ptr->comm_kind == MPIR_COMM_KIND__INTERCOMM"
cond_a = cond_intra + " && r == root"
cond_b = cond_inter + " && root == MPI_ROOT"
cond = "(%s) || (%s)" % (cond_a, cond_b)
dump_if_open(cond)
swap_one("n", counts)
swap_next("n", "n", "displs")
dump_if_close()
replace_arg(counts, 'tmp_array')
replace_arg('displs', 'tmp_array + n')
def dump_coll_v_exit(func):
G.out.append("MPL_free(tmp_array);")
def dump_body_topo_fns(func, method):
comm_ptr = func['_has_comm'] + "_ptr"
dump_if_open("%s->topo_fns && %s->topo_fns->%s" % (comm_ptr, comm_ptr, method))
# The extension will output `MPI_Comm *` rather than `MPIR_Comm **`
args = re.sub(r'&(\w+)_ptr', r'\1', ", ".join(func['_impl_arg_list']))
dump_line_with_break("mpi_errno = %s->topo_fns->%s(%s);" % (comm_ptr, method, args))
dump_error_check("")
dump_else()
dump_body_impl(func)
dump_if_close()
def dump_body_impl(func, prefix='mpir'):
# mpi_errno = MPIR_Xxx_impl(...);
if func['_has_handle_out']:
for p in func['_has_handle_out']:
(name, kind) = (p['name'], p['kind'])
mpir_type = G.handle_mpir_types[kind]
G.out.append("%s *%s_ptr ATTRIBUTE((unused)) = NULL;" % (mpir_type, name))
if kind in G.handle_NULLs:
G.out.append("*%s = %s;" % (name, G.handle_NULLs[kind]))
elif RE.match(r'mpi_type_', func['name'], re.IGNORECASE):
p = func['c_parameters'][-1]
if p['kind'] == "DATATYPE" and p['param_direction'] == 'out':
# check it is not a datatype array (MPI_Type_get_contents)
if not p['length']:
G.out.append("*%s = MPI_DATATYPE_NULL;" % p['name'])
impl = func['name']
if func['_is_large'] and func['_poly_impl'] == "separate":
impl += '_large'
if prefix == 'mpid':
impl = re.sub(r'^MPIX?_', 'MPID_', impl)
else:
impl = re.sub(r'^MPIX?_', 'MPIR_', impl) + "_impl"
args = ", ".join(func['_impl_arg_list'])
dump_line_with_break("mpi_errno = %s(%s);" % (impl, args))
dump_error_check("")
if func['_has_handle_out']:
# assuming we are creating new handle(s)
for p in func['_has_handle_out']:
(name, kind) = (p['name'], p['kind'])
dump_if_open("%s_ptr" % name)
if name == 'win':
G.out.append("/* Initialize a few fields that have specific defaults */")
G.out.append("win_ptr->name[0] = 0;")
G.out.append("win_ptr->errhandler = 0;")
G.out.append("MPIR_OBJ_PUBLISH_HANDLE(*%s, %s_ptr->handle);" % (name, name))
dump_if_close()
elif '_has_handle_inout' in func:
# assuming we the func is free the handle
p = func['_has_handle_inout']
kind = p['kind']
if kind in G.handle_NULLs:
G.out.append("*%s = %s;" % (p['name'], G.handle_NULLs[p['kind']]))
else:
print("Not sure how to handle inout %s" % p['name'], file=sys.stderr)
def dump_body_threadcomm(func):
# return MPIR_Threadcomm_Xxx(...);
impl = func['name']
if func['_is_large'] and func['_poly_impl'] == "separate":
impl += '_large'
impl += '_impl'
impl = re.sub(r'^mpix?_(comm_)?', 'MPIR_Threadcomm_', impl.lower())
args = ", ".join(func['_impl_arg_list'])
if RE.match(r'.*request_ptr$', args):
G.out.append("MPIR_Request *request_ptr = NULL;")
elif RE.match(r'.*newcomm_ptr$', args):
G.out.append("MPIR_Comm *newcomm_ptr = NULL;")
dump_line_with_break("mpi_errno = %s(%s);" % (impl, args))
dump_error_check("")
if RE.match(r'.*request_ptr$', args):
G.out.append("*request = request_ptr->handle;")
elif RE.match(r'.*newcomm_ptr$', args):
G.out.append("*newcomm = newcomm_ptr->handle;")
push_threadcomm_impl_decl(func)
def dump_body_reduce_equal(func):
impl = func['name']
impl = re.sub(r'^MPI_', 'MPIR_', impl)
impl += '_equal'
args = ", ".join(func['_impl_arg_list'])
args = re.sub(r'recvbuf, ', '', args)
args = re.sub(r'op, ', 'recvbuf, ', args)
dump_line_with_break("mpi_errno = %s(%s);" % (impl, args))
dump_error_check("")
def dump_function_replace(func, repl_call):
G.out.append("int mpi_errno = MPI_SUCCESS;")
G.out.append("MPIR_FUNC_TERSE_ENTER;")
G.out.append("")
G.out.append(repl_call)
G.out.append("")
G.out.append("MPIR_FUNC_TERSE_EXIT;")
G.out.append("return mpi_errno;")
def dump_function_direct(func):
for l in func['body']:
G.out.append(l)
# -- fn_fail ----
def dump_mpi_fn_fail(func):
G.out.append("/* --BEGIN ERROR HANDLINE-- */")
if RE.match(r'mpi_(finalized|initialized)', func['name'], re.IGNORECASE):
G.out.append("#ifdef HAVE_ERROR_CHECKING")
cond = "MPIR_Errutil_is_initialized()"
dump_if_open(cond)
s = get_fn_fail_create_code(func)
G.out.append(s)
G.out.append("mpi_errno = MPIR_Err_return_comm(0, __func__, mpi_errno);")
dump_if_close()
G.out.append("#endif")
else:
G.out.append("#ifdef HAVE_ERROR_CHECKING")
s = get_fn_fail_create_code(func)
dump_line_with_break(s)
G.out.append("#endif")
if '_has_comm' in func:
G.out.append("mpi_errno = MPIR_Err_return_comm(%s_ptr, __func__, mpi_errno);" % func['_has_comm'])
elif '_has_win' in func:
G.out.append("mpi_errno = MPIR_Err_return_win(win_ptr, __func__, mpi_errno);")
elif RE.match(r'mpi_session_init', func['name'], re.IGNORECASE):
G.out.append("mpi_errno = MPIR_Err_return_session_init(errhandler_ptr, __func__, mpi_errno);")
elif '_has_session' in func:
G.out.append("mpi_errno = MPIR_Err_return_session(session_ptr, __func__, mpi_errno);")
elif RE.match(r'mpi_comm_create_from_group', func['name'], re.IGNORECASE):
G.out.append("mpi_errno = MPIR_Err_return_comm_create_from_group(errhandler_ptr, __func__, mpi_errno);")
elif '_has_group' in func:
G.out.append("mpi_errno = MPIR_Err_return_group(%s_ptr, __func__, mpi_errno);" % func['_has_group'])
else:
G.out.append("mpi_errno = MPIR_Err_return_comm(0, __func__, mpi_errno);")
G.out.append("/* --END ERROR HANDLING-- */")
def get_fn_fail_create_code(func):
s = "mpi_errno = MPIR_Err_create_code(mpi_errno, MPIR_ERR_RECOVERABLE, __func__, __LINE__, MPI_ERR_OTHER,"
func_name = get_function_name(func, func['_is_large'])
err_name = func_name.lower()
mapping = get_kind_map('C', func['_is_large'])
(fmts, args, err_fmts) = ([], [], [])
fmt_codes = {'RANK': "i", 'TAG': "t", 'COMMUNICATOR': "C", 'ASSERT': "A", 'DATATYPE': "D", 'ERRHANDLER': "E", 'FILE': "F", 'GROUP': "G", 'INFO': "I", 'OPERATION': "O", 'REQUEST': "R", 'WINDOW': "W", 'SESSION': "S", 'KEYVAL': "K", "GREQUEST_CLASS": "x", "STREAM": "x", "ASYNC_THING": "p"}
for p in func['c_parameters']:
kind = p['kind']
name = p['name']
fmt = None
if kind == "STRING" and p['param_direction'] != 'out':
fmt = 's'
elif is_pointer_type(p):
fmt = 'p'
elif kind in fmt_codes:
fmt = fmt_codes[kind]
elif mapping[kind] == "int":
fmt = 'd'
elif mapping[kind] == "MPI_Aint":
fmt = 'L'
elif mapping[kind] == "MPI_Count":
fmt = 'c'
else:
print("Error format [%s] not found" % kind, file=sys.stderr)
fmts.append('%' + fmt)
if fmt == 'L':
args.append("(long long) " + name)
else:
args.append(name)
if RE.match(r'[spdLc]', fmt):
err_fmts.append(name + "=%" + fmt)
else:
err_fmts.append('%' + fmt)
if '_is_abi' not in func:
G.mpi_errnames.append("**%s:%s failed" % (err_name, func_name))
if args:
G.mpi_errnames.append("**%s %s:%s(%s) failed" % (err_name, ' '.join(fmts), func_name, ', '.join(err_fmts)))
if args:
s += " \"**%s\", \"**%s %s\", %s);" % (err_name, err_name, ' '.join(fmts), ', '.join(args))
else:
s += " \"**%s\", 0);" % (err_name)
return s
# -- early returns ----
def check_early_returns(func):
if 'earlyreturn' in func:
early_returns = re.split(r',\s*', func['earlyreturn'])
for kind in early_returns:
if RE.search(r'pt2pt_proc_null', kind, re.IGNORECASE):
dump_early_return_pt2pt_proc_null(func)
if 'code-early_return' in func:
for l in func['code-early_return']:
G.out.append(l)
def dump_early_return_pt2pt_proc_null(func):
check_rank, has_request, has_message, has_status, has_flag = '', '', '', '', ''
for p in func['c_parameters']:
kind = p['kind']
name = p['name']
if kind.startswith("RANK"):
check_rank = name
elif p['param_direction'] == 'out':
if kind == "REQUEST":
has_request = name
elif kind == "MESSAGE":
has_message = name
elif kind == "STATUS":
has_status = name
elif name == "flag":
has_flag = name
G.out.append("/* Return immediately for dummy process */")
G.out.append("if (unlikely(%s == MPI_PROC_NULL)) {" % check_rank)
G.out.append("INDENT")
if has_request:
request_kind = ''
if RE.search(r'mpi_.*(send|recv|probe)$', func['name'], re.IGNORECASE):
a = RE.m.group(1)
request_kind = "MPIR_REQUEST_KIND__" + a.upper()
elif RE.search(r'mpi_r(put|get|accumulate|get_accumulate)$', func['name'], re.IGNORECASE):
request_kind = "MPIR_REQUEST_KIND__RMA"
else:
raise Exception("Unexpected %s for pt2pt_proc_null" % func['name'])
G.out.append("MPIR_Request *lw_req = MPIR_Request_create_complete(%s);" % request_kind)
G.out.append("MPIR_ERR_CHKANDSTMT(lw_req == NULL, mpi_errno, MPIX_ERR_NOREQ, goto fn_fail,")
G.out.append(" \"**nomemreq\");")
G.out.append("*request = lw_req->handle;")
if has_message:
G.out.append("*message = MPI_MESSAGE_NO_PROC;")
if has_status:
G.out.append("MPIR_Status_set_procnull(status);")
if has_flag:
G.out.append("*flag = TRUE;")
G.out.append("goto fn_exit;")
G.out.append("DEDENT")
G.out.append("}")
# -- validations (complex, sorry) ----
def dump_handle_ptr_var(func, p):
(kind, name) = (p['kind'], p['name'])
if kind == "REQUEST" and p['length']:
G.out.append("MPIR_Request *request_ptr_array[MPIR_REQUEST_PTR_ARRAY_SIZE];")
G.out.append("MPIR_Request **request_ptrs = request_ptr_array;")
else:
mpir = G.handle_mpir_types[kind]
G.out.append("%s *%s_ptr ATTRIBUTE((unused)) = NULL;" % (mpir, name))
def dump_validate_handle(func, p):
func_name = func['name']
(kind, name) = (p['kind'], p['name'])
if '_pointer' in p:
name = "*" + p['name']
G.out.append("MPIR_ERRTEST_ARGNULL(%s, \"%s\", mpi_errno);" % (p['name'], p['name']))
if kind == "COMMUNICATOR":
if name == 'peer_comm':
# intercomm_create only checks peer_comm if it is local_leader
pass
elif 'can_be_null' in p:
dump_if_open("%s != MPI_COMM_NULL" % name)
G.out.append("MPIR_ERRTEST_COMM(%s, mpi_errno);" % name)
dump_if_close()
else:
G.err_codes['MPI_ERR_COMM'] = 1
G.out.append("MPIR_ERRTEST_COMM(%s, mpi_errno);" % name)
elif kind == "GROUP":
G.err_codes['MPI_ERR_GROUP'] = 1
G.out.append("MPIR_ERRTEST_GROUP(%s, mpi_errno);" % name)
elif kind == "WINDOW":
G.err_codes['MPI_ERR_WIN'] = 1
if 'can_be_null' in p:
dump_if_open("%s != MPI_WIN_NULL" % name)
G.out.append("MPIR_ERRTEST_WIN(%s, mpi_errno);" % name)
dump_if_close()
else:
G.out.append("MPIR_ERRTEST_WIN(%s, mpi_errno);" % name)
elif kind == "ERRHANDLER":
G.err_codes['MPI_ERR_ERRHANDLER'] = 1
G.out.append("MPIR_ERRTEST_ERRHANDLER(%s, mpi_errno);" % name)
elif kind == "REQUEST":
G.err_codes['MPI_ERR_REQUEST'] = 1
if p['length']:
dump_if_open("%s > 0" % p['length'])
G.out.append("MPIR_ERRTEST_ARGNULL(%s, \"%s\", mpi_errno);" % (name, name))
dump_for_open('i', p['length'])
if RE.match(r'mpi_startall', func['name'], re.IGNORECASE):
G.out.append("MPIR_ERRTEST_REQUEST(%s[i], mpi_errno);" % name)
else:
G.out.append("MPIR_ERRTEST_ARRAYREQUEST_OR_NULL(%s[i], i, mpi_errno);" % name)
dump_for_close()
dump_if_close()
else:
if 'can_be_null' in p:
G.out.append("MPIR_ERRTEST_REQUEST_OR_NULL(%s, mpi_errno);" % name)
else:
G.out.append("MPIR_ERRTEST_REQUEST(%s, mpi_errno);" % name)
elif kind == "MESSAGE":
G.err_codes['MPI_ERR_REQUEST'] = 1
G.out.append("MPIR_ERRTEST_REQUEST(%s, mpi_errno);" % name)
elif kind == "DATATYPE":
G.err_codes['MPI_ERR_TYPE'] = 1
if 'can_be_null' in p:
dump_if_open("%s != MPI_DATATYPE_NULL" % name)
G.out.append("MPIR_ERRTEST_DATATYPE(%s, \"%s\", mpi_errno);" % (name, name))
dump_if_close()
else:
G.out.append("MPIR_ERRTEST_DATATYPE(%s, \"%s\", mpi_errno);" % (name, name))
elif kind == "OP":
G.err_codes['MPI_ERR_OP'] = 1
G.out.append("MPIR_ERRTEST_OP(%s, mpi_errno);" % name)
elif kind == "INFO":
G.err_codes['MPI_ERR_INFO'] = 1
if 'can_be_null' in p:
G.out.append("MPIR_ERRTEST_INFO_OR_NULL(%s, mpi_errno);" % name)
else:
G.out.append("MPIR_ERRTEST_INFO(%s, mpi_errno);" % name)
def dump_convert_handle(func, p):
(kind, name) = (p['kind'], p['name'])
ptr_name = name + "_ptr"
if '_pointer' in p:
name = "*" + p['name']
if kind == "REQUEST" and p['length']:
G.out.append("if (%s > MPIR_REQUEST_PTR_ARRAY_SIZE) {" % p['length'])
G.out.append(" int nbytes = %s * sizeof(MPIR_Request *);" % p['length'])
G.out.append(" request_ptrs = (MPIR_Request **) MPL_malloc(nbytes, MPL_MEM_OBJECT);")
G.out.append(" if (request_ptrs == NULL) {")
G.out.append(" MPIR_CHKMEM_SETERR(mpi_errno, nbytes, \"request pointers\");")
G.out.append(" goto fn_fail;")
G.out.append(" }")
G.out.append("}")
func['_clean_up'].append("if (%s > MPIR_REQUEST_PTR_ARRAY_SIZE) {" % (p['length']))
func['_clean_up'].append(" MPL_free(request_ptrs);")
func['_clean_up'].append("}")
G.out.append("")
dump_for_open('i', p['length'])
G.out.append("MPIR_Request_get_ptr(%s[i], request_ptrs[i]);" % name)
dump_for_close()
else:
mpir = G.handle_mpir_types[kind]
if "can_be_null" in p:
G.out.append("if (%s != %s) {" % (name, p['can_be_null']))
G.out.append(" %s_get_ptr(%s, %s);" % (mpir, name, ptr_name))
G.out.append("}")
else:
G.out.append("%s_get_ptr(%s, %s);" % (mpir, name, ptr_name))
if kind == "REQUEST" and "_comm_from_request" in func:
G.out.append("if (%s) {" % ptr_name)
G.out.append(" /* Because %s may be freed, save a copy of comm_ptr for use at fn_fail */")
G.out.append(" comm_ptr = %s->comm;" % ptr_name)
G.out.append("}")
def dump_validate_handle_ptr(func, p):
func_name = func['name']
(kind, name) = (p['kind'], p['name'])
ptr_name = name + "_ptr"
if '_pointer' in p:
name = "*" + p['name']
mpir = G.handle_mpir_types[kind]
if kind == "REQUEST" and p['length']:
G.err_codes['MPI_ERR_REQUEST'] = 1
if RE.match(r'mpix?_(test|wait|request_get_status_)all', func['name'], re.IGNORECASE):
# MPI_Testall and MPI_Waitall do pointer conversion inside MPIR_{Test,Wait}all
pass
else:
ptr = p['_ptrs_name'] + '[i]'
dump_for_open('i', p['length'])
if RE.match(r'mpi_startall', func['name'], re.IGNORECASE):
G.out.append("MPIR_Request_valid_ptr(%s, mpi_errno);" % ptr)
dump_error_check("")
G.out.append("MPIR_ERRTEST_STARTREQ(%s, mpi_errno);" % ptr)
G.out.append("MPIR_ERRTEST_STARTREQ_ACTIVE(%s, mpi_errno);" % ptr)
else:
dump_if_open("%s[i] != MPI_REQUEST_NULL" % name)
G.out.append("MPIR_Request_valid_ptr(%s, mpi_errno);" % ptr)
dump_error_check("")
dump_if_close()
dump_for_close()
elif p['kind'] == "MESSAGE":
G.err_codes['MPI_ERR_REQUEST'] = 1
G.out.append("if (%s != MPI_MESSAGE_NO_PROC) {" % name)
G.out.append(" MPIR_Request_valid_ptr(%s, mpi_errno);" % ptr_name)
dump_error_check(" ")
G.out.append(" MPIR_ERR_CHKANDJUMP((%s->kind != MPIR_REQUEST_KIND__MPROBE)," % ptr_name)
G.out.append(" mpi_errno, MPI_ERR_ARG, \"**reqnotmsg\");")
G.out.append("}")
elif kind == "COMMUNICATOR" and RE.match(r'mpi_intercomm_create', func_name, re.IGNORECASE):
# use custom code in func['cond-error_check']
pass
elif kind == "COMMUNICATOR":
G.err_codes['MPI_ERR_COMM'] = 1
if "can_be_null" in p:
dump_if_open("%s != %s" % (name, p['can_be_null']))
G.out.append("MPIR_Comm_valid_ptr(%s, mpi_errno, %s);" % (ptr_name, func['_comm_valid_ptr_flag']))
dump_error_check("")
if "can_be_null" in p:
dump_if_close()
if '_errtest_comm_intra' in func:
G.out.append("MPIR_ERRTEST_COMM_INTRA(%s, mpi_errno);" % ptr_name)
else:
if "can_be_null" in p:
G.out.append("if (%s != %s) {" % (name, p['can_be_null']))
G.out.append(" %s_valid_ptr(%s, mpi_errno);" % (mpir, ptr_name))
dump_error_check(" ")
G.out.append("}")
else:
G.out.append("%s_valid_ptr(%s, mpi_errno);" % (mpir, ptr_name))
dump_error_check("")
if kind == "REQUEST" and RE.match(r'mpi_start', func_name, re.IGNORECASE):
G.out.append("MPIR_ERRTEST_STARTREQ(%s, mpi_errno);" % ptr_name)
G.out.append("MPIR_ERRTEST_STARTREQ_ACTIVE(%s, mpi_errno);" % ptr_name)
if kind == "REQUEST" and RE.match(r'mpi_pready(_range|_list)?$', func_name, re.IGNORECASE):
G.out.append("MPIR_ERRTEST_PREADYREQ(%s, mpi_errno);" % ptr_name)
if kind == "REQUEST" and RE.match(r'mpi_parrived', func_name, re.IGNORECASE):
G.out.append("MPIR_ERRTEST_PARRIVEDREQ(%s, mpi_errno);" % ptr_name)
if kind == "WINDOW" and RE.match(r'mpi_win_shared_query', func_name, re.IGNORECASE):
G.out.append("MPIR_ERRTEST_WIN_NOT_DYNAMIC(win_ptr, mpi_errno);")
if G.handle_error_codes[kind]:
G.err_codes[G.handle_error_codes[kind]] = 1
def dump_validation(func, t):
func_name = func['name']
(kind, name) = (t['kind'], t['name'])
if kind == "RANK":
G.err_codes['MPI_ERR_RANK'] = 1
if '_has_comm' in func:
comm_ptr = func['_has_comm'] + '_ptr'
elif '_has_win' in func:
comm_ptr = "win_ptr->comm_ptr"
else:
raise Exception("dump_validation RANK: missing comm_ptr in %s" % func_name)
if RE.match(r'mpi_(i?m?probe|i?recv|recv_init)$', func_name, re.IGNORECASE) or name == "recvtag":
# allow MPI_ANY_SOURCE, MPI_PROC_NULL
G.out.append("MPIR_ERRTEST_RECV_RANK(%s, %s, mpi_errno);" % (comm_ptr, name))
elif RE.match(r'mpi_(i?sendrecv)', func_name, re.IGNORECASE) and name == "source":
G.out.append("MPIR_ERRTEST_RECV_RANK(%s, %s, mpi_errno);" % (comm_ptr, name))
elif RE.match(r'(dest|target_rank)$', name) or RE.match(r'mpi_win_', func_name, re.IGNORECASE):
# allow MPI_PROC_NULL
G.out.append("MPIR_ERRTEST_SEND_RANK(%s, %s, mpi_errno);" % (comm_ptr, name))
else:
G.out.append("MPIR_ERRTEST_RANK(%s, %s, mpi_errno);" % (comm_ptr, name))
elif kind == "TAG":
G.err_codes['MPI_ERR_TAG'] = 1
if RE.match(r'mpi_(i?m?probe|i?recv|recv_init)$', func_name, re.IGNORECASE) or name == "recvtag":
G.out.append("MPIR_ERRTEST_RECV_TAG(%s, mpi_errno);" % name)
else:
G.out.append("MPIR_ERRTEST_SEND_TAG(%s, mpi_errno);" % name)
elif kind == "ROOT":
if RE.search(r'mpi_comm_', func_name, re.IGNORECASE):
G.out.append("MPIR_ERRTEST_INTRA_ROOT(comm_ptr, root, mpi_errno);")
else:
G.out.append("if (comm_ptr->comm_kind == MPIR_COMM_KIND__INTRACOMM) {")
G.out.append(" MPIR_ERRTEST_INTRA_ROOT(comm_ptr, root, mpi_errno);")
G.out.append("} else {")
G.out.append(" MPIR_ERRTEST_INTER_ROOT(comm_ptr, root, mpi_errno);")
G.out.append("}")
elif kind == "COUNT":
G.err_codes['MPI_ERR_COUNT'] = 1
G.out.append("MPIR_ERRTEST_COUNT(%s, mpi_errno);" % name)
elif kind == "WIN_SIZE":
G.err_codes['MPI_ERR_SIZE'] = 1
G.out.append("MPIR_ERRTEST_WIN_SIZE(%s, mpi_errno);" % name)
elif kind == "WIN_DISPUNIT":
G.err_codes['MPI_ERR_DISP'] = 1
G.out.append("MPIR_ERRTEST_WIN_DISPUNIT(%s, mpi_errno);" % name)
elif kind == "RMADISP":
G.err_codes['MPI_ERR_DISP'] = 1
G.out.append("if (win_ptr->create_flavor != MPI_WIN_FLAVOR_DYNAMIC) {")
G.out.append(" MPIR_ERRTEST_DISP(%s, mpi_errno);" % name)
G.out.append("}")
elif kind == "WINBUFFER":
G.err_codes['MPI_ERR_ARG'] = 1
G.out.append("if (size > 0) {")
G.out.append(" MPIR_ERRTEST_ARGNULL(%s, \"%s\", mpi_errno);" % (name, name))
G.out.append("}")
elif RE.search(r'USERBUFFER', kind):
G.err_codes['MPI_ERR_BUFFER'] = 1
G.err_codes['MPI_ERR_COUNT'] = 1
G.err_codes['MPI_ERR_TYPE'] = 1
p = name.split(',')
if kind == "USERBUFFER-simple":
dump_validate_userbuffer_simple(func, p[0], p[1], p[2])
elif kind == "USERBUFFER-partition":
dump_validate_userbuffer_partition(func, p[0], p[1], p[2], p[3])
elif kind == "USERBUFFER-reduce":
G.err_codes['MPI_ERR_OP'] = 1
dump_validate_userbuffer_reduce(func, p[0], p[1], p[2], p[3], p[4])
elif kind == "USERBUFFER-neighbor":
dump_validate_userbuffer_simple(func, p[0], p[1], p[2])
elif kind.startswith("USERBUFFER-neighbor"):
dump_validate_userbuffer_neighbor_vw(func, kind, p[0], p[1], p[3], p[2])
elif RE.search(r'-[vw]$', kind):
dump_validate_userbuffer_coll(func, kind, p[0], p[1], p[3], p[2])
else:
dump_validate_userbuffer_coll(func, kind, p[0], p[1], p[2], "")
elif RE.match(r'STATUS$', kind):
G.err_codes['MPI_ERR_ARG'] = 1
dump_if_open("%s != MPI_STATUS_IGNORE" % name)
G.out.append("MPIR_ERRTEST_ARGNULL(%s, \"%s\", mpi_errno);" % (name, name))
dump_if_close()
elif RE.match(r'STATUS-length$', kind):
G.err_codes['MPI_ERR_ARG'] = 1
dump_if_open("%s != MPI_STATUSES_IGNORE && %s > 0" % (name, t['length']))
G.out.append("MPIR_ERRTEST_ARGNULL(%s, \"%s\", mpi_errno);" % (name, name))
dump_if_close()
elif RE.match(r'(ARGNULL)$', kind):
if func['dir'] == 'mpit':
G.err_codes['MPI_T_ERR_INVALID'] = 1
G.out.append("MPIT_ERRTEST_ARGNULL(%s);" % name)
else:
G.err_codes['MPI_ERR_ARG'] = 1
G.out.append("MPIR_ERRTEST_ARGNULL(%s, \"%s\", mpi_errno);" % (name, name))
elif RE.match(r'(ARGNULL-length)$', kind):
G.err_codes['MPI_ERR_ARG'] = 1
dump_if_open("%s > 0" % t['length'])
G.out.append("MPIR_ERRTEST_ARGNULL(%s, \"%s\", mpi_errno);" % (name, name))
dump_if_close()
elif RE.match(r'(ARGNEG)$', kind):
if func['dir'] == 'mpit':
G.err_codes['MPI_T_ERR_INVALID'] = 1
G.out.append("MPIT_ERRTEST_ARGNEG(%s);" % name)
else:
G.err_codes['MPI_ERR_ARG'] = 1
G.out.append("MPIR_ERRTEST_ARGNEG(%s, \"%s\", mpi_errno);" % (name, name))
elif RE.match(r'(ARGNONPOS)$', kind):
G.err_codes['MPI_ERR_ARG'] = 1
G.out.append("MPIR_ERRTEST_ARGNONPOS(%s, \"%s\", mpi_errno, MPI_ERR_ARG);" % (name, name))
elif RE.match(r'(TYPE_RMA_ATOMIC|OP_ACC|OP_GACC)$', kind):
a = RE.m.group(1)
G.out.append("MPIR_ERRTEST_%s(%s, mpi_errno);" % (a, name))
elif kind == "datatype_and_ptr":
G.err_codes['MPI_ERR_TYPE'] = 1
dump_validate_datatype(func, name)
elif kind == "op_ptr":
G.err_codes['MPI_ERR_OP'] = 1
dump_validate_op(name, "", False)
elif kind == "op_and_ptr":
G.err_codes['MPI_ERR_OP'] = 1
dump_validate_op(name, "", True)
elif kind == 'KEYVAL':
G.err_codes['MPI_ERR_KEYVAL'] = 1
if RE.match(r'mpi_comm_', func_name, re.IGNORECASE):
G.out.append("MPIR_ERRTEST_KEYVAL(%s, MPIR_COMM, \"%s\", mpi_errno);" % (name, name))
elif RE.match(r'mpi_type_', func_name, re.IGNORECASE):
G.out.append("MPIR_ERRTEST_KEYVAL(%s, MPIR_DATATYPE, \"%s\", mpi_errno);" % (name, name))
elif RE.match(r'mpi_win_', func_name, re.IGNORECASE):
G.out.append("MPIR_ERRTEST_KEYVAL(%s, MPIR_WIN, \"%s\", mpi_errno);" % (name, name))
if not RE.match(r'\w+_(get_attr)', func_name, re.IGNORECASE):
G.out.append("MPIR_ERRTEST_KEYVAL_PERM(%s, mpi_errno);" % name)
elif kind == "group_check_valid_ranks":
G.out.append("if (group_ptr) {")
G.out.append(" mpi_errno = MPIR_Group_check_valid_ranks(group_ptr, ranks, n);")
dump_error_check(" ")
G.out.append("}")
elif kind == "infokey":
G.out.append("MPIR_ERR_CHKANDJUMP((!%s), mpi_errno, MPI_ERR_INFO_KEY, \"**infokeynull\");" % (name))
G.out.append("int keylen = (int) strlen(%s);" % name)
G.out.append("MPIR_ERR_CHKANDJUMP((keylen > MPI_MAX_INFO_KEY), mpi_errno, MPI_ERR_INFO_KEY, \"**infokeylong\");")
G.out.append("MPIR_ERR_CHKANDJUMP((keylen == 0), mpi_errno, MPI_ERR_INFO_KEY, \"**infokeyempty\");")
elif RE.match(r'mpit_(cat|cvar|pvar)_index', kind, re.I):
T = RE.m.group(1).upper()
G.err_codes['MPI_T_ERR_INVALID_INDEX'] = 1
G.out.append("MPIT_ERRTEST_%s_INDEX(%s);" % (T, name))
elif RE.match(r'mpit_(enum|cvar|pvar)_handle', kind, re.I):
T = RE.m.group(1).upper()
G.err_codes['MPI_T_ERR_INVALID_HANDLE'] = 1
G.out.append("MPIT_ERRTEST_%s_HANDLE(%s);" % (T, name))
elif kind == "mpit_enum_item":
# note: name is "enum_, index_"
G.err_codes['MPI_T_ERR_INVALID_ITEM'] = 1
G.out.append("MPIT_ERRTEST_ENUM_ITEM(%s);" % name)
elif kind == "mpit_pvar_session":
G.err_codes['MPI_T_ERR_INVALID_SESSION'] = 1
G.out.append("MPIT_ERRTEST_PVAR_SESSION(%s);" % name)
elif kind == "mpit_pvar_class":
G.err_codes['MPI_T_ERR_INVALID_NAME'] = 1
G.out.append("MPIT_ERRTEST_PVAR_CLASS(%s);" % name)
elif kind == "mpit_event_registration":
G.err_codes['MPI_T_ERR_INVALID_HANDLE'] = 1
G.out.append('MPIT_ERRTEST_EVENT_REG_HANDLE(%s);' % name)
elif kind == "mpit_event_instance":
G.err_codes['MPI_T_ERR_INVALID_HANDLE'] = 1
G.out.append('MPIT_ERRTEST_EVENT_INSTANCE_HANDLE(%s);' % name)
elif kind == "RANK-ARRAY":
# FIXME
pass
elif kind == "DISP-ARRAY":
G.err_codes['MPI_ERR_ARG'] = 1
dump_if_open("%s > 0" % t['length'])
G.out.append("MPIR_ERRTEST_ARGNULL(%s, \"%s\", mpi_errno);" % (name, name))
dump_if_close()
elif kind == "COUNT-ARRAY":
G.err_codes['MPI_ERR_ARG'] = 1
G.err_codes['MPI_ERR_COUNT'] = 1
dump_if_open("%s > 0" % t['length'])
G.out.append("MPIR_ERRTEST_ARGNULL(%s, \"%s\", mpi_errno);" % (name, name))
dump_for_open('i', t['length'])
G.out.append("MPIR_ERRTEST_COUNT(%s[i], mpi_errno);" % name)
dump_for_close()
dump_if_close()
elif kind == "TYPE-ARRAY":
G.err_codes['MPI_ERR_ARG'] = 1
G.err_codes['MPI_ERR_TYPE'] = 1
dump_if_open("%s > 0" % t['length'])
G.out.append("MPIR_ERRTEST_ARGNULL(%s, \"%s\", mpi_errno);" % (name, name))
dump_for_open('i', t['length'])
if re.match(r'mpi_type_create_struct', func_name, re.IGNORECASE):
# MPI_DATATYPE_NULL not allowed
cond = "!HANDLE_IS_BUILTIN(%s[i])" % name
else:
cond = "%s[i] != MPI_DATATYPE_NULL && !HANDLE_IS_BUILTIN(%s[i])" % (name, name)
dump_if_open(cond)
G.out.append("MPIR_Datatype *datatype_ptr;")
G.out.append("MPIR_Datatype_get_ptr(%s[i], datatype_ptr);" % name)
G.out.append("MPIR_Datatype_valid_ptr(datatype_ptr, mpi_errno);")
dump_error_check('')
dump_if_close()
dump_for_close()
dump_if_close()
else:
print("Unhandled validation kind: %s - %s" % (kind, name), file=sys.stderr)
def dump_validate_userbuffer_simple(func, buf, ct, dt):
check_no_op = False
if RE.match(r'mpi_r?get_accumulate', func['name'], re.IGNORECASE) and buf.startswith("origin_"):
check_no_op = True
if check_no_op:
dump_if_open("op != MPI_NO_OP")
G.out.append("MPIR_ERRTEST_COUNT(%s, mpi_errno);" % ct)
if func['dir'] == 'rma':
# RMA doesn't make sense to have zero-count message, always validate datatype
dump_validate_datatype(func, dt)
G.out.append("MPIR_ERRTEST_USERBUFFER(%s, %s, %s, mpi_errno);" % (buf, ct, dt))
else:
dump_if_open("%s > 0" % ct)
dump_validate_datatype(func, dt)
G.out.append("MPIR_ERRTEST_USERBUFFER(%s, %s, %s, mpi_errno);" % (buf, ct, dt))
dump_if_close()
if check_no_op:
dump_if_close()
def dump_validate_userbuffer_partition(func, buf, partitions, ct, dt):
dump_validate_userbuffer_simple(func, buf, ct, dt)
dump_if_open("%s > 0" % ct)
G.out.append("MPIR_ERRTEST_ARGNONPOS(%s, \"%s\", mpi_errno, MPI_ERR_ARG);" % (partitions, partitions))
dump_if_close()
def dump_validate_userbuffer_neighbor_vw(func, kind, buf, ct, dt, disp):
dump_validate_get_topo_size(func)
size = "outdegree"
if RE.search(r'recv', buf):
size = "indegree"
if not RE.search(r'-w$', kind):
dump_validate_datatype(func, dt)
dump_for_open('i', size)
ct += '[i]'
if RE.search(r'-w$', kind):
dt += '[i]'
dump_if_open("%s > 0" % ct)
dump_validate_datatype(func, dt)
dump_if_close()
G.out.append("MPIR_ERRTEST_COUNT(%s, mpi_errno);" % ct)
G.out.append("if (%s[i] == 0) {" % disp)
G.out.append(" MPIR_ERRTEST_USERBUFFER(%s, %s, %s, mpi_errno);" % (buf, ct, dt))
G.out.append("}")
dump_for_close()
def dump_validate_userbuffer_reduce(func, sbuf, rbuf, ct, dt, op):
cond_intra = "comm_ptr->comm_kind == MPIR_COMM_KIND__INTRACOMM"
cond_inter = "comm_ptr->comm_kind == MPIR_COMM_KIND__INTERCOMM"
if RE.match(r'mpi_reduce_local$', func['name'], re.IGNORECASE):
dump_validate_op(op, dt, True)
dump_validate_datatype(func, dt)
G.out.append("if (%s > 0) {" % ct)
G.out.append(" MPIR_ERRTEST_ALIAS_COLL(%s, %s, mpi_errno);" % (sbuf, rbuf))
G.out.append("}")
G.out.append("MPIR_ERRTEST_NAMED_BUF_INPLACE(%s, \"%s\", %s, mpi_errno);" % (sbuf, sbuf, ct))
G.out.append("MPIR_ERRTEST_NAMED_BUF_INPLACE(%s, \"%s\", %s, mpi_errno);" % (rbuf, rbuf, ct))
elif RE.match(r'mpi_i?reduce(_init)?$', func['name'], re.IGNORECASE):
# exclude intercomm MPI_PROC_NULL
G.out.append("if (" + cond_intra + " || root != MPI_PROC_NULL) {")
G.out.append("INDENT")
dump_validate_op(op, dt, True)
dump_validate_datatype(func, dt)
dump_validate_get_comm_rank(func)
cond_a = cond_intra + " && comm_rank == root"
cond_b = cond_inter + " && root == MPI_ROOT"
G.out.append("if ((" + cond_a + ") || (" + cond_b + ")) {")
# test recvbuf
G.out.append(" MPIR_ERRTEST_RECVBUF_INPLACE(%s, %s, mpi_errno);" % (rbuf, ct))
G.out.append(" MPIR_ERRTEST_USERBUFFER(%s, %s, %s, mpi_errno);" % (rbuf, ct, dt))
G.out.append(" if (%s > 0 && %s != MPI_IN_PLACE) {" % (ct, sbuf))
G.out.append(" MPIR_ERRTEST_ALIAS_COLL(%s, %s, mpi_errno);" % (sbuf, rbuf))
G.out.append(" }")
G.out.append("}")
# test sendbuf
cond_a = cond_intra
cond_b = cond_inter + " && root != MPI_ROOT && root != MPI_PROC_NULL"
dump_if_open("(" + cond_a + ") || (" + cond_b + ")")
G.out.append("if (" + cond_inter + ") {")
G.out.append(" MPIR_ERRTEST_SENDBUF_INPLACE(%s, %s, mpi_errno);" % (sbuf, ct))
G.out.append("}")
G.out.append("if (%s > 0 && %s != MPI_IN_PLACE) {" % (ct, sbuf))
G.out.append(" MPIR_ERRTEST_USERBUFFER(%s, %s, %s, mpi_errno);" % (sbuf, ct, dt))
G.out.append("}")
dump_if_close()
G.out.append("DEDENT")
G.out.append("}")
else:
dump_validate_op(op, dt, True)
dump_validate_datatype(func, dt)
(sct, rct) = (ct, ct)
if RE.search(r'reduce_scatter(_init)?$', func['name'], re.IGNORECASE):
dump_validate_get_comm_size(func)
dump_validate_get_comm_rank(func)
G.out.append("int sum = 0;")
dump_for_open('i', 'comm_size')
G.out.append("MPIR_ERRTEST_COUNT(%s[i], mpi_errno);" % ct)
G.out.append("sum += %s[i];" % ct)
dump_for_close()
sct = "sum"
rct = ct + "[comm_rank]"
G.out.append("MPIR_ERRTEST_RECVBUF_INPLACE(%s, %s, mpi_errno);" % (rbuf, rct))
G.out.append("if (" + cond_inter + ") {")
G.out.append(" MPIR_ERRTEST_SENDBUF_INPLACE(%s, %s, mpi_errno);" % (sbuf, sct))
G.out.append("} else if (%s != MPI_IN_PLACE && %s != 0) {" % (sbuf, sct))
G.out.append(" MPIR_ERRTEST_ALIAS_COLL(%s, %s, mpi_errno);" % (sbuf, rbuf))
G.out.append("}")
G.out.append("MPIR_ERRTEST_USERBUFFER(%s, %s, %s, mpi_errno);" % (rbuf, rct, dt))
G.out.append("MPIR_ERRTEST_USERBUFFER(%s, %s, %s, mpi_errno);" % (sbuf, sct, dt))
def dump_validate_userbuffer_coll(func, kind, buf, ct, dt, disp):
func_name = func['name']
cond_intra = "comm_ptr->comm_kind == MPIR_COMM_KIND__INTRACOMM"
cond_inter = "comm_ptr->comm_kind == MPIR_COMM_KIND__INTERCOMM"
inplace = True
if RE.search(r'-noinplace', kind):
inplace = False
check_alias = None
if inplace:
check_alias = cond_intra
with_vw = False
if RE.search(r'-[vw]$', kind):
with_vw = True
dump_validate_get_comm_size(func)
# -- whether the buffer need be checked (or ignored)
check_buf = None
if RE.search(r'_i?(gather|scatter)', func_name, re.IGNORECASE):
dump_validate_get_comm_rank(func)
if inplace:
cond_a = cond_intra + " && %s != MPI_IN_PLACE" % buf
cond_b = cond_inter + " && root != MPI_ROOT && root != MPI_PROC_NULL"
else:
cond_a = cond_intra + " && comm_rank == root"
cond_b = cond_inter + " && root == MPI_ROOT"
check_buf = "(%s) || (%s)" % (cond_a, cond_b)
if inplace:
check_alias = cond_intra + " && comm_rank == root"
elif inplace:
check_buf = " %s != MPI_IN_PLACE" % buf
if check_buf:
G.out.append("if (%s) {" % check_buf)
G.out.append("INDENT")
if not RE.search(r'-w$', kind):
dump_validate_datatype(func, dt)
if with_vw:
dump_for_open('i', 'comm_size')
ct += '[i]'
if RE.search(r'-w$', kind):
dt += '[i]'
dump_if_open("%s > 0" % ct)
dump_validate_datatype(func, dt)
dump_if_close()
# -- test wrong MPI_IN_PLACE
SEND = "SEND"
if RE.search(r'recv', buf):
SEND = "RECV"
if RE.search(r'-noinplace', kind):
G.out.append("MPIR_ERRTEST_%sBUF_INPLACE(%s, %s, mpi_errno);" % (SEND, buf, ct))
else:
G.out.append("if (comm_ptr->comm_kind == MPIR_COMM_KIND__INTERCOMM) {")
G.out.append(" MPIR_ERRTEST_%sBUF_INPLACE(%s, %s, mpi_errno);" % (SEND, buf, ct))
G.out.append("}")
# -- check count && buffer
G.out.append("MPIR_ERRTEST_COUNT(%s, mpi_errno);" % ct)
if disp:
dump_if_open("%s[i] == 0" % disp)
G.out.append("MPIR_ERRTEST_USERBUFFER(%s, %s, %s, mpi_errno);" % (buf, ct, dt))
if disp:
dump_if_close()
if with_vw:
dump_for_close() # i = 0:comm_size
if check_alias:
G.out.append("if (%s) {" % check_alias)
G.out.append("INDENT")
if RE.search(r'i?(alltoall)', func_name, re.IGNORECASE):
cond = "sendtype == recvtype && sendcount == recvcount && sendcount != 0"
if RE.search(r'-v$', kind):
cond = "sendtype == recvtype && sendcounts == recvcounts"
elif RE.search(r'-w$', kind):
cond = "sendtypes == recvtypes && sendcounts == recvcounts"
G.out.append("if (%s) {" % cond)
G.out.append(" MPIR_ERRTEST_ALIAS_COLL(sendbuf, recvbuf, mpi_errno);")
G.out.append("}")
elif RE.search(r'i?(allgather|gather|scatter)(v?)(_init)?$', func_name, re.IGNORECASE):
dump_validate_get_comm_rank(func)
t1, t2 = RE.m.group(1, 2)
(a, b) = ("send", "recv")
if RE.match(r'scatter', t1, re.IGNORECASE):
(a, b) = ("recv", "send")
cond = "sendtype == recvtype && sendcount == recvcount && sendcount != 0"
if t2 == "v":
cond = "sendtype == recvtype && %scount != 0 && %scounts[comm_rank] !=0" % (a, b)
buf2 = "(char *) %sbuf + comm_rank * %scount * %stype_size" % (b, b, b)
if t2 == "v":
buf2 = "(char *) %sbuf + displs[comm_rank] * %stype_size" % (b, b)
G.out.append("if (%s) {" % cond)
G.out.append(" MPI_Aint %stype_size;" % b)
G.out.append(" MPIR_Datatype_get_size_macro(%stype, %stype_size);" % (b, b))
G.out.append(" MPIR_ERRTEST_ALIAS_COLL(%s, %s, mpi_errno);" % (buf, buf2))
G.out.append("}")
else:
raise Exception("dump_validate_userbuffer_coll: inplace alias check: unexpected %s" % func_name)
G.out.append("DEDENT")
G.out.append("}")
if check_buf:
G.out.append("DEDENT")
G.out.append("}")
def dump_validate_datatype(func, dt):
G.out.append("MPIR_ERRTEST_DATATYPE(%s, \"datatype\", mpi_errno);" % dt)
G.out.append("if (!HANDLE_IS_BUILTIN(%s)) {" % dt)
G.out.append(" MPIR_Datatype *datatype_ptr ATTRIBUTE((unused)) = NULL;")
G.out.append(" MPIR_Datatype_get_ptr(%s, datatype_ptr);" % dt)
G.out.append(" MPIR_Datatype_valid_ptr(datatype_ptr, mpi_errno);")
dump_error_check(" ")
if not RE.match(r'mpi_(type_|get_count|pack_external_size|status_set_elements)', func['name'], re.IGNORECASE):
G.out.append(" MPIR_Datatype_committed_ptr(datatype_ptr, mpi_errno);")
dump_error_check(" ")
G.out.append("}")
def dump_validate_op(op, dt, is_coll):
if is_coll:
G.out.append("MPIR_ERRTEST_OP(%s, mpi_errno);" % op)
G.out.append("if (!HANDLE_IS_BUILTIN(%s)) {" % op)
G.out.append(" MPIR_Op *op_ptr = NULL;")
G.out.append(" MPIR_Op_get_ptr(%s, op_ptr);" % op)
G.out.append(" MPIR_Op_valid_ptr(op_ptr, mpi_errno);")
if dt:
G.out.append("} else {")
G.out.append(" mpi_errno = (*MPIR_OP_HDL_TO_DTYPE_FN(%s)) (%s);" % (op, dt))
# check predefined datatype and replace with basic_type if necessary
G.out.append(" if (mpi_errno != MPI_SUCCESS) {")
G.out.append(" MPI_Datatype alt_dt = MPIR_Op_get_alt_datatype(%s, %s);" % (op, dt))
G.out.append(" if (alt_dt != MPI_DATATYPE_NULL) {")
G.out.append(" %s = alt_dt;" % dt)
G.out.append(" mpi_errno = MPI_SUCCESS;")
G.out.append(" }")
G.out.append(" }")
G.out.append("}")
dump_error_check("")
def dump_validate_get_comm_size(func):
if '_got_comm_size' not in func:
if RE.match(r'mpi_i?(reduce_scatter_init|reduce_scatter)\b', func['name'], re.IGNORECASE):
G.out.append("int comm_size = comm_ptr->local_size;")
else:
G.out.append("int comm_size;")
G.out.append("if (comm_ptr->comm_kind == MPIR_COMM_KIND__INTERCOMM) {")
G.out.append(" comm_size = comm_ptr->remote_size;")
G.out.append("} else {")
G.out.append(" comm_size = comm_ptr->local_size;")
G.out.append("}")
G.out.append("#ifdef ENABLE_THREADCOMM")
dump_if_open("comm_ptr->threadcomm")
G.out.append("comm_size = comm_ptr->threadcomm->rank_offset_table[comm_ptr->local_size - 1];")
dump_if_close()
G.out.append("#endif")
func['_got_comm_size'] = 1
def dump_validate_get_comm_rank(func):
if '_got_comm_rank' not in func:
G.out.append("int comm_rank;")
G.out.append("comm_rank = comm_ptr->rank;")
G.out.append("#ifdef ENABLE_THREADCOMM")
dump_if_open("comm_ptr->threadcomm")
G.out.append("comm_rank = MPIR_THREADCOMM_TID_TO_RANK(comm_ptr->threadcomm, MPIR_threadcomm_get_tid(comm_ptr->threadcomm));")
dump_if_close()
G.out.append("#endif")
func['_got_comm_rank'] = 1
def dump_validate_get_topo_size(func):
if '_got_topo_size' not in func:
G.out.append("int indegree, outdegree, weighted;")
G.out.append("mpi_errno = MPIR_Topo_canon_nhb_count(comm_ptr, &indegree, &outdegree, &weighted);")
func['_got_topo_size'] = 1
# ---- supporting routines (reusable) ----
def get_function_args(func):
arg_list = []
for p in func['c_parameters']:
arg_list.append(p['name'])
return ', '.join(arg_list)
def get_declare_function(func, is_large, kind=""):
filter_c_parameters(func)
name = get_function_name(func, is_large)
mapping = get_kind_map('C', is_large)
ret = "int"
if 'return' in func:
ret = mapping[func['return']]
if func['return'] == 'EXTRA_STATE':
ret = 'void *'
params = get_C_params(func, mapping)
s_param = ', '.join(params)
if kind == 'abi':
s_param = re.sub(r'\bMPI_', 'ABI_', s_param)
s = "%s %s(%s)" % (ret, name, s_param)
if kind == 'proto':
if '_pointertag_list' in func:
for t in func['_pointertag_list']:
s += " MPICH_ATTR_POINTER_WITH_TYPE_TAG(%s)" % t
s += " MPICH_API_PUBLIC"
return s
def get_C_params(func, mapping):
param_list = []
for p in func['c_parameters']:
param_list.append(get_C_param(p, func, mapping))
if not len(param_list):
return ["void"]
else:
return param_list
def get_impl_param(func, param):
mapping = get_kind_map('C', func['_is_large'])
s = get_C_param(param, func, mapping)
if RE.match(r'POLY', param['kind']):
if func['_is_large']:
# internally we always use MPI_Aint for poly type
s = re.sub(r'MPI_Count', r'MPI_Aint', s)
elif func['_poly_impl'] != "separate":
# SMALL but internally use MPI_Aint
s = re.sub(r'\bint\b', r'MPI_Aint', s)
return s
def get_polymorph_param_and_arg(s):
# s is a polymorph spec, e.g. "MPIR_Attr_type attr_type=MPIR_ATTR_PTR"
# Note: we assume limit of single extra param for now (which is sufficient)
RE.match(r'^\s*(.+?)\s*(\w+)\s*=\s*(.+)', s)
extra_param = RE.m.group(1) + ' ' + RE.m.group(2)
extra_arg = RE.m.group(3)
return (extra_param, extra_arg)
def get_funcname_from_decl(l):
m = re.match(r'([a-zA-Z0-9_]+|void \*) ([a-zA-Z0-9_]*)\(.*', l);
return m.group(2);
def dump_error_check(sp):
G.out.append("%sif (mpi_errno) {" % sp)
G.out.append("%s goto fn_fail;" % sp)
G.out.append("%s}" % sp)
def dump_if_open(cond):
G.out.append("if (%s) {" % cond)
G.out.append("INDENT")
def dump_else():
G.out.append("DEDENT")
G.out.append("} else {")
G.out.append("INDENT")
def dump_if_close():
G.out.append("DEDENT")
G.out.append("}")
# split "} else {", used along with "#ifdef-#else-#endif"
def dump_else_open():
G.out.append("DEDENT")
G.out.append("} else")
def dump_else_close():
G.out.append("{")
G.out.append("INDENT")
def dump_for_open(idx, count):
G.out.append("for (int %s = 0; %s < %s; %s++) {" % (idx, idx, count, idx))
G.out.append("INDENT")
def dump_for_close():
G.out.append("DEDENT")
G.out.append("}")
def dump_error(errname, errfmt, errargs):
G.out.append("mpi_errno = MPIR_Err_create_code(mpi_errno, MPIR_ERR_RECOVERABLE,")
G.out.append(" __func__, __LINE__, MPI_ERR_OTHER,")
G.out.append(" \"**%s\"," % errname)
G.out.append(" \"**%s %s\", %s);" % (errname, errfmt, errargs))
G.out.append("goto fn_fail;")
def dump_line_with_break(s, tail=''):
tlist = split_line_with_break(s, tail, 100)
G.out.extend(tlist)