34 '''Usage: wrap.py [-fgd] [-i pmpi_init] [-c mpicc_name] [-o file] wrapper.w [...] 35 Python script for creating PMPI wrappers. Roughly follows the syntax of 36 the Argonne PMPI wrapper generator, with some enhancements. 38 -d Just dump function declarations parsed out of mpi.h 39 -f Generate fortran wrappers in addition to C wrappers. 40 -g Generate reentry guards around wrapper functions. 41 -s Skip writing #includes, #defines, and other front-matter (for non-C output). 42 -c exe Provide name of MPI compiler (for parsing mpi.h). Default is \'mpicc\'. 43 -I dir Provide an extra include directory to use when parsing mpi.h. 44 -i pmpi_init Specify proper binding for the fortran pmpi_init function. 45 Default is \'pmpi_init_\'. Wrappers compiled for PIC will guess the 46 right binding automatically (use -DPIC when you compile dynamic libs). 47 -o file Send output to a file instead of stdout. 48 by Todd Gamblin, tgamblin@llnl.gov 50 import tempfile, getopt, subprocess, sys, os, re, StringIO, types, itertools
55 pmpi_init_binding =
"pmpi_init_" 56 output_fortran_wrappers =
False 59 dump_prototypes =
False 62 pmpi_init_bindings = [
"PMPI_INIT",
"pmpi_init",
"pmpi_init_",
"pmpi_init__"]
68 rtypes = [
'int',
'double' ]
71 exclude_strings = [
"c2f",
"f2c",
"typedef" ]
75 begin_decl_re = re.compile(
"(" +
"|".join(rtypes) +
")\s+(MPI_\w+)\s*\(")
76 exclude_re = re.compile(
"|".join(exclude_strings))
77 end_decl_re = re.compile(
"\).*\;")
81 formal_re = re.compile(
86 "(?:\s*\*(?:\s*const)?)*" +
93 f_wrap_suffix =
"_fortran_wrapper" 96 wrapper_includes =
''' 102 #define _EXTERN_C_ extern "C" 103 #else /* __cplusplus */ 105 #endif /* __cplusplus */ 106 #endif /* _EXTERN_C_ */ 108 _EXTERN_C_ void *MPIR_ToPointer(int); 109 #endif // MPICH_HAS_C2F 111 /* For shared libraries, declare these weak and figure out which one was linked 112 based on which init wrapper was called. See mpi_init wrappers. */ 113 #pragma weak pmpi_init 114 #pragma weak PMPI_INIT 115 #pragma weak pmpi_init_ 116 #pragma weak pmpi_init__ 118 _EXTERN_C_ void pmpi_init(MPI_Fint *ierr); 119 _EXTERN_C_ void PMPI_INIT(MPI_Fint *ierr); 120 _EXTERN_C_ void pmpi_init_(MPI_Fint *ierr); 121 _EXTERN_C_ void pmpi_init__(MPI_Fint *ierr); 125 default_modifiers = [
"_EXTERN_C_"]
128 mpi_handle_types =
set([
"MPI_Comm",
"MPI_Errhandler",
"MPI_File",
"MPI_Group",
"MPI_Info",
129 "MPI_Op",
"MPI_Request",
"MPI_Status",
"MPI_Datatype",
"MPI_Win" ])
134 "MPI_Startall" : { 1:0 },
135 "MPI_Testall" : { 1:0, 3:0 },
136 "MPI_Testany" : { 1:0 },
137 "MPI_Testsome" : { 1:0, 4:0 },
138 "MPI_Type_create_struct" : { 3:0 },
139 "MPI_Type_get_contents" : { 6:1 },
140 "MPI_Type_struct" : { 3:0 },
141 "MPI_Waitall" : { 1:0, 2:0 },
142 "MPI_Waitany" : { 1:0 },
143 "MPI_Waitsome" : { 1:0, 4:0 }
147 def find_matching_paren(string, index, lparen='(
', rparen=')
'): 148 """Find the closing paren corresponding to the open paren at <index> 149 in <string>. Optionally, can provide other characters to match on. 150 If found, returns the index of the matching parenthesis. If not found, 153 if not string[index] == lparen:
154 raise ValueError(
"Character at index %d is '%s'. Expected '%s'" 155 % (index, string[index], lparen))
158 while index < len(string)
and count > 0:
159 while index < len(string)
and string[index]
not in (lparen, rparen):
161 if string[index] == lparen:
163 elif string[index] == rparen:
173 """True if a string is something we can index an array with.""" 181 if not hasattr(function,
"did_once"):
183 function.did_once =
True 186 def conversion_prefix(handle_type):
187 if handle_type ==
"MPI_Datatype":
193 def joinlines(list, sep="\n"):
195 return sep.join(list) + sep
200 LBRACE, RBRACE, TEXT, IDENTIFIER = range(4)
203 """Represents tokens; generated from input by lexer and fed to parse().""" 204 def __init__(self, type, value, line=0):
210 return "'%s'" % re.sub(
r'\n',
"\\\\n", self.value)
213 return self.type == type
216 class LineTrackingLexer(object):
217 """Base class for Lexers that keep track of line numbers.""" 218 def __init__(self, lexicon):
220 self.scanner = re.Scanner(lexicon)
222 def make_token(self, type, value):
223 token = Token(type, value, self.line_no)
224 self.line_no += value.count(
"\n")
229 tokens, remainder = self.scanner.scan(text)
231 sys.stderr.write(
"Unlexable input:\n%s\n" % remainder)
236 class OuterRegionLexer(LineTrackingLexer):
238 super(OuterRegionLexer, self).__init__([
239 (
r'{{', self.lbrace),
240 (
r'}}', self.rbrace),
241 (
r'({(?!{)|}(?!})|[^{}])*', self.text)])
242 def lbrace(self, scanner, token):
return self.make_token(LBRACE, token)
243 def rbrace(self, scanner, token):
return self.make_token(RBRACE, token)
244 def text(self, scanner, token):
return self.make_token(TEXT, token)
246 class OuterCommentLexer(OuterRegionLexer):
248 super(OuterRegionLexer, self).__init__([
249 (
r'/\*(.|[\r\n])*?\*/', self.text),
250 (
r'//(.|[\r\n])*?(?=[\r\n])', self.text),
251 (
r'{{', self.lbrace),
252 (
r'}}', self.rbrace),
253 (
r'({(?!{)|}(?!})|/(?![/*])|[^{}/])*', self.text)])
255 class InnerLexer(OuterRegionLexer):
257 super(OuterRegionLexer, self).__init__([
258 (
r'{{', self.lbrace),
259 (
r'}}', self.rbrace),
260 (
r'(["\'])?((?:(?!\1)[^\\]|\\.)*)\1', self.quoted_id),
261 (
r'([^\s]+)', self.identifier),
263 def identifier(self, scanner, token):
return self.make_token(IDENTIFIER, token)
264 def quoted_id(self, scanner, token):
267 return self.make_token(IDENTIFIER, re.sub(
r'^["\'](.*)["\']$',
'\\1', token))
273 class WrapSyntaxError:
274 """Simple Class for syntax errors raised by the wrapper generator (rather than python)""" 277 def syntax_error(msg):
279 sys.stderr.write(
"%s:%d: %s\n" % (cur_filename, 0, msg))
281 sys.stderr.write(
" While handling %s.\n" % cur_function)
282 raise WrapSyntaxError
290 """ This is the very basic class for scopes in the wrapper generator. Scopes 291 are hierarchical and support nesting. They contain string keys mapped 292 to either string values or to macro functions. 293 Scopes also keep track of the particular macro they correspond to (macro_name). 295 def __init__(self, enclosing_scope=None):
297 self.enclosing_scope = enclosing_scope
298 self.macro_name =
None 300 def __getitem__(self, key):
301 if key
in self.map:
return self.map[key]
302 elif self.enclosing_scope:
return self.enclosing_scope[key]
303 else:
raise KeyError(key +
" is not in scope.")
305 def __contains__(self, key):
306 if key
in self.map:
return True 307 elif self.enclosing_scope:
return key
in self.enclosing_scope
310 def __setitem__(self, key, value):
311 self.map[key] = value
313 def include(self, map):
314 """Add entire contents of the map (or scope) to this scope.""" 326 """Descriptor for formal parameters of MPI functions. 327 Doesn't represent a full parse, only the initial type information, 328 name, and array info of the argument split up into strings. 330 def __init__(self, type, pointers, name, array, pos):
332 self.pointers = pointers
338 def setDeclaration(self, decl):
339 """Needs to be called by Declaration to finish initing the arg.""" 342 def isHandleArray(self):
343 """True if this Param represents an array of MPI handle values.""" 344 return (self.decl.name
in mpi_array_calls
345 and self.pos
in mpi_array_calls[self.decl.name])
347 def countParam(self):
348 """If this Param is a handle array, returns the Param that represents the count of its elements""" 349 return self.decl.args[mpi_array_calls[self.decl.name][self.pos]]
352 """True if this Param is one of the MPI builtin handle types.""" 353 return self.type
in mpi_handle_types
356 """True if this Param is an MPI_Status. MPI_Status is handled differently 357 in c2f/f2c calls from the other handle types. 359 return self.type ==
"MPI_Status" 361 def fortranFormal(self):
362 """Prints out a formal parameter for a fortran wrapper.""" 365 if self.type ==
"MPI_Aint" or self.type.endswith(
"_function"):
373 pointers = self.pointers
380 arr = self.array
or '' 381 return "%s %s%s%s" % (ftype, pointers, self.name, arr)
387 arr = self.array
or '' 388 pointers = self.pointers
or '' 389 return "%s%s%s" % (self.type, pointers, arr)
392 """Prints out a formal parameter for a C wrapper.""" 396 arr = self.array
or '' 397 pointers = self.pointers
or '' 398 return "%s %s%s%s" % (self.type, pointers, self.name, arr)
401 arr = self.array
or '' 402 pointers = self.pointers
or '' 404 if arr.count(
'[') > 1:
408 arr = arr.replace(
'[]',
'')
409 return "%s%s%s" % (self.type, pointers, arr)
412 return self.cFormal()
416 """ Descriptor for simple MPI function declarations. 417 Contains return type, name of function, and a list of args. 419 def __init__(self, rtype, name):
424 def addArgument(self, arg):
425 arg.setDeclaration(self)
426 self.args.append(arg)
429 for arg
in self.args:
yield arg
432 return self.prototype()
438 return [arg.cFormal()
for arg
in self.args]
441 return [arg.cType()
for arg
in self.args]
443 def argsNoEllipsis(self):
444 return filter(
lambda arg: arg.name !=
"...", self.args)
446 def returnsErrorCode(self):
447 """This is a special case for MPI_Wtime and MPI_Wtick. 448 These functions actually return a double value instead of an int error code. 450 return self.rtype ==
"int" 453 return [arg.name
for arg
in self.argsNoEllipsis()]
455 def getArgName(self, index):
456 return self.argsNoEllipsis()[index].name
458 def fortranFormals(self):
459 formals =
map(Param.fortranFormal, self.argsNoEllipsis())
460 if self.name ==
"MPI_Init": formals = []
463 if self.returnsErrorCode(): ierr = [
"MPI_Fint *ierr"]
464 return formals + ierr
466 def fortranArgNames(self):
467 names = self.argNames()
468 if self.name ==
"MPI_Init": names = []
471 if self.returnsErrorCode(): ierr = [
"ierr"]
474 def prototype(self, modifiers=""):
475 if modifiers: modifiers = joinlines(modifiers,
" ")
476 return "%s%s %s(%s)" % (modifiers, self.retType(), self.name,
", ".join(self.formals()))
478 def pmpi_prototype(self, modifiers=""):
479 if modifiers: modifiers = joinlines(modifiers,
" ")
480 return "%s%s P%s(%s)" % (modifiers, self.retType(), self.name,
", ".join(self.formals()))
482 def fortranPrototype(self, name=None, modifiers=""):
483 if not name: name = self.name
484 if modifiers: modifiers = joinlines(modifiers,
" ")
486 if self.returnsErrorCode():
490 return "%s%s %s(%s)" % (modifiers, rtype, name,
", ".join(self.fortranFormals()))
496 def enumerate_mpi_declarations(mpicc, includes):
497 """ Invokes mpicc's C preprocessor on a C file that includes mpi.h. 498 Parses the output for declarations, and yields each declaration to 502 tmpfile = tempfile.NamedTemporaryFile(
'w+b', -1,
'.c')
503 tmpname =
"%s" % tmpfile.name
504 tmpfile.write(
'#include <mpi.h>')
510 string_includes = [
"-I"+dir
for dir
in includes]
511 mpicc_cmd =
"%s -E %s" % (mpicc,
" ".join(string_includes))
513 popen = subprocess.Popen(
"%s %s" % (mpicc_cmd, tmpname), shell=
True,
514 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
516 sys.stderr.write(
"IOError: couldn't run '" + mpicc_cmd +
"' for parsing mpi.h\n")
523 begin = begin_decl_re.search(line)
524 if begin
and not exclude_re.search(line):
526 return_type, fn_name = begin.groups()
529 while not end_decl_re.search(line):
530 line +=
" " + mpi_h.next().strip()
533 fn_and_paren =
r'(%s\s*\()' % fn_name
534 match = re.search(fn_and_paren, line)
535 lparen = match.start(1) + len(match.group(1)) - 1
536 rparen = find_matching_paren(line, lparen)
538 raise ValueError(
"Malformed declaration in header: '%s'" % line)
540 arg_string = line[lparen+1:rparen]
541 arg_list =
map(
lambda s: s.strip(), arg_string.split(
","))
544 if arg_list == [
'void']:
548 decl = Declaration(return_type, fn_name)
552 decl.addArgument(Param(
None,
None,
'...',
None, arg_num))
554 match = formal_re.match(arg)
556 sys.stderr.write(
"MATCH FAILED FOR: '%s' in %s\n" % (arg, fn_name))
559 type, pointers, name, array = match.groups()
561 all_pointers.add(pointers)
563 if not name: name =
"arg_" + str(arg_num)
565 decl.addArgument(Param(type.strip(), pointers, name, array, arg_num))
571 return_code = popen.wait()
573 sys.stderr.write(
"Error: Couldn't run '%s' for parsing mpi.h.\n" % mpicc_cmd)
574 sys.stderr.write(
" Process exited with code %d.\n" % return_code)
581 def write_enter_guard(out, decl):
582 """Prevent us from entering wrapper functions if we're already in a wrapper function. 583 Just call the PMPI function w/o the wrapper instead.""" 585 out.write(
" if (in_wrapper) return P%s(%s);\n" % (decl.name,
", ".join(decl.argNames())))
586 out.write(
" in_wrapper = 1;\n")
588 def write_exit_guard(out):
589 """After a call, set in_wrapper back to 0 so we can enter the next call.""" 591 out.write(
" in_wrapper = 0;\n")
594 def write_c_wrapper(out, decl, return_val, write_body):
595 """Write the C wrapper for an MPI function.""" 598 out.write(decl.pmpi_prototype(default_modifiers))
602 out.write(decl.prototype(default_modifiers))
604 out.write(
" %s %s = 0;\n" % (decl.retType(), return_val))
606 write_enter_guard(out, decl)
608 write_exit_guard(out)
610 out.write(
" return %s;\n" % return_val)
614 def write_fortran_binding(out, decl, delegate_name, binding, stmts=None):
615 """Outputs a wrapper for a particular fortran binding that delegates to the 616 primary Fortran wrapper. Optionally takes a list of statements to execute 619 out.write(decl.fortranPrototype(binding, default_modifiers))
622 out.write(joinlines(
map(
lambda s:
" " + s, stmts)))
623 if decl.returnsErrorCode():
625 out.write(
" %s(%s);\n" % (delegate_name,
", ".join(decl.fortranArgNames())))
628 out.write(
" return %s(%s);\n" % (delegate_name,
", ".join(decl.fortranArgNames())))
632 class FortranDelegation:
633 """Class for constructing a call to a Fortran wrapper delegate function. Provides 634 storage for local temporary variables, copies of parameters, callsites for MPI-1 and 635 MPI-2, and writebacks to local pointer types. 637 def __init__(self, decl, return_val):
639 self.return_val = return_val
645 self.mpich_actuals = []
647 def addTemp(self, type, name):
648 """Adds a temp var with a particular name. Adds the same var only once.""" 649 temp =
" %s %s;" % (type, name)
652 def addActual(self, actual):
653 self.actuals.append(actual)
654 self.mpich_actuals.append(actual)
656 def addActualMPICH(self, actual):
657 self.mpich_actuals.append(actual)
659 def addActualMPI2(self, actual):
660 self.actuals.append(actual)
662 def addWriteback(self, stmt):
663 self.writebacks.append(
" %s" % stmt)
665 def addCopy(self, stmt):
666 self.copies.append(
" %s" % stmt)
668 def write(self, out):
669 assert len(self.actuals) == len(self.mpich_actuals)
671 call =
" %s = %s" % (self.return_val, self.decl.name)
672 mpich_call =
"%s(%s);\n" % (call,
", ".join(self.mpich_actuals))
673 mpi2_call =
"%s(%s);\n" % (call,
", ".join(self.actuals))
675 out.write(
" %s %s = 0;\n" % (self.decl.retType(), self.return_val))
676 if mpich_call == mpi2_call
and not (self.temps
or self.copies
or self.writebacks):
677 out.write(mpich_call)
679 out.write(
"#if (!defined(MPICH_HAS_C2F) && defined(MPICH_NAME) && (MPICH_NAME == 1)) /* MPICH test */\n")
680 out.write(mpich_call)
681 out.write(
"#else /* MPI-2 safe call */\n")
682 out.write(joinlines(self.temps))
683 out.write(joinlines(self.copies))
685 out.write(joinlines(self.writebacks))
686 out.write(
"#endif /* MPICH test */\n")
689 def write_fortran_wrappers(out, decl, return_val):
690 """Writes primary fortran wrapper that handles arg translation. 691 Also outputs bindings for this wrapper for different types of fortran compilers. 693 delegate_name = decl.name + f_wrap_suffix
694 out.write(decl.fortranPrototype(delegate_name, [
"static"]))
697 call = FortranDelegation(decl, return_val)
699 if decl.name ==
"MPI_Init":
701 out.write(
" int argc = 0;\n");
702 out.write(
" char ** argv = NULL;\n");
703 call.addActual(
"&argc");
704 call.addActual(
"&argv");
706 out.write(
" *ierr = %s;\n" % return_val)
710 write_fortran_binding(out, decl, delegate_name,
"MPI_INIT", [
"fortran_init = 1;"])
711 write_fortran_binding(out, decl, delegate_name,
"mpi_init", [
"fortran_init = 2;"])
712 write_fortran_binding(out, decl, delegate_name,
"mpi_init_", [
"fortran_init = 3;"])
713 write_fortran_binding(out, decl, delegate_name,
"mpi_init__", [
"fortran_init = 4;"])
717 for arg
in decl.args:
718 if arg.name ==
"...":
721 if not (arg.pointers
or arg.array):
722 if not arg.isHandle():
724 dereferenced =
"*%s" % arg.name
725 call.addActual(dereferenced)
729 call.addActualMPI2(
"%s_f2c(*%s)" % (conversion_prefix(arg.type), arg.name))
730 call.addActualMPICH(
"(%s)(*%s)" % (arg.type, arg.name))
733 if not arg.isHandle():
736 call.addActual(
"(%s)%s" % (arg.castType(), arg.name))
739 call.addActualMPICH(
"(%s*)%s" % (arg.type, arg.name))
740 conv = conversion_prefix(arg.type)
741 temp =
"temp_%s" % arg.name
744 if not arg.isHandleArray():
745 call.addTemp(arg.type, temp)
746 call.addActualMPI2(
"&%s" % temp)
749 call.addCopy(
"%s_f2c(%s, &%s);" % (conv, arg.name, temp))
750 call.addWriteback(
"%s_c2f(&%s, %s);" % (conv, temp, arg.name))
752 call.addCopy(
"%s = %s_f2c(*%s);" % (temp, conv, arg.name))
753 call.addWriteback(
"*%s = %s_c2f(%s);" % (arg.name, conv, temp))
756 temp_arr_type =
"%s*" % arg.type
757 call.addTemp(temp_arr_type, temp)
758 call.addTemp(
"int",
"i")
762 copy =
" %s_f2c(&%s[i], &%s[i])" % (conv, arg.name, temp)
763 writeback =
" %s_c2f(&%s[i], &%s[i])" % (conv, temp, arg.name)
765 copy =
" temp_%s[i] = %s_f2c(%s[i])" % (arg.name, conv, arg.name)
766 writeback =
" %s[i] = %s_c2f(temp_%s[i])" % (arg.name, conv, arg.name)
769 count =
"*%s" % arg.countParam().name
770 call.addCopy(
"%s = (%s)malloc(sizeof(%s) * %s);" %
771 (temp, temp_arr_type, arg.type, count))
772 call.addCopy(
"for (i=0; i < %s; i++)" % count)
773 call.addCopy(
"%s;" % copy)
774 call.addActualMPI2(temp)
775 call.addWriteback(
"for (i=0; i < %s; i++)" % count)
776 call.addWriteback(
"%s;" % writeback)
777 call.addWriteback(
"free(%s);" % temp)
780 if decl.returnsErrorCode():
781 out.write(
" *ierr = %s;\n" % return_val)
783 out.write(
" return %s;\n" % return_val)
787 write_fortran_binding(out, decl, delegate_name, decl.name.upper())
788 write_fortran_binding(out, decl, delegate_name, decl.name.lower())
789 write_fortran_binding(out, decl, delegate_name, decl.name.lower() +
"_")
790 write_fortran_binding(out, decl, delegate_name, decl.name.lower() +
"__")
803 def macro(macro_name, **attrs):
805 macros[macro_name] = fun
808 setattr(fun, key, attrs[key])
812 def handle_list(list_name, list, args):
813 """This function handles indexing lists used as macros in the wrapper generator. 814 There are two syntaxes: 815 {{<list_name>}} Evaluates to the whole list, e.g. 'foo, bar, baz' 816 {{<list_name> <index>}} Evaluates to a particular element of a list. 821 len(args) == 1
or syntax_error(
"Wrong number of args for list expression.")
823 return list[int(args[0])]
825 syntax_error(
"Invald index value: '%s'" % args[0])
827 syntax_error(
"Index out of range in '%s': %d" % (list_name, index))
830 """This class implements a Macro function for applying something callable to 831 args in a decl with a particular type. 833 def __init__(self, decl):
836 def __call__(self, out, scope, args, children):
837 len(args) == 2
or syntax_error(
"Wrong number of args in apply macro.")
838 type, macro_name = args
839 for arg
in self.decl.args:
840 if arg.cType() == type:
841 out.write(
"%s(%s);\n" % (macro_name, arg.name))
843 def include_decl(scope, decl):
844 """This function is used by macros to include attributes MPI declarations in their scope.""" 845 scope[
"ret_type"] = decl.retType()
846 scope[
"args"] = decl.argNames()
847 scope[
"nargs"] = len(decl.argNames())
848 scope[
"types"] = decl.types()
849 scope[
"formals"] = decl.formals()
850 scope[
"apply_to_type"] = TypeApplier(decl)
851 scope.function_name = decl.name
854 def get_arg(out, scope, args, children):
855 return handle_list(
"args", decl.argNames(), args)
856 scope[
"get_arg"] = get_arg
857 scope[
"applyToType"] = scope[
"apply_to_type"]
858 scope[
"retType"] = scope[
"ret_type"]
859 scope[
"argList"] =
"(%s)" %
", ".join(scope[
"args"])
860 scope[
"argTypeList"] =
"(%s)" %
", ".join(scope[
"formals"])
862 def all_but(fn_list):
863 """Return a list of all mpi functions except those in fn_list""" 864 all_mpi =
set(mpi_functions.keys())
865 diff = all_mpi -
set(fn_list)
866 return [x
for x
in diff]
868 @macro(
"foreachfn", has_body=
True)
869 def foreachfn(out, scope, args, children):
870 """Iterate over all functions listed in args.""" 871 args
or syntax_error(
"Error: foreachfn requires function name argument.")
875 for fn_name
in args[1:]:
876 cur_function = fn_name
877 if not fn_name
in mpi_functions:
878 syntax_error(fn_name +
" is not an MPI function")
880 fn = mpi_functions[fn_name]
881 fn_scope = Scope(scope)
882 fn_scope[fn_var] = fn_name
883 include_decl(fn_scope, fn)
885 for child
in children:
886 child.evaluate(out, fn_scope)
889 @macro(
"fn", has_body=
True)
890 def fn(out, scope, args, children):
891 """Iterate over listed functions and generate skeleton too.""" 892 args
or syntax_error(
"Error: fn requires function name argument.")
896 for fn_name
in args[1:]:
897 cur_function = fn_name
898 if not fn_name
in mpi_functions:
899 syntax_error(fn_name +
" is not an MPI function")
901 fn = mpi_functions[fn_name]
902 return_val =
"_wrap_py_return_val" 904 fn_scope = Scope(scope)
905 fn_scope[fn_var] = fn_name
906 include_decl(fn_scope, fn)
908 fn_scope[
"ret_val"] = return_val
909 fn_scope[
"returnVal"] = fn_scope[
"ret_val"]
911 c_call =
"%s = P%s(%s);" % (return_val, fn.name,
", ".join(fn.argNames()))
912 if fn_name ==
"MPI_Init" and output_fortran_wrappers:
913 def callfn(out, scope, args, children):
918 out.write(
" if (fortran_init) {\n")
919 out.write(
"#ifdef PIC\n")
920 out.write(
" if (!PMPI_INIT && !pmpi_init && !pmpi_init_ && !pmpi_init__) {\n")
921 out.write(
" fprintf(stderr, \"ERROR: Couldn't find fortran pmpi_init function. Link against static library instead.\\n\");\n")
922 out.write(
" exit(1);\n")
924 out.write(
" switch (fortran_init) {\n")
925 out.write(
" case 1: PMPI_INIT(&%s); break;\n" % return_val)
926 out.write(
" case 2: pmpi_init(&%s); break;\n" % return_val)
927 out.write(
" case 3: pmpi_init_(&%s); break;\n" % return_val)
928 out.write(
" case 4: pmpi_init__(&%s); break;\n" % return_val)
929 out.write(
" default:\n")
930 out.write(
" fprintf(stderr, \"NO SUITABLE FORTRAN MPI_INIT BINDING\\n\");\n")
931 out.write(
" break;\n")
933 out.write(
"#else /* !PIC */\n")
934 out.write(
" %s(&%s);\n" % (pmpi_init_binding, return_val))
935 out.write(
"#endif /* !PIC */\n")
936 out.write(
" } else {\n")
937 out.write(
" %s\n" % c_call)
940 fn_scope[
"callfn"] = callfn
942 def write_fortran_init_flag():
943 output.write(
"static int fortran_init = 0;\n")
944 once(write_fortran_init_flag)
947 fn_scope[
"callfn"] = c_call
950 for child
in children:
951 child.evaluate(out, fn_scope)
953 out.write(
"/* ================== C Wrappers for %s ================== */\n" % fn_name)
954 write_c_wrapper(out, fn, return_val, write_body)
955 if output_fortran_wrappers:
956 out.write(
"/* =============== Fortran Wrappers for %s =============== */\n" % fn_name)
957 write_fortran_wrappers(out, fn, return_val)
958 out.write(
"/* ================= End Wrappers for %s ================= */\n\n\n" % fn_name)
961 @macro(
"forallfn", has_body=
True)
962 def forallfn(out, scope, args, children):
963 """Iterate over all but the functions listed in args.""" 964 args
or syntax_error(
"Error: forallfn requires function name argument.")
965 foreachfn(out, scope, [args[0]] + all_but(args[1:]), children)
967 @macro(
"fnall", has_body=
True)
968 def fnall(out, scope, args, children):
969 """Iterate over all but listed functions and generate skeleton too.""" 970 args
or syntax_error(
"Error: fnall requires function name argument.")
971 fn(out, scope, [args[0]] + all_but(args[1:]), children)
974 def sub(out, scope, args, children):
975 """{{sub <string> <regexp> <substitution>}} 976 Replaces value of <string> with all instances of <regexp> replaced with <substitution>. 978 len(args) == 3
or syntax_error(
"'sub' macro takes exactly 4 arguments.")
979 string, regex, substitution = args
980 if isinstance(string, list):
981 return [re.sub(regex, substitution, s)
for s
in string]
982 if not isinstance(regex, str):
983 syntax_error(
"Invalid regular expression in 'sub' macro: '%s'" % regex)
985 return re.sub(regex, substitution, string)
988 def zip_macro(out, scope, args, children):
989 len(args) == 2
or syntax_error(
"'zip' macro takes exactly 2 arguments.")
990 if not all([isinstance(a, list)
for a
in args]):
991 syntax_error(
"Arguments to 'zip' macro must be lists.")
993 return [
"%s %s" % x
for x
in zip(a, b)]
996 def def_macro(out, scope, args, children):
997 len(args) == 2
or syntax_error(
"'def' macro takes exactly 2 arguments.")
998 scope[args[0]] = args[1]
1001 def list_macro(out, scope, args, children):
1004 if isinstance(arg, list):
1011 def filter_macro(out, scope, args, children):
1012 """{{filter <regex> <list>}} 1013 Returns a list containing all elements of <list> that <regex> matches. 1015 len(args) == 2
or syntax_error(
"'filter' macro takes exactly 2 arguments.")
1017 if not isinstance(l, list):
1018 syntax_error(
"Invalid list in 'filter' macro: '%s'" % str(list))
1019 if not isinstance(regex, str):
1020 syntax_error(
"Invalid regex in 'filter' macro: '%s'" % str(regex))
1022 return re.search(regex, s)
1023 return filter(match, l)
1026 def fn_num(out, scope, args, children):
1039 """Represents a piece of a wrapper file. Is either a text chunk 1040 or a macro chunk with children to which the macro should be applied. 1041 macros are evaluated lazily, so the macro is just a string until 1042 execute is called and it is fetched from its enclosing scope.""" 1049 def iwrite(self, file, level, text):
1050 """Write indented text.""" 1051 for x
in xrange(level):
1055 def write(self, file=sys.stdout, l=0):
1056 if self.macro: self.iwrite(file, l,
"{{%s %s}}" % (self.macro,
" ".join([str(arg)
for arg
in self.args])))
1057 if self.text: self.iwrite(file, l,
"TEXT\n")
1058 for child
in self.children:
1059 child.write(file, l+1)
1061 def execute(self, out, scope):
1062 """This function executes a chunk. For strings, lists, text chunks, etc., this just 1063 entails returning the chunk's value. For callable macros, this executes and returns 1067 out.write(self.text)
1069 if not self.macro
in scope:
1070 error_msg =
"Invalid macro: '%s'" % self.macro
1071 if scope.function_name:
1072 error_msg +=
" for " + scope.function_name
1073 syntax_error(error_msg)
1075 value = scope[self.macro]
1076 if hasattr(value,
"__call__"):
1079 if isinstance(arg, Chunk):
1080 return arg.execute(out, scope)
1083 args = [eval_arg(arg)
for arg
in self.args]
1084 return value(out, scope, args, self.children)
1085 elif isinstance(value, list):
1087 return handle_list(self.macro, value, self.args)
1092 def stringify(self, value):
1093 """Used by evaluate() to print the return values of chunks out to the output file.""" 1094 if isinstance(value, list):
1095 return ", ".join(value)
1099 def evaluate(self, out, scope):
1100 """This is an 'interactive' version of execute. This should be called when 1101 the chunk's value (if any) should be written out. Body macros and the outermost 1102 scope should use this instead of execute(). 1104 value = self.execute(out, scope)
1105 if value
is not None:
1106 out.write(self.stringify(value))
1109 """Parser for the really simple wrappergen grammar. 1110 This parser has support for multiple lexers. self.tokens is a list of iterables, each 1111 representing a new token stream. You can add additional tokens to be lexed using push_tokens. 1112 This will cause the pushed tokens to be handled before any others. This allows us to switch 1113 lexers while parsing, so that the outer part of the file is processed in a language-agnostic 1114 way, but stuff inside macros is handled as its own macro language. 1116 def __init__(self, macros):
1117 self.macros = macros
1118 self.macro_lexer = InnerLexer()
1119 self.tokens = iter([])
1124 """Puts the next token in the input stream into self.next.""" 1126 self.next = self.tokens.next()
1127 except StopIteration:
1130 def push_tokens(self, iterable):
1131 """Adds all tokens in some iterable to the token stream.""" 1132 self.tokens = itertools.chain(iter(iterable), iter([self.next]), self.tokens)
1135 def accept(self, id):
1136 """Puts the next symbol in self.token if we like it. Then calls gettok()""" 1137 if self.next.isa(id):
1138 self.token = self.next
1143 def unexpected_token(self):
1144 syntax_error(
"Unexpected token: %s." % self.next)
1146 def expect(self, id):
1147 """Like accept(), but fails if we don't like the next token.""" 1152 self.unexpected_token()
1154 syntax_error(
"Unexpected end of file.")
1157 def is_body_macro(self, name):
1158 """Shorthand for testing whether a particular name is the name of a macro that has a body. 1159 Need this for parsing the language b/c things like {{fn}} need a corresponding {{endfn}}. 1161 return name
in self.macros
and self.macros[name].has_body
1163 def macro(self, accept_body_macros=True):
1165 if self.accept(TEXT):
1166 self.push_tokens(self.macro_lexer.lex(self.token.value))
1170 self.expect(IDENTIFIER)
1171 chunk.macro = self.token.value
1173 if not accept_body_macros
and self.is_body_macro(chunk.macro):
1174 syntax_error(
"Cannot use body macros in expression context: '%s'" % chunk.macro)
1178 if self.accept(LBRACE):
1179 chunk.args.append(self.macro(
False))
1180 elif self.accept(IDENTIFIER):
1181 chunk.args.append(self.token.value)
1182 elif self.accept(TEXT):
1183 self.push_tokens(self.macro_lexer.lex(self.token.value))
1189 def text(self, end_macro = None):
1192 if self.accept(TEXT):
1194 chunk.text = self.token.value
1195 chunks.append(chunk)
1196 elif self.accept(LBRACE):
1197 chunk = self.macro()
1200 if name == end_macro:
1203 elif isindex(chunk.macro):
1205 chunk.macro =
"args" 1207 elif self.is_body_macro(name):
1208 chunk.children = self.text(
"end"+name)
1209 chunks.append(chunk)
1211 self.unexpected_token()
1215 def parse(self, text):
1217 outer_lexer = OuterRegionLexer()
1219 outer_lexer = OuterCommentLexer()
1220 self.push_tokens(outer_lexer.lex(text))
1228 sys.stderr.write(usage_string)
1233 output_filename =
None 1236 opts, args = getopt.gnu_getopt(sys.argv[1:],
"fsgdc:o:i:I:")
1237 except getopt.GetoptError, err:
1238 sys.stderr.write(err +
"\n")
1241 for opt, arg
in opts:
1242 if opt ==
"-d": dump_prototypes =
True 1243 if opt ==
"-f": output_fortran_wrappers =
True 1244 if opt ==
"-s": skip_headers =
True 1245 if opt ==
"-g": output_guards =
True 1246 if opt ==
"-c": mpicc = arg
1247 if opt ==
"-o": output_filename = arg
1249 stripped = arg.strip()
1250 if stripped: includes.append(stripped)
1252 if not arg
in pmpi_init_bindings:
1253 sys.stderr.write(
"ERROR: PMPI_Init binding must be one of:\n %s\n" %
" ".join(possible_bindings))
1256 pmpi_init_binding = arg
1258 if len(args) < 1
and not dump_prototypes:
1262 for decl
in enumerate_mpi_declarations(mpicc, includes):
1263 mpi_functions[decl.name] = decl
1264 if dump_prototypes:
print decl
1267 if not mpi_functions:
1268 sys.stderr.write(
"Error: Found no declarations in mpi.h.\n")
1272 if dump_prototypes: sys.exit(0)
1277 output = open(output_filename,
"w")
1279 sys.stderr.write(
"Error: couldn't open file " + arg +
" for writing.\n")
1284 if not skip_headers:
1285 output.write(wrapper_includes)
1286 if output_guards: output.write(
"static int in_wrapper = 0;\n")
1293 file = open(cur_filename)
1296 outer_scope = Scope()
1297 outer_scope[
"fileno"] = str(fileno)
1298 outer_scope.include(macros)
1300 parser = Parser(macros)
1301 chunks = parser.parse(file.read())
1303 for chunk
in chunks:
1304 chunk.evaluate(output, Scope(outer_scope))
1307 except WrapSyntaxError:
1309 if output_filename: os.remove(output_filename)
__host__ __device__ double set(double &x)
static void sub(Float *dst, Float *a, Float *b, int cnt)
std::map< TuneKey, TuneParam > map
__host__ __device__ constexpr bool match()