/*---------------------------------------------------------------*/
/*---                                                         ---*/
/*--- A library of wrappers for MPI 2 functions.              ---*/
/*---                                                         ---*/
/*---------------------------------------------------------------*/

/* ----------------------------------------------------------------

   Notice that the following BSD-style license applies to this one
   file (mpiwrap.c) only.  The rest of Valgrind is licensed under the
   terms of the GNU General Public License, version 2, unless
   otherwise indicated.  See the COPYING file in the source
   distribution for details.

   ----------------------------------------------------------------

   This file is part of Valgrind, a dynamic binary instrumentation
   framework.

   Copyright (C) 2006-2011 OpenWorks LLP.  All rights reserved.

   Redistribution and use in source and binary forms, with or without
   modification, are permitted provided that the following conditions
   are met:

   1. Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.

   2. The origin of this software must not be misrepresented; you must
      not claim that you wrote the original software.  If you use this
      software in a product, an acknowledgment in the product
      documentation would be appreciated but is not required.

   3. Altered source versions must be plainly marked as such, and must
      not be misrepresented as being the original software.

   4. The name of the author may not be used to endorse or promote
      products derived from this software without specific prior written
      permission.

   THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
   OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
   GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

   Neither the names of the U.S. Department of Energy nor the
   University of California nor the names of its contributors may be
   used to endorse or promote products derived from this software
   without prior written permission.
*/

/* Handling of MPI_STATUS{ES}_IGNORE for MPI_Status* arguments.

   The MPI-2 spec allows many functions which have MPI_Status* purely
   as an out parameter, to accept the constants MPI_STATUS_IGNORE or
   MPI_STATUSES_IGNORE there instead, if the caller does not care
   about the status.  See the MPI-2 spec sec 4.5.1 ("Passing
   MPI_STATUS_IGNORE for Status").  (mpi2-report.pdf, 1615898 bytes,
   md5=694a5efe2fd291eecf7e8c9875b5f43f).

   This library handles such cases by allocating a fake MPI_Status
   object (on the stack) or an array thereof (on the heap), and
   passing that onwards instead.  From the outside the caller sees no
   difference.  Unfortunately the simpler approach of merely detecting
   and handling these special cases at a lower level does not work,
   because we need to use information returned in MPI_Status*
   arguments to paint result buffers, even if the caller doesn't
   supply a real MPI_Status object.

   Eg, MPI_Recv.  We can't paint the result buffer without knowing how
   many items arrived; but we can't find that out without passing a
   real MPI_Status object to the (real) MPI_Recv call.  Hence, if the
   caller did not supply one, we have no option but to use a temporary
   stack allocated one for the inner call.  Ditto, more indirectly
   (via maybe_complete) for nonblocking receives and the various
   associated wait/test calls. */


/*------------------------------------------------------------*/
/*--- includes                                             ---*/
/*------------------------------------------------------------*/

#include <stdio.h>
#include <assert.h>
#include <unistd.h>     /* getpid */
#include <stdlib.h>     /* exit */
#include <string.h>     /* strstr */
#include <pthread.h>    /* pthread_mutex_{lock,unlock} */

/* Include Valgrind magic macros for writing wrappers. */
#include "../memcheck/memcheck.h"

/* Include macros for VALGRIND_{DIS,EN}ABLE_ERROR_REPORTING.
   This is somewhat experimental and hence disable-able, by
   setting cONFIG_DER to zero. */
#include "../include/valgrind.h"

#define cONFIG_DER  1   /* set to 0 to disable */


/*------------------------------------------------------------*/
/*--- Connect to MPI library                               ---*/
/*------------------------------------------------------------*/

/* Include headers for whatever MPI implementation the wrappers are to
   be used with.  The configure system will tell us what the path to
   the chosen MPI implementation is, via -I.. to the compiler. */
#include "mpi.h"

/* Where are API symbols?
   Open MPI      lib/libmpi.so,   soname = libmpi.so.0
   Quadrics MPI  lib/libmpi.so,   soname = libmpi.so.0
   MPICH         libmpich.so.1.0, soname = libmpich.so.1.0

   A suitable soname to match with is therefore "libmpi*.so*".
*/
#define I_WRAP_FNNAME_U(_name) \
        I_WRAP_SONAME_FNNAME_ZU(libmpiZaZdsoZa,_name)


/* Define HAVE_MPI_STATUS_IGNORE iff we have to deal with
   MPI_STATUS{ES}_IGNORE. */
#if MPI_VERSION >= 2 \
    || (defined(MPI_STATUS_IGNORE) && defined(MPI_STATUSES_IGNORE))
#  undef HAVE_MPI_STATUS_IGNORE
#  define HAVE_MPI_STATUS_IGNORE 1
#else
#  undef HAVE_MPI_STATUS_IGNORE
#endif


/*------------------------------------------------------------*/
/*--- Decls                                                ---*/
/*------------------------------------------------------------*/

typedef  unsigned char  Bool;
#define False ((Bool)0)
#define True  ((Bool)1)

/* Word, UWord are machine words - same size as a pointer.  This is
   checked at startup.  The wrappers below use 'long' to mean a
   machine word - this too is tested at startup. */
typedef    signed long  Word;
typedef  unsigned long  UWord;

#if !defined(offsetof)
#  define offsetof(type,memb) ((int)&((type*)0)->memb)
#endif

/* Find the size of long double image (not 'sizeof(long double)').
   See comments in sizeofOneNamedTy. */
static long sizeof_long_double_image ( void );


/*------------------------------------------------------------*/
/*--- Simple helpers                                       ---*/
/*------------------------------------------------------------*/

/* ------ Helpers for debug printing ------ */

/* constant */
static const char* preamble = "valgrind MPI wrappers";

/* established at startup */
static pid_t my_pid         = -1;
static char* options_str    = NULL;
static int   opt_verbosity  = 1;
static Bool  opt_missing    = 0; /* 0:silent; 1:warn; 2:abort */
static Bool  opt_help       = False;
static Bool  opt_initkludge = False;

static void before ( char* fnname )
{
   /* This isn't thread-safe wrt 'done' (no locking).  It's not
      critical. */
   static int done = 0;
   if (done == 0) {
      done = 1;
      my_pid = getpid();
      options_str = getenv("MPIWRAP_DEBUG");
      if (options_str) {
         if (NULL != strstr(options_str, "warn"))
            opt_missing = 1;
         if (NULL != strstr(options_str, "strict"))
            opt_missing = 2;
         if (NULL != strstr(options_str, "verbose"))
            opt_verbosity++;
         if (NULL != strstr(options_str, "quiet"))
            opt_verbosity--;
         if (NULL != strstr(options_str, "help"))
            opt_help = True;
         if (NULL != strstr(options_str, "initkludge"))
            opt_initkludge = True;
      }
      if (opt_verbosity > 0)
         fprintf(stderr, "%s %5d: Active for pid %d\n", 
                         preamble, my_pid, my_pid);
      /* Sanity check - that Word/UWord really are machine words. */
      assert(sizeof(Word)  == sizeof(void*));
      assert(sizeof(UWord) == sizeof(void*));
      /* Sanity check - char is byte-sized (else address calculations
         in walk_type don't work. */
      assert(sizeof(char) == 1);
      if (opt_help) {
         fprintf(stderr, "\n");
         fprintf(stderr, "Valid options for the MPIWRAP_DEBUG environment"
                         " variable are:\n");
         fprintf(stderr, "\n");
         fprintf(stderr, "   quiet       be silent except for errors\n");
         fprintf(stderr, "   verbose     show wrapper entries/exits\n");
         fprintf(stderr, "   strict      abort the program if a function"
                         " with no wrapper is used\n");
         fprintf(stderr, "   warn        give a warning if a function"
                         " with no wrapper is used\n");
         fprintf(stderr, "   help        display this message, then exit\n");
         fprintf(stderr, "   initkludge  debugging hack; do not use\n");
         fprintf(stderr, "\n");
         fprintf(stderr, "Multiple options are allowed, eg"
                         " MPIWRAP_DEBUG=strict,verbose\n");
         fprintf(stderr, "Note: 'warn' generates output even if 'quiet'"
                         " is also specified\n");
         fprintf(stderr, "\n");
         fprintf(stderr, "%s %5d: exiting now\n", preamble, my_pid );
         exit(1);
      }
      if (opt_verbosity > 0)
         fprintf(stderr, 
                 "%s %5d: Try MPIWRAP_DEBUG=help for possible options\n", 
                 preamble, my_pid);

   }
   if (opt_verbosity > 1)
      fprintf(stderr, "%s %5d: enter PMPI_%s\n", preamble,  my_pid, fnname );
}

static __inline__ void after ( char* fnname, int err )
{
   if (opt_verbosity > 1)
      fprintf(stderr, "%s %5d:  exit PMPI_%s (err = %d)\n", 
                      preamble, my_pid, fnname, err );
}

static void barf ( char* msg )
{
   fprintf(stderr, "%s %5d: fatal: %s\n",   preamble, my_pid, msg);
   fprintf(stderr, "%s %5d: exiting now\n", preamble, my_pid );
   exit(1);
}

/* Half-hearted type-showing function (for debugging). */
static void showTy ( FILE* f, MPI_Datatype ty )
{
        if (ty == MPI_DATATYPE_NULL)  fprintf(f,"DATATYPE_NULL");
   else if (ty == MPI_BYTE)           fprintf(f,"BYTE");
   else if (ty == MPI_PACKED)         fprintf(f,"PACKED");
   else if (ty == MPI_CHAR)           fprintf(f,"CHAR");
   else if (ty == MPI_SHORT)          fprintf(f,"SHORT");
   else if (ty == MPI_INT)            fprintf(f,"INT");
   else if (ty == MPI_LONG)           fprintf(f,"LONG");
   else if (ty == MPI_FLOAT)          fprintf(f,"FLOAT");
   else if (ty == MPI_DOUBLE)         fprintf(f,"DOUBLE");
   else if (ty == MPI_LONG_DOUBLE)    fprintf(f,"LONG_DOUBLE");
   else if (ty == MPI_UNSIGNED_CHAR)  fprintf(f,"UNSIGNED_CHAR");
   else if (ty == MPI_UNSIGNED_SHORT) fprintf(f,"UNSIGNED_SHORT");
   else if (ty == MPI_UNSIGNED_LONG)  fprintf(f,"UNSIGNED_LONG");
   else if (ty == MPI_UNSIGNED)       fprintf(f,"UNSIGNED");
   else if (ty == MPI_FLOAT_INT)      fprintf(f,"FLOAT_INT");
   else if (ty == MPI_DOUBLE_INT)     fprintf(f,"DOUBLE_INT");
   else if (ty == MPI_LONG_DOUBLE_INT) fprintf(f,"LONG_DOUBLE_INT");
   else if (ty == MPI_LONG_INT)       fprintf(f,"LONG_INT");
   else if (ty == MPI_SHORT_INT)      fprintf(f,"SHORT_INT");
   else if (ty == MPI_2INT)           fprintf(f,"2INT");
   else if (ty == MPI_UB)             fprintf(f,"UB");
   else if (ty == MPI_LB)             fprintf(f,"LB");
#  if defined(MPI_WCHAR)
   else if (ty == MPI_WCHAR)          fprintf(f,"WCHAR");
#  endif
   else if (ty == MPI_LONG_LONG_INT)  fprintf(f,"LONG_LONG_INT");
#  if defined(MPI_LONG_LONG)
   else if (ty == MPI_LONG_LONG)      fprintf(f,"LONG_LONG");
#  endif
#  if defined(MPI_UNSIGNED_LONG_LONG)
   else if (ty == MPI_UNSIGNED_LONG_LONG) fprintf(f,"UNSIGNED_LONG_LONG");
#  endif
#  if defined(MPI_REAL8)
   else if (ty == MPI_REAL8)          fprintf(f, "REAL8");
#  endif
#  if defined(MPI_REAL4)
   else if (ty == MPI_REAL4)          fprintf(f, "REAL4");
#  endif
#  if defined(MPI_REAL)
   else if (ty == MPI_REAL)           fprintf(f, "REAL");
#  endif
#  if defined(MPI_INTEGER8)
   else if (ty == MPI_INTEGER8)       fprintf(f, "INTEGER8");
#  endif
#  if defined(MPI_INTEGER4)
   else if (ty == MPI_INTEGER4)       fprintf(f, "INTEGER4");
#  endif
#  if defined(MPI_INTEGER)
   else if (ty == MPI_INTEGER)        fprintf(f, "INTEGER");
#  endif
#  if defined(MPI_DOUBLE_PRECISION)
   else if (ty == MPI_DOUBLE_PRECISION) fprintf(f, "DOUBLE_PRECISION");
#  endif
#  if defined(MPI_COMPLEX)
   else if (ty == MPI_COMPLEX)          fprintf(f, "COMPLEX");
#  endif
#  if defined(MPI_DOUBLE_COMPLEX)
   else if (ty == MPI_DOUBLE_COMPLEX)   fprintf(f, "DOUBLE_COMPLEX");
#  endif
#  if defined(MPI_LOGICAL)
   else if (ty == MPI_LOGICAL)          fprintf(f, "LOGICAL");
#  endif
#  if defined(MPI_2INTEGER)
   else if (ty == MPI_2INTEGER)         fprintf(f, "2INTEGER");
#  endif
#  if defined(MPI_2COMPLEX)
   else if (ty == MPI_2COMPLEX)         fprintf(f, "2COMPLEX");
#  endif
#  if defined(MPI_2DOUBLE_COMPLEX)
   else if (ty == MPI_2DOUBLE_COMPLEX)  fprintf(f, "2DOUBLE_COMPLEX");
#  endif
#  if defined(MPI_2REAL)
   else if (ty == MPI_2REAL)            fprintf(f, "2REAL");
#  endif
#  if defined(MPI_2DOUBLE_PRECISION)
   else if (ty == MPI_2DOUBLE_PRECISION) fprintf(f, "2DOUBLE_PRECISION");
#  endif
#  if defined(MPI_CHARACTER)
   else if (ty == MPI_CHARACTER)         fprintf(f, "CHARACTER");
#  endif
   else fprintf(f,"showTy:???");
}

static void showCombiner ( FILE* f, int combiner )
{
   switch (combiner) {
      case MPI_COMBINER_NAMED:       fprintf(f, "NAMED"); break;
#if   defined(MPI_COMBINER_DUP)
      case MPI_COMBINER_DUP:         fprintf(f, "DUP"); break;
#     endif
      case MPI_COMBINER_CONTIGUOUS:  fprintf(f, "CONTIGUOUS"); break;
      case MPI_COMBINER_VECTOR:      fprintf(f, "VECTOR"); break;
#if   defined(MPI_COMBINER_HVECTOR_INTEGER)
      case MPI_COMBINER_HVECTOR_INTEGER: fprintf(f, "HVECTOR_INTEGER"); break;
#     endif
      case MPI_COMBINER_HVECTOR:     fprintf(f, "HVECTOR"); break;
      case MPI_COMBINER_INDEXED:     fprintf(f, "INDEXED"); break;
#if   defined(MPI_COMBINER_HINDEXED_INTEGER)
      case MPI_COMBINER_HINDEXED_INTEGER: fprintf(f, "HINDEXED_INTEGER"); break;
#     endif
      case MPI_COMBINER_HINDEXED:    fprintf(f, "HINDEXED"); break;
#if   defined(MPI_COMBINER_INDEXED_BLOCK)
      case MPI_COMBINER_INDEXED_BLOCK: fprintf(f, "INDEXED_BLOCK"); break;
#     endif
#if   defined(MPI_COMBINER_STRUCT_INTEGER)
      case MPI_COMBINER_STRUCT_INTEGER: fprintf(f, "STRUCT_INTEGER"); break;
#     endif
      case MPI_COMBINER_STRUCT:      fprintf(f, "STRUCT"); break;
#if   defined(MPI_COMBINER_SUBARRAY)
      case MPI_COMBINER_SUBARRAY:    fprintf(f, "SUBARRAY"); break;
#     endif
#if   defined(MPI_COMBINER_DARRAY)
      case MPI_COMBINER_DARRAY:      fprintf(f, "DARRAY"); break;
#     endif
#if   defined(MPI_COMBINER_F90_REAL)
      case MPI_COMBINER_F90_REAL:    fprintf(f, "F90_REAL"); break;
#     endif
#if   defined(MPI_COMBINER_F90_COMPLEX)
      case MPI_COMBINER_F90_COMPLEX: fprintf(f, "F90_COMPLEX"); break;
#     endif
#if   defined(MPI_COMBINER_F90_INTEGER)
      case MPI_COMBINER_F90_INTEGER: fprintf(f, "F90_INTEGER"); break;
#     endif
#if   defined(MPI_COMBINER_RESIZED)
      case MPI_COMBINER_RESIZED:     fprintf(f, "RESIZED"); break;
#     endif
      default: fprintf(f, "showCombiner:??"); break;
   }
}


/* ------ Get useful bits of info ------ */

/* Note, PMPI_Comm_rank/size are themselves wrapped.  Should work
   fine. */

static __inline__ int comm_rank ( MPI_Comm comm ) 
{
   int err, r;
   err = PMPI_Comm_rank(comm, &r);
   return err ? 0/*arbitrary*/ : r;
}

static __inline__ int comm_size ( MPI_Comm comm ) 
{
   int err, r;
   err = PMPI_Comm_size(comm, &r);
   return err ? 0/*arbitrary*/ : r;
}

static __inline__ Bool count_from_Status( /*OUT*/int* recv_count, 
                                      MPI_Datatype datatype, 
                                      MPI_Status* status)
{
   int n;
   int err = PMPI_Get_count(status, datatype, &n);
   if (err == MPI_SUCCESS) {
      *recv_count = n;
      return True;
   } else {
      return False;
   }
}

/* It's critical that we can do equality on MPI_Requests.
   Unfortunately these are opaque objects to us (handles, in the
   parlance of the MPI 1.1 spec).  Fortunately Sec 2.4.1 ("Opaque
   Objects") specifies that "In C, [...] These [handles] should be
   types that support assignment and equality operations."  Hence the
   following function should compile for any compliant definition of
   MPI_Request. */
static __inline__ 
Bool eq_MPI_Request ( MPI_Request r1, MPI_Request r2 )
{
   return r1 == r2;
}

/* Return True if status is MPI_STATUS_IGNORE or MPI_STATUSES_IGNORE.
   On MPI-1.x platforms which don't have these symbols (and they would
   only have them if they've been backported from 2.x) always return
   False. */
static __inline__
Bool isMSI ( MPI_Status* status )
{
#  if defined(HAVE_MPI_STATUS_IGNORE)
   return status == MPI_STATUSES_IGNORE || status == MPI_STATUS_IGNORE;
#  else
   return False;
#  endif
}

/* Get the 'extent' of a type.  Note, as per the MPI spec this
   includes whatever padding would be required when using 'ty' in an
   array. */
static long extentOfTy ( MPI_Datatype ty )
{
   int      r;
   MPI_Aint n;
   r = PMPI_Type_extent(ty, &n);
   assert(r == MPI_SUCCESS);
   return (long)n;
}

/* Free up *ty, if it is safe to do so */
static void maybeFreeTy ( MPI_Datatype* ty )
{
   int r, n_ints, n_addrs, n_dtys, tycon;

   r = PMPI_Type_get_envelope( *ty, &n_ints, &n_addrs, &n_dtys, &tycon );
   assert(r == MPI_SUCCESS);

   /* can't free named types */
   if (tycon == MPI_COMBINER_NAMED)
      return;

   /* some kinds of structs are predefined so we can't free them
      either. */
   if (*ty == MPI_FLOAT_INT || *ty == MPI_DOUBLE_INT 
       || *ty == MPI_LONG_INT || *ty == MPI_2INT 
       || *ty == MPI_SHORT_INT || *ty == MPI_LONG_DOUBLE_INT)
      return;

   /* Looks OK - free it. */
   if (0) {
      /* show me what you're about to free .. */
      fprintf(stderr, "freeing combiner ");
      showCombiner(stderr,tycon);
      fprintf(stderr, " ty= ");
      showTy(stderr,*ty);
      fprintf(stderr,"\n");
   }
   r = PMPI_Type_free(ty);
   assert(r == MPI_SUCCESS);
}

/* How big is a "named" (base) type?  Returns 0 if not known.  Note.
   There is a subtlety, which is that this is required to return the
   exact size of one item of the type, NOT the size of it when padded
   suitably to make an array of them.  In particular that's why the
   size of LONG_DOUBLE is computed by looking at the result of doing a
   long double store, rather than just asking what is the sizeof(long
   double).

   For LONG_DOUBLE on x86-linux and amd64-linux my impression is that
   the right answer is 10 even though sizeof(long double) says 12 and
   16 respectively.  On ppc32-linux it appears to be 16.

   Ref: MPI 1.1 doc p18 */
static long sizeofOneNamedTy ( MPI_Datatype ty )
{
   if (ty == MPI_CHAR)           return sizeof(signed char);
   if (ty == MPI_SHORT)          return sizeof(signed short int);
   if (ty == MPI_INT)            return sizeof(signed int);
   if (ty == MPI_LONG)           return sizeof(signed long int);
   if (ty == MPI_UNSIGNED_CHAR)  return sizeof(unsigned char);
   if (ty == MPI_UNSIGNED_SHORT) return sizeof(unsigned short int);
   if (ty == MPI_UNSIGNED)       return sizeof(unsigned int);
   if (ty == MPI_UNSIGNED_LONG)  return sizeof(unsigned long int);
   if (ty == MPI_FLOAT)          return sizeof(float);
   if (ty == MPI_DOUBLE)         return sizeof(double);
   if (ty == MPI_BYTE)           return 1;
   if (ty == MPI_LONG_DOUBLE)    return sizeof_long_double_image();
   if (ty == MPI_PACKED)         return 1;
   if (ty == MPI_LONG_LONG_INT)  return sizeof(signed long long int);

#  if defined(MPI_REAL8)
   if (ty == MPI_REAL8)          return 8; /* MPI2 spec */;
#  endif
#  if defined(MPI_REAL4)
   if (ty == MPI_REAL4)          return 4; /* MPI2 spec */;
#  endif
#  if defined(MPI_REAL)
   if (ty == MPI_REAL)           return 4; /* MPI2 spec */;
#  endif
#  if defined(MPI_INTEGER8)
   if (ty == MPI_INTEGER8)       return 8; /* MPI2 spec */;
#  endif
#  if defined(MPI_INTEGER4)
   if (ty == MPI_INTEGER4)       return 4; /* MPI2 spec */;
#  endif
#  if defined(MPI_INTEGER)
   if (ty == MPI_INTEGER)        return 4; /* MPI2 spec */;
#  endif
#  if defined(MPI_DOUBLE_PRECISION)
   if (ty == MPI_DOUBLE_PRECISION) return 8; /* MPI2 spec */;
#  endif

   /* new in MPI2: */
#  if defined(MPI_WCHAR)
   if (ty == MPI_WCHAR)              return 2; /* MPI2 spec */;
#  endif
#  if defined(MPI_SIGNED_CHAR)
   if (ty == MPI_SIGNED_CHAR)        return 1; /* MPI2 spec */;
#  endif
#  if defined(MPI_UNSIGNED_LONG_LONG)
   if (ty == MPI_UNSIGNED_LONG_LONG) return 8; /* MPI2 spec */;
#  endif
#  if defined(MPI_COMPLEX)
   if (ty == MPI_COMPLEX)            return 2 * 4; /* MPI2 spec */
#  endif
#  if defined(MPI_DOUBLE_COMPLEX)
   if (ty == MPI_DOUBLE_COMPLEX)     return 2 * 8; /* MPI2 spec */
#  endif
#  if defined(MPI_LOGICAL)
   if (ty == MPI_LOGICAL)            return 4; /* MPI2 spec */
#  endif
#  if defined(MPI_2INTEGER)
   if (ty == MPI_2INTEGER)      return 2 * 4; /* undocumented in MPI2 */
#  endif
#  if defined(MPI_2COMPLEX)
   if (ty == MPI_2COMPLEX)      return 2 * 8; /* undocumented in MPI2 */
#  endif
#  if defined(MPI_2DOUBLE_COMPLEX)
   /* 32: this is how openmpi-1.2.2 behaves on x86-linux, but I have
      really no idea if this is right. */
   if (ty == MPI_2DOUBLE_COMPLEX)   return 32; /* undocumented in MPI2 */
#  endif
#  if defined(MPI_2REAL)
   if (ty == MPI_2REAL)              return 2 * 4; /* undocumented in MPI2 */
#  endif
#  if defined(MPI_2DOUBLE_PRECISION)
   if (ty == MPI_2DOUBLE_PRECISION)  return 2 * 8; /* undocumented in MPI2 */
#  endif
#  if defined(MPI_CHARACTER)
   if (ty == MPI_CHARACTER)          return 1; /* MPI2 spec */
#  endif

   /* Note: the following are named structs, not named basic types,
      and so are not handled here:
         FLOAT_INT DOUBLE_INT LONG_INT 2INT SHORT_INT LONG_DOUBLE_INT
      My guess is they are probably for doing max-w-index style
      reductions, the INT carrying the index of the max/min and the
      other type its actual value.
   */
   return 0;
}


/* Find the size of long double image (not 'sizeof(long double)').
   See comments in sizeofOneNamedTy. 
*/
static long sizeof_long_double_image ( void )
{
   long i;
   unsigned char* p;
   static long cached_result = 0;

   /* Hopefully we have it already. */
   if (cached_result != 0) {
      assert(cached_result == 10 || cached_result == 16 || cached_result == 8);
      return cached_result;
   }

   /* No?  Then we'll have to compute it.  This isn't thread-safe but
      it doesn't really matter since all races to compute it should
      produce the same answer. */
   p = malloc(64);
   assert(p);
   for (i = 0; i < 64; i++)
      p[i] = 0x55;

   /* Write a value which isn't known at compile time and therefore
      must come out of a register.  If we just store a constant here,
      some compilers write more data than a store from a machine
      register would.  Therefore we have to force a store from a
      machine register by storing a value which isn't known at compile
      time.  Since getpid() will return a value < 1 million, turn it
      into a zero by dividing by 1e+30. */
   *(long double*)(&p[16]) = (long double)(1.0e-30 * (double)getpid());

   for (i = 0; i < 16; i++) {
      assert(p[i] == 0x55);
      assert(p[i+48] == 0x55);
   }
   for (i = 16; i <= 48; i++) {
      if (p[i] == 0x55)
         break;
   }

   assert(i < 48);
   assert(i > 16);
   free(p);
   cached_result = i - 16;

   if (0) 
      printf("sizeof_long_double_image: computed %d\n", (int)cached_result);

   assert(cached_result == 10 || cached_result == 16 || cached_result == 8);
   return cached_result;
}


/*------------------------------------------------------------*/
/*--- Unpicking datatypes                                  ---*/
/*------------------------------------------------------------*/

static __inline__
void walk_type_array ( void(*f)(void*,long), char* base, 
                       MPI_Datatype ty, long count );


/* Walk over all fragments of the object of type 'ty' with base
   address 'base', and apply 'f' to the start/length of each
   contiguous fragment. */
static 
void walk_type ( void(*f)(void*,long), char* base, MPI_Datatype ty )
{
   int  r, n_ints, n_addrs, n_dtys, tycon;
   long ex, i;
   int*          ints  = NULL;
   MPI_Aint*     addrs = NULL;
   MPI_Datatype* dtys  = NULL;

   /* Stuff for limiting how much complaining text it spews out */
   static int complaints = 3;
   static int last_complained_about_tycon = -987654321; /* presumably bogus */

   if (0)
      printf("walk_type %p\n", (void*)(unsigned long)ty);

   r = PMPI_Type_get_envelope( ty, &n_ints, &n_addrs, &n_dtys, &tycon );
   assert(r == MPI_SUCCESS);

   /* Handle the base cases fast(er/ish). */
   if (tycon == MPI_COMBINER_NAMED) {
      long sz = sizeofOneNamedTy(ty);
      if (sz > 0) {
         f(base, sz);
         return;
      }
      /* Hmm.  Perhaps it's a named struct?  Unfortunately we can't
         take them to bits so we have to do a really ugly hack, which
         makes assumptions about how the MPI implementation has laid
         out these types.  At least Open MPI 1.0.1 appears to put
         the 'val' field first.  MPICH2 agrees.
      */
      if (ty == MPI_2INT) {
         typedef struct { int val; int loc; } Ty;
         f(base + offsetof(Ty,val), sizeof(int));
         f(base + offsetof(Ty,loc), sizeof(int));
         return;
      }
      if (ty == MPI_LONG_INT) {
         typedef struct { long val; int loc; } Ty;
         f(base + offsetof(Ty,val), sizeof(long));
         f(base + offsetof(Ty,loc), sizeof(int));
         return;
      }
      if (ty == MPI_DOUBLE_INT) {
         typedef struct { double val; int loc; } Ty;
         f(base + offsetof(Ty,val), sizeof(double));
         f(base + offsetof(Ty,loc), sizeof(int));
         return;
      }
      if (ty == MPI_SHORT_INT) {
         typedef struct { short val; int loc; } Ty;
         f(base + offsetof(Ty,val), sizeof(short));
         f(base + offsetof(Ty,loc), sizeof(int));
         return;
      }
      if (ty == MPI_FLOAT_INT) {
         typedef struct { float val; int loc; } Ty;
         f(base + offsetof(Ty,val), sizeof(float));
         f(base + offsetof(Ty,loc), sizeof(int));
         return;
      }
      if (ty == MPI_LONG_DOUBLE_INT) {
         typedef struct { long double val; int loc; } Ty;
         f(base + offsetof(Ty,val), sizeof_long_double_image());
         f(base + offsetof(Ty,loc), sizeof(int));
         return;
      }
      if (ty == MPI_LB || ty == MPI_UB)
         return; /* have zero size, so nothing needs to be done */
      goto unhandled;
      /*NOTREACHED*/
   }

   if (0) {
      ex = extentOfTy(ty);
      printf("tycon 0x%llx %d %d %d (ext %d)\n",
             (unsigned long long int)tycon, 
             n_ints, n_addrs, n_dtys, (int)ex );
   }

   /* Now safe to do MPI_Type_get_contents */
   assert(n_ints  >= 0);
   assert(n_addrs >= 0);
   assert(n_dtys  >= 0);

   if (n_ints  > 0) {
      ints = malloc(n_ints * sizeof(int));
      assert(ints);
   }
   if (n_addrs > 0) {
      addrs = malloc(n_addrs * sizeof(MPI_Aint));
      assert(addrs);
   }
   if (n_dtys  > 0) {
      dtys = malloc(n_dtys * sizeof(MPI_Datatype));
      assert(dtys);
   }

   r = PMPI_Type_get_contents( ty, n_ints, n_addrs, n_dtys,
                                   ints, addrs, dtys );
   assert(r == MPI_SUCCESS);

   switch (tycon) {

      case MPI_COMBINER_CONTIGUOUS:
         assert(n_ints == 1 && n_addrs == 0 && n_dtys == 1);
	 walk_type_array( f, base, dtys[0], ints[0] );
         maybeFreeTy( &dtys[0] );
         break;

      case MPI_COMBINER_VECTOR:
         assert(n_ints == 3 && n_addrs == 0 && n_dtys == 1);
         ex = extentOfTy(dtys[0]);
         if (0)
         printf("vector count %d x (bl %d stride %d)\n", 
                (int)ints[0], (int)ints[1], (int)ints[2]);
         for (i = 0; i < ints[0]; i++) {
            walk_type_array( f, base + i * ints[2]/*stride*/ * ex,
                                dtys[0], ints[1]/*blocklength*/ );
         }
         maybeFreeTy( &dtys[0] );
         break;

      case MPI_COMBINER_HVECTOR:
         assert(n_ints == 2 && n_addrs == 1 && n_dtys == 1);
         ex = extentOfTy(dtys[0]);
         if (0)
         printf("hvector count %d x (bl %d hstride %d)\n", 
                (int)ints[0], (int)ints[1], (int)addrs[0]);
         for (i = 0; i < ints[0]; i++) {
            walk_type_array( f, base + i * addrs[0]/*hstride*/,
                                dtys[0], ints[1]/*blocklength*/ );
         }
         maybeFreeTy( &dtys[0] );
         break;

      case MPI_COMBINER_INDEXED:
         assert(n_addrs == 0 && n_dtys == 1);
         assert(n_ints > 0);
         assert(n_ints == 2 * ints[0] + 1);
         ex = extentOfTy(dtys[0]);
         for (i = 0; i < ints[0]; i++) {
            if (0) 
            printf("indexed (elem %d) off %d copies %d\n",
                   (int)i, ints[i+1+ints[0]], ints[i+1] );
            walk_type_array( f, base + ex * ints[i+1+ints[0]], 
                                dtys[0], ints[i+1] );
         }
         maybeFreeTy( &dtys[0] );
         break;

      case MPI_COMBINER_HINDEXED:
         assert(n_ints > 0);
         assert(n_ints == ints[0] + 1);
         assert(n_addrs == ints[0] && n_dtys == 1);
         ex = extentOfTy(dtys[0]);
         for (i = 0; i < ints[0]; i++) {
            if (0) 
            printf("hindexed (elem %d) hoff %d copies %d\n",
                   (int)i, (int)addrs[i], ints[i+1] );
            walk_type_array( f, base + addrs[i], 
                                dtys[0], ints[i+1] );
         }
         maybeFreeTy( &dtys[0] );
         break;

      case MPI_COMBINER_STRUCT:
         assert(n_addrs == n_ints-1);
         assert(n_dtys  == n_ints-1);
         assert(n_ints > 0);
         assert(n_ints == ints[0] + 1);
	 for (i = 0; i < ints[0]; i++) {
            if (0)
            printf("struct (elem %d limit %d) hoff %d copies %d\n", 
                   (int)i, (int)ints[0], (int)addrs[i], (int)ints[i+1]);
            walk_type_array( f, base + addrs[i], dtys[i], (long)ints[i+1] );
            maybeFreeTy( &dtys[i] );
	 }
         break;

      default:
         goto unhandled;

   }

   /* normal exit */
   if (ints)  free(ints);
   if (addrs) free(addrs);
   if (dtys)  free(dtys);
   return;

  unhandled:
   /* Complain, but limit the amount of complaining that can happen to
      the first 3 different unhandled tycons that show up, so as to
      avoid swamping users with thousands of duplicate messages. */
   if (complaints > 0 && tycon != last_complained_about_tycon) {
      complaints--;
      last_complained_about_tycon = tycon;
      if (tycon == MPI_COMBINER_NAMED) {
         fprintf(stderr, "%s %5d: walk_type: unhandled base type 0x%lx ",
                         preamble, my_pid, (long)ty);
         showTy(stderr, ty);
         fprintf(stderr, "\n");
      } else {
         fprintf(stderr, "%s %5d: walk_type: unhandled combiner 0x%lx\n",
                         preamble, my_pid, (long)tycon);
      }
   }
   if (ints)  free(ints);
   if (addrs) free(addrs);
   if (dtys)  free(dtys);
   if (opt_missing >= 2)
      barf("walk_type: unhandled combiner, strict checking selected");
}


/* Same as walk_type but apply 'f' to every element in an array of
   'count' items starting at 'base'.  The only purpose of pushing this
   into a different routine is so it can attempt to optimise the case
   where the array elements are contiguous and packed together without
   holes. */
static __inline__
void walk_type_array ( void(*f)(void*,long), char* base, 
                       MPI_Datatype elemTy, long count )
{
   long i, ex;

   assert(sizeof(unsigned long) == sizeof(char*));

   /* First see if we can do this the fast way. */
   ex = sizeofOneNamedTy(elemTy);

   if ( /* ty is a primitive type with power-of-2 size */
        (ex == 8 || ex == 4 || ex == 2 || ex == 1)
        && /* base is suitably aligned for ty */
           ( ((unsigned long)base) & (ex-1)) == 0)  {

      /* We're sure it's contiguous, so just paint/check it in one
         go. */
     if (0) printf("walk_type_array fast %ld of size %ld\n", count, ex );
     f ( base, count * ex );

   } else {

      /* Bad news.  We have to futz with each element individually.
         This could be very expensive.

         Note: subtle.  If ty is LONG_DOUBLE then the extent will be
         12, so the following loop will jump along in steps of 12, but
         the size painted by walk_type will be 10 since it uses
         sizeofOneNamedTy to establish the size of base types.  Which
         is what we need to happen. */
      ex = extentOfTy(elemTy);
      if (0) printf("walk_type_array SLOW %ld of size %ld\n", count, ex );
      for (i = 0; i < count; i++)
         walk_type( f, base + i * ex, elemTy );

   }
}


/* Hook so it's visible from outside (can be handy to dlopen/dlsym
   it) */
void mpiwrap_walk_type_EXTERNALLY_VISIBLE
    ( void(*f)(void*,long), char* base, MPI_Datatype ty )
{
   walk_type(f, base, ty);
}


/*------------------------------------------------------------*/
/*--- Address-range helpers                                ---*/
/*------------------------------------------------------------*/

/* ----------------
   Do corresponding checks on memory areas defined using a 
   straightforward (start, length) description.
   ----------------
*/

static __inline__
void check_mem_is_defined_untyped ( void* buffer, long nbytes )
{
   if (nbytes > 0) {
      VALGRIND_CHECK_MEM_IS_DEFINED(buffer, nbytes);
   }
}

static __inline__
void check_mem_is_addressable_untyped ( void* buffer, long nbytes )
{
   if (nbytes > 0) {
      VALGRIND_CHECK_MEM_IS_ADDRESSABLE(buffer, nbytes);
   }
}

static __inline__
void make_mem_defined_if_addressable_untyped ( void* buffer, long nbytes )
{
   if (nbytes > 0) {
      VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE(buffer, nbytes);
   }
}

static __inline__
void make_mem_defined_if_addressable_if_success_untyped ( int err, 
                                       void* buffer, long nbytes )
{
   if (err == MPI_SUCCESS && nbytes > 0) {
      VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE(buffer, nbytes);
   }
}


/* ----------------
   Do checks on memory areas defined using the MPI (buffer, count,
   type) convention.
   ----------------
*/

/* Check that the specified area is both addressible and contains
   initialised data, and cause V to complain if not. */

static __inline__
void check_mem_is_defined ( char* buffer, long count, MPI_Datatype datatype )
{
   walk_type_array( check_mem_is_defined_untyped, buffer, datatype, count );
}


/* Check that the specified area is addressible, and cause V to
   complain if not. Doesn't matter whether the data there is
   initialised or not. */

static __inline__
void check_mem_is_addressable ( void *buffer, long count, MPI_Datatype datatype )
{
   walk_type_array( check_mem_is_addressable_untyped, buffer, datatype, count );
}


/* Set the specified area to 'defined for each byte which is
   addressible' state. */

static __inline__
void make_mem_defined_if_addressable ( void *buffer, int count, MPI_Datatype datatype )
{
   walk_type_array( make_mem_defined_if_addressable_untyped,
                    buffer, datatype, count );
}

static __inline__
void 
make_mem_defined_if_addressable_if_success ( int err, void *buffer, int count, 
                                             MPI_Datatype datatype )
{
   if (err == MPI_SUCCESS)
      make_mem_defined_if_addressable(buffer, count, datatype);
}


/*------------------------------------------------------------*/
/*---                                                      ---*/
/*--- The wrappers proper.   They are listed in the order  ---*/
/*--- in which they appear in "MPI: A Message-Passing      ---*/
/*--- Interface Standard, MPIF, Nov 15 2003" (the MPI 1.1  ---*/
/*--- spec.  All unimplemented wrappers are listed at the  ---*/
/*--- end of the file.  The list of function names is      ---*/
/*--- taken from the headers of Open MPI svn r9191.        ---*/
/*--- Hopefully it is a complete list of all the MPI 2     ---*/
/*--- functions.                                           ---*/
/*---                                                      ---*/
/*------------------------------------------------------------*/

/* Handy abbreviation */
#define WRAPPER_FOR(name) I_WRAP_FNNAME_U(name)

/* Generates (conceptually) a wrapper which does nothing.  In
   fact just generate no wrapper at all. */
#define HAS_NO_WRAPPER(basename) /* */


/*------------------------------------------------------------*/
/*---                                                      ---*/
/*--- Sec 3.2, Blocking Send and Receive Operations        ---*/
/*---                                                      ---*/
/*------------------------------------------------------------*/

/* --- {,B,S,R}Send --- */
/* pre: rd: (buf,count,datatype) */
static
int generic_Send(void *buf, int count, MPI_Datatype datatype, 
                            int dest, int tag, MPI_Comm comm)
{
   OrigFn fn;
   int    err;
   VALGRIND_GET_ORIG_FN(fn);
   before("{,B,S,R}Send");
   check_mem_is_defined(buf, count, datatype);
   if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
   CALL_FN_W_6W(err, fn, buf,count,datatype,dest,tag,comm);
   if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
   after("{,B,S,R}Send", err);
   return err;
}
int WRAPPER_FOR(PMPI_Send)(void *buf, int count, MPI_Datatype datatype, 
                           int dest, int tag, MPI_Comm comm) {
   return generic_Send(buf,count,datatype, dest,tag,comm);
}
int WRAPPER_FOR(PMPI_Bsend)(void *buf, int count, MPI_Datatype datatype, 
                            int dest, int tag, MPI_Comm comm) {
   return generic_Send(buf,count,datatype, dest,tag,comm);
}
int WRAPPER_FOR(PMPI_Ssend)(void *buf, int count, MPI_Datatype datatype, 
                            int dest, int tag, MPI_Comm comm) {
   return generic_Send(buf,count,datatype, dest,tag,comm);
}
int WRAPPER_FOR(PMPI_Rsend)(void *buf, int count, MPI_Datatype datatype, 
                            int dest, int tag, MPI_Comm comm) {
   return generic_Send(buf,count,datatype, dest,tag,comm);
}

/* --- Recv --- */
/* pre:  must be writable: (buf,count,datatype)
         must be writable: status
   post: make readable: (buf,recv_count,datatype)
         where recv_count is determined from *status
*/
int WRAPPER_FOR(PMPI_Recv)(void *buf, int count, MPI_Datatype datatype, 
                           int source, int tag, 
                           MPI_Comm comm, MPI_Status *status)
{
   OrigFn     fn;
   int        err, recv_count = 0;
   MPI_Status fake_status;
   VALGRIND_GET_ORIG_FN(fn);
   before("Recv");
   if (isMSI(status))
      status = &fake_status;
   check_mem_is_addressable(buf, count, datatype);
   check_mem_is_addressable_untyped(status, sizeof(*status));
   if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
   CALL_FN_W_7W(err, fn, buf,count,datatype,source,tag,comm,status);
   if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
   if (err == MPI_SUCCESS && count_from_Status(&recv_count,datatype,status)) {
      make_mem_defined_if_addressable(buf, recv_count, datatype);
   }
   after("Recv", err);
   return err;
}

/* --- Get_count --- */
/* pre:  must be readable: *status
   post: make defined: *count -- don't bother, libmpi will surely do this
*/
int WRAPPER_FOR(PMPI_Get_count)(MPI_Status* status, 
                                MPI_Datatype ty, int* count )
{
   OrigFn fn;
   int    err;
   VALGRIND_GET_ORIG_FN(fn);
   before("Get_count");
   check_mem_is_defined_untyped(status, sizeof(*status));
   if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
   CALL_FN_W_WWW(err, fn, status,ty,count);
   if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
   after("Get_count", err);
   return err;
}


/*------------------------------------------------------------*/
/*---                                                      ---*/
/*--- Sec 3.7, Nonblocking communication                   ---*/
/*---                                                      ---*/
/*------------------------------------------------------------*/

/* Maintain a table that makes it possible for the wrappers to
   complete MPI_Irecv successfully.

   The issue is that MPI_Irecv states the recv buffer and returns
   immediately, giving a handle (MPI_Request) for the transaction.
   Later the user will have to poll for completion with MPI_Wait etc,
   and at that point these wrappers have to paint the recv buffer.
   But the recv buffer details are not presented to MPI_Wait - only
   the handle is.  We therefore have to use a shadow table
   (sReqs{,_size,_used,_lock}) which associates uncompleted
   MPI_Requests with the corresponding buffer address/count/type.

   Only read requests are placed in the table, since there is no need
   to do any buffer painting following completion of an Isend - all
   the checks for that are done at the time Isend is called.

   Care has to be take to remove completed requests from the table.

   Access to the table is guarded by sReqs_lock so as to make it
   thread-safe.
*/

typedef
   struct {
      Bool         inUse;
      MPI_Request  key;
      void*        buf;
      int          count;
      MPI_Datatype datatype;
   }
   ShadowRequest;

static ShadowRequest*  sReqs      = NULL;
static int             sReqs_size = 0;
static int             sReqs_used = 0;
static pthread_mutex_t sReqs_lock = PTHREAD_MUTEX_INITIALIZER;

#define LOCK_SREQS                                  \
  do { int pr = pthread_mutex_lock(&sReqs_lock);    \
       assert(pr == 0);                             \
  } while (0)

#define UNLOCK_SREQS                                \
  do { int pr = pthread_mutex_unlock(&sReqs_lock);  \
       assert(pr == 0);                             \
  } while (0)


/* Ensure the sReqs expandable array has at least one free slot, by
   copying it into a larger one if necessary.  NOTE: sReqs_lock is
   held throughout this procedure.*/
static void ensure_sReq_space ( void )
{
   int            i;
   ShadowRequest* sReqs2;
   if (sReqs_used == sReqs_size) {
      sReqs_size = sReqs_size==0 ? 2 : 2*sReqs_size;
      sReqs2 = malloc( sReqs_size * sizeof(ShadowRequest) );
      if (sReqs2 == NULL) {
         UNLOCK_SREQS;
         barf("add_shadow_Request: malloc failed.\n");
      }
      for (i = 0; i < sReqs_used; i++)
         sReqs2[i] = sReqs[i];
      if (sReqs)
         free(sReqs);
      sReqs = sReqs2;
   }
   assert(sReqs_used < sReqs_size);
}


/* Find shadow info for 'request', or NULL if none. */

static 
ShadowRequest* find_shadow_Request ( MPI_Request request )
{
   ShadowRequest* ret = NULL;
   int i;
   LOCK_SREQS;
   for (i = 0; i < sReqs_used; i++) {
      if (sReqs[i].inUse && eq_MPI_Request(sReqs[i].key,request)) {
         ret = &sReqs[i];
         break;
      }
   }
   UNLOCK_SREQS;
   return ret;
}


/* Delete shadow info for 'request', if any. */

static void delete_shadow_Request ( MPI_Request request )
{
   int i;
   LOCK_SREQS;
   for (i = 0; i < sReqs_used; i++) {
      if (sReqs[i].inUse && eq_MPI_Request(sReqs[i].key,request)) {
         sReqs[i].inUse = False;
         break;
      }
   }
   UNLOCK_SREQS;
}


/* Add a shadow for 'request', overwriting any old binding for it. */

static 
void add_shadow_Request( MPI_Request request, 
                         void* buf, int count, 
                         MPI_Datatype datatype )
{
   int i, ix = -1;
   LOCK_SREQS;
   assert(sReqs_used >= 0);
   assert(sReqs_size >= 0);
   assert(sReqs_used <= sReqs_size);
   if (sReqs == NULL) assert(sReqs_size == 0);

   /* First of all see if we already have a binding for this key; if
      so just replace it, and have done. */
   for (i = 0; i < sReqs_used; i++) {
      if (sReqs[i].inUse && eq_MPI_Request(sReqs[i].key,request)) {
         ix = i;
         break;
      }
   }

   if (ix < 0) {
      /* Ok, we don't have it, so will have to add it.  First search
         to see if there is an existing empty slot. */
      for (i = 0; i < sReqs_used; i++) {
         if (!sReqs[i].inUse) {
            ix = i;
            break;
         }
      }
   }

   /* No empty slots.  Allocate a new one. */
   if (ix < 0) {
      ensure_sReq_space();
      assert(sReqs_used < sReqs_size);
      ix = sReqs_used;
      sReqs_used++;
   }

   assert(ix >= 0 && ix < sReqs_used);
   assert(sReqs_used <= sReqs_size);

   sReqs[ix].inUse    = True;
   sReqs[ix].key      = request;
   sReqs[ix].buf      = buf;
   sReqs[ix].count    = count;
   sReqs[ix].datatype = datatype;

   UNLOCK_SREQS;
   if (opt_verbosity > 1)
      fprintf(stderr, "%s %5d: sReq+ 0x%lx -> b/c/d %p/%d/0x%lx [slot %d]\n",
                      preamble, my_pid, (unsigned long)request, 
                                buf, count, (long)datatype, ix);
}

static 
MPI_Request* clone_Request_array ( int count, MPI_Request* orig )
{
   MPI_Request* copy;
   int i;
   LOCK_SREQS;
   if (count < 0) 
      count = 0; /* Hmm.  Call Mulder and Scully. */
   copy = malloc( count * sizeof(MPI_Request) );
   if (copy == NULL && count > 0) {
      UNLOCK_SREQS;
      barf("clone_Request_array: malloc failed");
   }
   for (i = 0; i < count; i++)
      copy[i] = orig[i];
   UNLOCK_SREQS;
   return copy;
}

#undef LOCK_SREQS
#undef UNLOCK_SREQS


static void maybe_complete ( Bool         error_in_status,
                             MPI_Request  request_before,
                             MPI_Request  request_after,
                             MPI_Status*  status )
{
   int recv_count = 0;
   ShadowRequest* shadow;
   /* How do we know if this is an Irecv request that has now
      finished successfully? 
      
      request_before isn't MPI_REQUEST_NULL
      and request_before is found in the shadow table
      and request_after *is* MPI_REQUEST_NULL
      and (if error_in_status then status.MPI_ERROR is MPI_SUCCESS)

      (when error_in_status == False, then we expect not to get
      called at all if there was an error.)
   */
   if (request_before != MPI_REQUEST_NULL
       && request_after == MPI_REQUEST_NULL
       && (error_in_status ? status->MPI_ERROR == MPI_SUCCESS : True)
       && ( (shadow=find_shadow_Request(request_before)) != NULL) ) {
      /* The Irecv detailed in 'shadow' completed.  Paint the result
         buffer, and delete the entry. */
      if (count_from_Status(&recv_count, shadow->datatype, status)) {
         make_mem_defined_if_addressable(shadow->buf, recv_count, shadow->datatype);
         if (opt_verbosity > 1)
            fprintf(stderr, "%s %5d: sReq- %p (completed)\n", 
                            preamble, my_pid, request_before);
      }
      delete_shadow_Request(request_before);
   }
}


/* --- Isend --- */
/* rd: (buf,count,datatype) */
/* wr: *request */
static __inline__
int generic_Isend(void *buf, int count, MPI_Datatype datatype, 
                             int dest, int tag, MPI_Comm comm, 
                             MPI_Request* request)
{
   OrigFn fn;
   int    err;
   VALGRIND_GET_ORIG_FN(fn);
   before("{,B,S,R}Isend");
   check_mem_is_defined(buf, count, datatype);
   check_mem_is_addressable_untyped(request, sizeof(*request));
   if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
   CALL_FN_W_7W(err, fn, buf,count,datatype,dest,tag,comm,request);
   if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
   make_mem_defined_if_addressable_if_success_untyped(err, request, sizeof(*request));
   after("{,B,S,R}Isend", err);
   return err;
}
int WRAPPER_FOR(PMPI_Isend)(void *buf, int count, MPI_Datatype datatype, 
                            int dest, int tag, MPI_Comm comm, 
                            MPI_Request* request) {
   return generic_Isend(buf,count,datatype, dest,tag,comm, request);
}
int WRAPPER_FOR(PMPI_Ibsend)(void *buf, int count, MPI_Datatype datatype, 
                             int dest, int tag, MPI_Comm comm, 
                             MPI_Request* request) {
   return generic_Isend(buf,count,datatype, dest,tag,comm, request);
}
int WRAPPER_FOR(PMPI_Issend)(void *buf, int count, MPI_Datatype datatype, 
                             int dest, int tag, MPI_Comm comm, 
                             MPI_Request* request) {
   return generic_Isend(buf,count,datatype, dest,tag,comm, request);
}
int WRAPPER_FOR(PMPI_Irsend)(void *buf, int count, MPI_Datatype datatype, 
                             int dest, int tag, MPI_Comm comm, 
                             MPI_Request* request) {
   return generic_Isend(buf,count,datatype, dest,tag,comm, request);
}


/* --- Irecv --- */
/* pre:  must be writable: (buf,count,datatype), *request
   post: make readable *request
         add a request->(buf,count,ty) binding to the 
         shadow request table. 
*/
int WRAPPER_FOR(PMPI_Irecv)( void* buf, int count, MPI_Datatype datatype, 
                             int source, int tag, MPI_Comm comm, 
                             MPI_Request* request )
{
   OrigFn fn;
   int    err;
   VALGRIND_GET_ORIG_FN(fn);
   before("Irecv");
   check_mem_is_addressable(buf, count, datatype);
   check_mem_is_addressable_untyped(request, sizeof(*request));
   if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
   CALL_FN_W_7W(err, fn, buf,count,datatype,source,tag,comm,request);
   if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
   if (err == MPI_SUCCESS) {
      make_mem_defined_if_addressable_untyped(request, sizeof(*request));
      add_shadow_Request( *request, buf,count,datatype );
   }
   after("Irecv", err);
   return err;
}

/* --- Wait --- */
/* The MPI1 spec (imprecisely) defines 3 request states:
   - "null"     if the request is MPI_REQUEST_NULL
   - "inactive" if not "null" and not associated with ongoing comms
   - "active"   if not "null" and is associated with ongoing comms
*/
int WRAPPER_FOR(PMPI_Wait)( MPI_Request* request,
                            MPI_Status* status )
{
   MPI_Request  request_before;
   MPI_Status   fake_status;
   OrigFn       fn;
   int          err;
   VALGRIND_GET_ORIG_FN(fn);
   before("Wait");
   if (isMSI(status))
      status = &fake_status;
   check_mem_is_addressable_untyped(status, sizeof(MPI_Status));
   check_mem_is_defined_untyped(request, sizeof(MPI_Request));
   request_before = *request;
   if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
   CALL_FN_W_WW(err, fn, request,status);
   if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
   if (err == MPI_SUCCESS) {
      maybe_complete(False/*err in status?*/, 
                     request_before, *request, status);
      make_mem_defined_if_addressable_untyped(status, sizeof(MPI_Status));
   }
   after("Wait", err);
   return err;
}

/* --- Waitany --- */
int WRAPPER_FOR(PMPI_Waitany)( int count,
                               MPI_Request* requests,
                               int* index,
                               MPI_Status* status )
{
   MPI_Request* requests_before = NULL;
   MPI_Status   fake_status;
   OrigFn       fn;
   int          err, i;
   VALGRIND_GET_ORIG_FN(fn);
   before("Waitany");
   if (isMSI(status))
      status = &fake_status;
   if (0) fprintf(stderr, "Waitany: %d\n", count);
   check_mem_is_addressable_untyped(index, sizeof(int));
   check_mem_is_addressable_untyped(status, sizeof(MPI_Status));
   for (i = 0; i < count; i++) {
      check_mem_is_defined_untyped(&requests[i], sizeof(MPI_Request));
   }
   requests_before = clone_Request_array( count, requests );
   if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
   CALL_FN_W_WWWW(err, fn, count,requests,index,status);
   if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
   if (err == MPI_SUCCESS && *index >= 0 && *index < count) {
      maybe_complete(False/*err in status?*/, 
                     requests_before[*index], requests[*index], status);
      make_mem_defined_if_addressable_untyped(status, sizeof(MPI_Status));
   }
   if (requests_before)
      free(requests_before);
   after("Waitany", err);
   return err;
}

/* --- Waitall --- */
int WRAPPER_FOR(PMPI_Waitall)( int count, 
                               MPI_Request* requests,
                               MPI_Status* statuses )
{
   MPI_Request* requests_before = NULL;
   OrigFn       fn;
   int          err, i;
   Bool         free_sta = False;
   VALGRIND_GET_ORIG_FN(fn);
   before("Waitall");
   if (0) fprintf(stderr, "Waitall: %d\n", count);
   if (isMSI(statuses)) {
      free_sta = True;
      statuses = malloc( (count < 0 ? 0 : count) * sizeof(MPI_Status) );
   }
   for (i = 0; i < count; i++) {
      check_mem_is_addressable_untyped(&statuses[i], sizeof(MPI_Status));
      check_mem_is_defined_untyped(&requests[i], sizeof(MPI_Request));
   }
   requests_before = clone_Request_array( count, requests );
   if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
   CALL_FN_W_WWW(err, fn, count,requests,statuses);
   if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
   if (err == MPI_SUCCESS /*complete success*/
       || err == MPI_ERR_IN_STATUS /* partial success */) {
      Bool e_i_s = err == MPI_ERR_IN_STATUS;
      for (i = 0; i < count; i++) {
         maybe_complete(e_i_s, requests_before[i], requests[i], 
                               &statuses[i]);
         make_mem_defined_if_addressable_untyped(&statuses[i],
                                                 sizeof(MPI_Status));
      }
   }
   if (requests_before)
      free(requests_before);
   if (free_sta)
      free(statuses);
   after("Waitall", err);
   return err;
}

/* --- Test --- */
/* nonblocking version of Wait */
int WRAPPER_FOR(PMPI_Test)( MPI_Request* request, int* flag, 
                            MPI_Status* status )
{
   MPI_Request  request_before;
   MPI_Status   fake_status;
   OrigFn       fn;
   int          err;
   VALGRIND_GET_ORIG_FN(fn);
   before("Test");
   if (isMSI(status))
      status = &fake_status;
   check_mem_is_addressable_untyped(status, sizeof(MPI_Status));
   check_mem_is_addressable_untyped(flag, sizeof(int));
   check_mem_is_defined_untyped(request, sizeof(MPI_Request));
   request_before = *request;
   if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
   CALL_FN_W_WWW(err, fn, request,flag,status);
   if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
   if (err == MPI_SUCCESS && *flag) {
      maybe_complete(False/*err in status?*/, 
                     request_before, *request, status);
      make_mem_defined_if_addressable_untyped(status, sizeof(MPI_Status));
   }
   after("Test", err);
   return err;
}

/* --- Testall --- */
/* nonblocking version of Waitall */
int WRAPPER_FOR(PMPI_Testall)( int count, MPI_Request* requests,
                               int* flag, MPI_Status* statuses )
{
   MPI_Request* requests_before = NULL;
   OrigFn       fn;
   int          err, i;
   Bool         free_sta = False;
   VALGRIND_GET_ORIG_FN(fn);
   before("Testall");
   if (0) fprintf(stderr, "Testall: %d\n", count);
   if (isMSI(statuses)) {
      free_sta = True;
      statuses = malloc( (count < 0 ? 0 : count) * sizeof(MPI_Status) );
   }
   check_mem_is_addressable_untyped(flag, sizeof(int));
   for (i = 0; i < count; i++) {
      check_mem_is_addressable_untyped(&statuses[i], sizeof(MPI_Status));
      check_mem_is_defined_untyped(&requests[i], sizeof(MPI_Request));
   }
   requests_before = clone_Request_array( count, requests );
   if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
   CALL_FN_W_WWWW(err, fn, count,requests,flag,statuses);
   if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
   /* Urk.  Is the following "if (...)" really right?  I don't know. */
   if (*flag
       && (err == MPI_SUCCESS /*complete success*/
           || err == MPI_ERR_IN_STATUS /* partial success */)) {
      Bool e_i_s = err == MPI_ERR_IN_STATUS;
      for (i = 0; i < count; i++) {
         maybe_complete(e_i_s, requests_before[i], requests[i], 
                               &statuses[i]);
         make_mem_defined_if_addressable_untyped(&statuses[i],
                                                 sizeof(MPI_Status));
      }
   }
   if (requests_before)
      free(requests_before);
   if (free_sta)
      free(statuses);
   after("Testall", err);
   return err;
}

/* --- Iprobe --- */
/* pre:  must-be-writable: *flag, *status */
/* post: make-readable *flag
         if *flag==True  make-defined *status */
int WRAPPER_FOR(PMPI_Iprobe)(int source, int tag, 
                             MPI_Comm comm, 
                             int* flag, MPI_Status* status)
{
   MPI_Status fake_status;
   OrigFn     fn;
   int        err;
   VALGRIND_GET_ORIG_FN(fn);
   before("Iprobe");
   if (isMSI(status))
      status = &fake_status;
   check_mem_is_addressable_untyped(flag, sizeof(*flag));
   check_mem_is_addressable_untyped(status, sizeof(*status));
   if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
   CALL_FN_W_5W(err, fn, source,tag,comm,flag,status);
   if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
   if (err == MPI_SUCCESS) {
      make_mem_defined_if_addressable_untyped(flag, sizeof(*flag));
      if (*flag)
         make_mem_defined_if_addressable_untyped(status, sizeof(*status));
   }
   after("Iprobe", err);
   return err;
}

/* --- Probe --- */
/* pre:  must-be-writable *status */
/* post: make-defined *status */
int WRAPPER_FOR(PMPI_Probe)(int source, int tag,
                            MPI_Comm comm, MPI_Status* status)
{
   MPI_Status fake_status;
   OrigFn     fn;
   int        err;
   VALGRIND_GET_ORIG_FN(fn);
   before("Probe");
   if (isMSI(status))
      status = &fake_status;
   check_mem_is_addressable_untyped(status, sizeof(*status));
   if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
   CALL_FN_W_WWWW(err, fn, source,tag,comm,status);
   if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
   make_mem_defined_if_addressable_if_success_untyped(err, status, sizeof(*status));
   after("Probe", err);
   return err;
}

/* --- Cancel --- */
/* Wrapping PMPI_Cancel is interesting only to the extent that we need
   to be able to detect when a request should be removed from our
   shadow table due to cancellation. */
int WRAPPER_FOR(PMPI_Cancel)(MPI_Request* request)
{
   OrigFn      fn;
   int         err;
   MPI_Request tmp;
   VALGRIND_GET_ORIG_FN(fn);
   before("Cancel");
   check_mem_is_addressable_untyped(request, sizeof(*request));
   tmp = *request;
   if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
   CALL_FN_W_W(err, fn, request);
   if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
   if (err == MPI_SUCCESS)
      delete_shadow_Request(tmp);
   after("Cancel", err);
   return err;
}


/*------------------------------------------------------------*/
/*---                                                      ---*/
/*--- Sec 3.10, Send-receive                               ---*/
/*---                                                      ---*/
/*------------------------------------------------------------*/

/* --- Sendrecv --- */
/* pre: must be readable: (sendbuf,sendcount,sendtype)
        must be writable: (recvbuf,recvcount,recvtype)
   post: make readable: (recvbuf,recvcount_actual,datatype)
         where recvcount_actual is determined from *status
*/
int WRAPPER_FOR(PMPI_Sendrecv)(
       void *sendbuf, int sendcount, MPI_Datatype sendtype,
       int dest, int sendtag, 
       void *recvbuf, int recvcount, MPI_Datatype recvtype, 
       int source, int recvtag,
       MPI_Comm comm,  MPI_Status *status)
{
   MPI_Status fake_status;
   OrigFn     fn;
   int        err, recvcount_actual = 0;
   VALGRIND_GET_ORIG_FN(fn);
   before("Sendrecv");
   if (isMSI(status))
      status = &fake_status;
   check_mem_is_defined(sendbuf, sendcount, sendtype);
   check_mem_is_addressable(recvbuf, recvcount, recvtype);
   check_mem_is_addressable_untyped(status, sizeof(*status));
   if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
   CALL_FN_W_12W(err, fn, sendbuf,sendcount,sendtype,dest,sendtag,
                          recvbuf,recvcount,recvtype,source,recvtag,
                          comm,status);
   if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
   if (err == MPI_SUCCESS 
       && count_from_Status(&recvcount_actual,recvtype,status)) {
      make_mem_defined_if_addressable(recvbuf, recvcount_actual, recvtype);
   }
   after("Sendrecv", err);
   return err;
}


/*------------------------------------------------------------*/
/*---                                                      ---*/
/*--- Sec 3.12, Derived datatypes                          ---*/
/*---                                                      ---*/
/*------------------------------------------------------------*/

/* --- Address --- */
/* Does this have anything worth checking? */
HAS_NO_WRAPPER(Address)

/* --- MPI 2 stuff --- */
/* Type_extent, Type_get_contents and Type_get_envelope sometimes get
   used intensively by the type walker (walk_type).  There's no reason
   why they couldn't be properly wrapped if needed, but doing so slows
   everything down, so don't bother until needed. */
HAS_NO_WRAPPER(Type_extent)
HAS_NO_WRAPPER(Type_get_contents)
HAS_NO_WRAPPER(Type_get_envelope)

/* --- Type_commit --- */
int WRAPPER_FOR(PMPI_Type_commit)( MPI_Datatype* ty )
{
   OrigFn fn;
   int    err;
   VALGRIND_GET_ORIG_FN(fn);
   before("Type_commit");
   check_mem_is_defined_untyped(ty, sizeof(*ty));
   if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
   CALL_FN_W_W(err, fn, ty);
   if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
   after("Type_commit", err);
   return err;
}

/* --- Type_free --- */
int WRAPPER_FOR(PMPI_Type_free)( MPI_Datatype* ty )
{
   OrigFn fn;
   int    err;
   VALGRIND_GET_ORIG_FN(fn);
   before("Type_free");
   check_mem_is_defined_untyped(ty, sizeof(*ty));
   if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
   CALL_FN_W_W(err, fn, ty);
   if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
   after("Type_free", err);
   return err;
}


/*------------------------------------------------------------*/
/*---                                                      ---*/
/*--- Sec 3.13, Pack and unpack                            ---*/
/*---                                                      ---*/
/*------------------------------------------------------------*/

/* --- Pack --- */
/* pre: must be readable: position
        must be readable: (inbuf,incount,datatype)
        must be writable: outbuf[0 .. outsize-1]
        must be writable: outbuf[*position .. 
                                 *position - 1 
                                 + however much space PMPI_Pack_size 
                                   says we will need]
   post: make readable: outbuf[old *position .. new *position]
*/
int WRAPPER_FOR(PMPI_Pack)( void* inbuf, int incount, MPI_Datatype datatype, 
                            void* outbuf, int outsize, 
                            int* position, MPI_Comm comm ) 
{
   OrigFn fn;
   int    err, szB = 0;
   int    position_ORIG = *position;
   VALGRIND_GET_ORIG_FN(fn);
   before("Pack");
   /* stay sane */
   check_mem_is_defined_untyped(position, sizeof(*position));
   /* check input */
   check_mem_is_defined(inbuf, incount, datatype);
   /* check output area's stated bounds make sense */
   check_mem_is_addressable_untyped(outbuf, outsize);
   /* check output area's actual used size properly */
   err = PMPI_Pack_size( incount, datatype, comm, &szB );
   if (err == MPI_SUCCESS && szB > 0) {
      check_mem_is_addressable_untyped( 
         ((char*)outbuf) + position_ORIG, szB
      );
   }

   if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
   CALL_FN_W_7W(err, fn, inbuf,incount,datatype, outbuf,outsize,position, comm);
   if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;

   if (err == MPI_SUCCESS && (*position) > position_ORIG) {
      /* paint output */
      make_mem_defined_if_addressable_untyped( 
         ((char*)outbuf) + position_ORIG, *position - position_ORIG
      );
   }
   after("Pack", err);
   return err;
}

/* --- Unpack --- */
/* pre: must be readable: position
        must be writable: (outbuf,outcount,datatype)
        must be writable: outbuf[0 .. outsize-1]
        must be writable: outbuf[*position .. 
                                 *position - 1 
                                 + however much space PMPI_Pack_size 
                                   says we will need]
   post: make readable: (outbuf,outcount,datatype)
         and also do a readability check of
         inbuf[old *position .. new *position]
*/
int WRAPPER_FOR(PMPI_Unpack)( void* inbuf, int insize, int* position,
                              void* outbuf, int outcount, MPI_Datatype datatype, 
                              MPI_Comm comm )
{
   OrigFn fn;
   int    err, szB = 0;
   int    position_ORIG = *position;
   VALGRIND_GET_ORIG_FN(fn);
   before("Unpack");
   /* stay sane */
   check_mem_is_defined_untyped(position, sizeof(*position));
   /* check output area is accessible */
   check_mem_is_addressable(outbuf, outcount, datatype);
   /* check input area's stated bounds make sense */
   check_mem_is_addressable_untyped(inbuf, insize);
   /* check input area's actual used size properly */
   err = PMPI_Pack_size( outcount, datatype, comm, &szB );
   if (err == MPI_SUCCESS && szB > 0) {
      check_mem_is_addressable_untyped( 
         ((char*)inbuf) + position_ORIG, szB
      );
   }

   if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
   CALL_FN_W_7W(err, fn, inbuf,insize,position, outbuf,outcount,datatype, comm);
   if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;

   if (err == MPI_SUCCESS && (*position) > position_ORIG) {
      /* recheck input more carefully */
      check_mem_is_defined_untyped( 
         ((char*)inbuf) + position_ORIG, *position - position_ORIG
      );
      /* paint output */
      make_mem_defined_if_addressable( outbuf, outcount, datatype );
   }
   after("Unpack", err);
   return err;
}


/*------------------------------------------------------------*/
/*---                                                      ---*/
/*--- Sec 4.4, Broadcast                                   ---*/
/*---                                                      ---*/
/*------------------------------------------------------------*/

/* --- Bcast --- */
/* pre:  must-be-readable (buffer,count,datatype) for rank==root
         must-be-writable (buffer,count,datatype) for rank!=root
   post: make-readable (buffer,count,datatype) for all

   Resulting behaviour is: if root sends uninitialised stuff, then
   V complains, but then all ranks, including itself, see the buffer
   as initialised after that.
*/
int WRAPPER_FOR(PMPI_Bcast)(void *buffer, int count, 
                            MPI_Datatype datatype,
                            int root, MPI_Comm comm)
{
   OrigFn fn;
   int    err;
   Bool  i_am_sender;
   VALGRIND_GET_ORIG_FN(fn);
   before("Bcast");
   i_am_sender = root == comm_rank(comm);
   if (i_am_sender) {
      check_mem_is_defined(buffer, count, datatype);
   } else {
      check_mem_is_addressable(buffer, count, datatype);
   }
   if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
   CALL_FN_W_5W(err, fn, buffer,count,datatype,root,comm);
   if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
   make_mem_defined_if_addressable_if_success(err, buffer, count, datatype);
   after("Bcast", err);
   return err; 
}


/*------------------------------------------------------------*/
/*---                                                      ---*/
/*--- Sec 4.5, Gather                                      ---*/
/*---                                                      ---*/
/*------------------------------------------------------------*/

/* --- Gather --- */
/* JRS 20060217: I don't really understand this.  Each process is
   going to send sendcount items of type sendtype to the root.  So
   the root is going to receive comm_size*sendcount items of type
   sendtype (right?)  So why specify recvcount and recvtype?

   Anyway, assuming the MPI Spec is correct (seems likely :-) we have:

   pre:  (all)        must be readable: (sendbuf,sendcount,sendtype)
         (root only): must be writable: (recvbuf,recvcount * comm_size,recvtype)
   post: (root only): make readable: (recvbuf,recvcount * comm_size,recvtype)
*/
int WRAPPER_FOR(PMPI_Gather)(
       void *sendbuf, int sendcount, MPI_Datatype sendtype,
       void *recvbuf, int recvcount, MPI_Datatype recvtype,
       int root, MPI_Comm comm)
{
   OrigFn fn;
   int    err, me, sz;
   VALGRIND_GET_ORIG_FN(fn);
   before("Gather");
   me = comm_rank(comm);
   sz = comm_size(comm);
   check_mem_is_defined(sendbuf, sendcount, sendtype);
   if (me == root)
      check_mem_is_addressable(recvbuf, recvcount * sz, recvtype);
   if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
   CALL_FN_W_8W(err, fn, sendbuf,sendcount,sendtype,
                         recvbuf,recvcount,recvtype,
                         root,comm);
   if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
   if (me == root)
      make_mem_defined_if_addressable_if_success(err, recvbuf, recvcount * sz, recvtype);
   after("Gather", err);
   return err;
}


/*------------------------------------------------------------*/
/*---                                                      ---*/
/*--- Sec 4.6, Scatter                                     ---*/
/*---                                                      ---*/
/*------------------------------------------------------------*/

/* pre:  (root only): must be readable: (sendbuf,sendcount * comm_size,sendtype)
         (all):       must be writable: (recvbuf,recvbuf,recvtype)
   post: (all):       make defined: (recvbuf,recvbuf,recvtype)
*/
int WRAPPER_FOR(PMPI_Scatter)(
       void* sendbuf, int sendcount, MPI_Datatype sendtype,
       void* recvbuf, int recvcount, MPI_Datatype recvtype,
       int root, MPI_Comm comm)
{
   OrigFn fn;
   int    err, me, sz;
   VALGRIND_GET_ORIG_FN(fn);
   before("Scatter");
   me = comm_rank(comm);
   sz = comm_size(comm);
   check_mem_is_addressable(recvbuf, recvcount, recvtype);
   if (me == root)
      check_mem_is_defined(sendbuf, sendcount * sz, sendtype);
   if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
   CALL_FN_W_8W(err, fn, sendbuf,sendcount,sendtype,
                         recvbuf,recvcount,recvtype,
                         root,comm);
   if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
   make_mem_defined_if_addressable_if_success(err, recvbuf, recvcount, recvtype);
   after("Scatter", err);
   return err;
}


/*------------------------------------------------------------*/
/*---                                                      ---*/
/*--- Sec 4.8, All-to-All Scatter/Gather                   ---*/
/*---                                                      ---*/
/*------------------------------------------------------------*/

/* pre:  (all) must be readable: (sendbuf,sendcount * comm_size,sendtype)
         (all) must be writable: (recvbuf,recvcount * comm_size,recvtype)
   post: (all) make defined:     (recvbuf,recvcount * comm_size,recvtype)
*/
int WRAPPER_FOR(PMPI_Alltoall)(
       void* sendbuf, int sendcount, MPI_Datatype sendtype,
       void* recvbuf, int recvcount, MPI_Datatype recvtype,
       MPI_Comm comm)
{
   OrigFn fn;
   int    err, sz;
   VALGRIND_GET_ORIG_FN(fn);
   before("Alltoall");
   sz = comm_size(comm);
   check_mem_is_defined(sendbuf, sendcount * sz, sendtype);
   check_mem_is_addressable(recvbuf, recvcount * sz, recvtype);
   if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
   CALL_FN_W_7W(err, fn, sendbuf,sendcount,sendtype,
                         recvbuf,recvcount,recvtype,
                         comm);
   if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
   make_mem_defined_if_addressable_if_success(err, recvbuf, recvcount * sz, recvtype);
   after("Alltoall", err);
   return err;
}


/*------------------------------------------------------------*/
/*---                                                      ---*/
/*--- Sec 4.9, Global Reduction Operations                 ---*/
/*---                                                      ---*/
/*------------------------------------------------------------*/

/* --- Reduce --- */
/* rd: (sendbuf,count,datatype) for all
   wr: (recvbuf,count,datatype) but only for rank == root
*/
int WRAPPER_FOR(PMPI_Reduce)(void *sendbuf, void *recvbuf, 
                             int count,
                             MPI_Datatype datatype, MPI_Op op, 
                             int root, MPI_Comm comm)
{
   OrigFn fn;
   int    err;
   Bool  i_am_root;
   VALGRIND_GET_ORIG_FN(fn);
   before("Reduce");
   i_am_root = root == comm_rank(comm);
   check_mem_is_defined(sendbuf, count, datatype);
   if (i_am_root)
      check_mem_is_addressable(recvbuf, count, datatype);
   if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
   CALL_FN_W_7W(err, fn, sendbuf,recvbuf,count,datatype,op,root,comm);
   if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
   if (i_am_root)
      make_mem_defined_if_addressable_if_success(err, recvbuf, count, datatype);
   after("Reduce", err);
   return err;
}


/* --- Allreduce --- */
/* rd: (sendbuf,count,datatype) for all
   wr: (recvbuf,count,datatype) for all
*/
int WRAPPER_FOR(PMPI_Allreduce)(void *sendbuf, void *recvbuf, 
                                int count,
                                MPI_Datatype datatype, MPI_Op op, 
                                MPI_Comm comm)
{
   OrigFn fn;
   int    err;
   VALGRIND_GET_ORIG_FN(fn);
   before("Allreduce");
   check_mem_is_defined(sendbuf, count, datatype);
   check_mem_is_addressable(recvbuf, count, datatype);
   if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
   CALL_FN_W_6W(err, fn, sendbuf,recvbuf,count,datatype,op,comm);
   if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
   make_mem_defined_if_addressable_if_success(err, recvbuf, count, datatype);
   after("Allreduce", err);
   return err;
}


/* --- Op_create --- */
/* This is a bit dubious.  I suppose it takes 'function' and 
   writes something at *op, but who knows what an MPI_Op is? 
   Can we safely do 'sizeof' on it? */
int WRAPPER_FOR(PMPI_Op_create)( MPI_User_function* function,
                                 int commute, 
                                 MPI_Op* op )
{
   OrigFn fn;
   int    err;
   VALGRIND_GET_ORIG_FN(fn);
   before("Op_create");
   check_mem_is_addressable_untyped(op, sizeof(*op));
   if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
   CALL_FN_W_WWW(err, fn, function,commute,op);
   if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
   make_mem_defined_if_addressable_if_success_untyped(err, op, sizeof(*op));
   after("Op_create", err);
   return err;
}


/*------------------------------------------------------------*/
/*---                                                      ---*/
/*--- Sec 5.4, Communicator management                     ---*/
/*---                                                      ---*/
/*------------------------------------------------------------*/

/* Hardly seems worth wrapping Comm_rank and Comm_size, but
   since it's done now .. */

/* --- Comm_create --- */
/* Let normal memcheck tracking handle this. */
int WRAPPER_FOR(PMPI_Comm_create)(MPI_Comm comm, MPI_Group group,
                                  MPI_Comm* newcomm)
{
   OrigFn fn;
   int    err;
   VALGRIND_GET_ORIG_FN(fn);
   before("Comm_create");
   if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
   CALL_FN_W_WWW(err, fn, comm,group,newcomm);
   if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
   after("Comm_create", err);
   return err;
}

/* --- Comm_dup --- */
/* Let normal memcheck tracking handle this. */
int WRAPPER_FOR(PMPI_Comm_dup)(MPI_Comm comm, MPI_Comm* newcomm)
{
   OrigFn fn;
   int    err;
   VALGRIND_GET_ORIG_FN(fn);
   before("Comm_dup");
   if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
   CALL_FN_W_WW(err, fn, comm,newcomm);
   if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
   after("Comm_dup", err);
   return err;
}

/* --- Comm_free --- */
/* Let normal memcheck tracking handle this. */
int WRAPPER_FOR(PMPI_Comm_free)(MPI_Comm* comm)
{
   OrigFn fn;
   int    err;
   VALGRIND_GET_ORIG_FN(fn);
   before("Comm_free");
   if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
   CALL_FN_W_W(err, fn, comm);
   if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
   after("Comm_free", err);
   return err;
}

/* --- Comm_rank --- */
/* wr: (rank, sizeof(*rank)) */
int WRAPPER_FOR(PMPI_Comm_rank)(MPI_Comm comm, int *rank)
{
   OrigFn fn;
   int    err;
   VALGRIND_GET_ORIG_FN(fn);
   before("Comm_rank");
   check_mem_is_addressable_untyped(rank, sizeof(*rank));
   if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
   CALL_FN_W_WW(err, fn, comm,rank);
   if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
   make_mem_defined_if_addressable_if_success_untyped(err, rank, sizeof(*rank));
   after("Comm_rank", err);
   return err;
}

/* --- Comm_size --- */
/* wr: (size, sizeof(*size)) */
int WRAPPER_FOR(PMPI_Comm_size)(MPI_Comm comm, int *size)
{
   OrigFn fn;
   int    err;
   VALGRIND_GET_ORIG_FN(fn);
   before("Comm_size");
   check_mem_is_addressable_untyped(size, sizeof(*size));
   if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
   CALL_FN_W_WW(err, fn, comm,size);
   if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
   make_mem_defined_if_addressable_if_success_untyped(err, size, sizeof(*size));
   after("Comm_size", err);
   return err;
}


/*------------------------------------------------------------*/
/*---                                                      ---*/
/*--- Sec 5.7, Caching                                     ---*/
/*---                                                      ---*/
/*------------------------------------------------------------*/


/*------------------------------------------------------------*/
/*---                                                      ---*/
/*--- Sec 7.3, Error codes and classes                     ---*/
/*---                                                      ---*/
/*------------------------------------------------------------*/

/* --- Error_string --- */
int WRAPPER_FOR(PMPI_Error_string)( int errorcode, char* string, 
                                    int* resultlen )
{
   OrigFn fn;
   int    err;
   VALGRIND_GET_ORIG_FN(fn);
   before("Error_string");
   check_mem_is_addressable_untyped(resultlen, sizeof(int));
   check_mem_is_addressable_untyped(string, MPI_MAX_ERROR_STRING);
   if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
   CALL_FN_W_WWW(err, fn, errorcode,string,resultlen);
   if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
   /* Don't bother to paint the result; we assume the real function
      will have filled it with defined characters :-) */
   after("Error_string", err);
   return err;
}


/*------------------------------------------------------------*/
/*---                                                      ---*/
/*--- Sec 7.5, Startup                                     ---*/
/*---                                                      ---*/
/*------------------------------------------------------------*/

/* --- Init --- */
/* rd: *argc, *argv[0 .. *argc-1] */
long WRAPPER_FOR(PMPI_Init)(int *argc, char ***argv)
{
   OrigFn fn;
   int    err;
   VALGRIND_GET_ORIG_FN(fn);
   before("Init");
   if (argc) {
      check_mem_is_defined_untyped(argc, sizeof(int));
   }
   if (argc && argv) {
      check_mem_is_defined_untyped(*argv, *argc * sizeof(char**));
   }
   if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
   CALL_FN_W_WW(err, fn, argc,argv);
   if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
   after("Init", err);
   if (opt_initkludge)
      return (long)(void*)&mpiwrap_walk_type_EXTERNALLY_VISIBLE;
   else
      return (long)err;
}

/* --- Initialized --- */
int WRAPPER_FOR(PMPI_Initialized)(int* flag)
{
   OrigFn fn;
   int    err;
   VALGRIND_GET_ORIG_FN(fn);
   before("Initialized");
   check_mem_is_addressable_untyped(flag, sizeof(int));
   if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
   CALL_FN_W_W(err, fn, flag);
   if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
   make_mem_defined_if_addressable_if_success_untyped(err, flag, sizeof(int));
   after("Initialized", err);
   return err;
}

/* --- Finalize --- */
int WRAPPER_FOR(PMPI_Finalize)(void)
{
   OrigFn fn;
   int    err;
   VALGRIND_GET_ORIG_FN(fn);
   before("Finalize");
   if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;
   CALL_FN_W_v(err, fn);
   if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;
   after("Finalize", err);
   return err;
}


/*------------------------------------------------------------*/
/*---                                                      ---*/
/*--- Default wrappers for all remaining functions         ---*/
/*---                                                      ---*/
/*------------------------------------------------------------*/

/* Boilerplate for default wrappers. */
#define DEFAULT_WRAPPER_PREAMBLE(basename)                        \
      OrigFn fn;                                                  \
      UWord  res;                                                 \
      static int complaints = 1;                                  \
      VALGRIND_GET_ORIG_FN(fn);                                   \
      before(#basename);                                          \
      if (opt_missing >= 2) {                                     \
         barf("no wrapper for PMPI_" #basename                    \
              ",\n\t\t\t     and you have "                       \
              "requested strict checking");                       \
      }                                                           \
      if (opt_missing == 1 && complaints > 0) {                   \
         fprintf(stderr, "%s %5d: warning: no wrapper "           \
                         "for PMPI_" #basename "\n",              \
                 preamble, my_pid);                               \
         complaints--;                                            \
      }                                                           \

#define DEFAULT_WRAPPER_W_0W(basename)                            \
   UWord WRAPPER_FOR(PMPI_##basename)( void )                     \
   {                                                              \
      DEFAULT_WRAPPER_PREAMBLE(basename)                          \
      if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;           \
      CALL_FN_W_v(res, fn);                                       \
      if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;            \
      return res;                                                 \
   }

#define DEFAULT_WRAPPER_W_1W(basename)                            \
   UWord WRAPPER_FOR(PMPI_##basename)( UWord a1 )                 \
   {                                                              \
      DEFAULT_WRAPPER_PREAMBLE(basename)                          \
      if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;           \
      CALL_FN_W_W(res, fn, a1);                                   \
      if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;            \
      return res;                                                 \
   }

#define DEFAULT_WRAPPER_W_2W(basename)                            \
   UWord WRAPPER_FOR(PMPI_##basename)( UWord a1, UWord a2 )       \
   {                                                              \
      DEFAULT_WRAPPER_PREAMBLE(basename)                          \
      if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;           \
      CALL_FN_W_WW(res, fn, a1,a2);                               \
      if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;            \
      return res;                                                 \
   }

#define DEFAULT_WRAPPER_W_3W(basename)                            \
   UWord WRAPPER_FOR(PMPI_##basename)                             \
      ( UWord a1, UWord a2, UWord a3 )                            \
   {                                                              \
      DEFAULT_WRAPPER_PREAMBLE(basename)                          \
      if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;           \
      CALL_FN_W_WWW(res, fn, a1,a2,a3);                           \
      if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;            \
      return res;                                                 \
   }

#define DEFAULT_WRAPPER_W_4W(basename)                            \
   UWord WRAPPER_FOR(PMPI_##basename)                             \
      ( UWord a1, UWord a2, UWord a3, UWord a4 )                  \
   {                                                              \
      DEFAULT_WRAPPER_PREAMBLE(basename)                          \
      if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;           \
      CALL_FN_W_WWWW(res, fn, a1,a2,a3,a4);                       \
      if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;            \
      return res;                                                 \
   }

#define DEFAULT_WRAPPER_W_5W(basename)                            \
   UWord WRAPPER_FOR(PMPI_##basename)                             \
      ( UWord a1, UWord a2, UWord a3, UWord a4, UWord a5 )        \
   {                                                              \
      DEFAULT_WRAPPER_PREAMBLE(basename)                          \
      if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;           \
      CALL_FN_W_5W(res, fn, a1,a2,a3,a4,a5);                      \
      if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;            \
      return res;                                                 \
   }

#define DEFAULT_WRAPPER_W_6W(basename)                            \
   UWord WRAPPER_FOR(PMPI_##basename)                             \
      ( UWord a1, UWord a2, UWord a3, UWord a4, UWord a5,         \
        UWord a6 )                                                \
   {                                                              \
      DEFAULT_WRAPPER_PREAMBLE(basename)                          \
      if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;           \
      CALL_FN_W_6W(res, fn, a1,a2,a3,a4,a5,a6);                   \
      if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;            \
      return res;                                                 \
   }

#define DEFAULT_WRAPPER_W_7W(basename)                            \
   UWord WRAPPER_FOR(PMPI_##basename)                             \
      ( UWord a1, UWord a2, UWord a3, UWord a4, UWord a5,         \
        UWord a6, UWord a7 )                                      \
   {                                                              \
      DEFAULT_WRAPPER_PREAMBLE(basename)                          \
      if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;           \
      CALL_FN_W_7W(res, fn, a1,a2,a3,a4,a5,a6,a7);                \
      if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;            \
      return res;                                                 \
   }

#define DEFAULT_WRAPPER_W_8W(basename)                            \
   UWord WRAPPER_FOR(PMPI_##basename)                             \
      ( UWord a1, UWord a2, UWord a3, UWord a4, UWord a5,         \
        UWord a6, UWord a7, UWord a8 )                            \
   {                                                              \
      DEFAULT_WRAPPER_PREAMBLE(basename)                          \
      if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;           \
      CALL_FN_W_8W(res, fn, a1,a2,a3,a4,a5,a6,a7,a8);             \
      if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;            \
      return res;                                                 \
   }

#define DEFAULT_WRAPPER_W_9W(basename)                            \
   UWord WRAPPER_FOR(PMPI_##basename)                             \
      ( UWord a1, UWord a2, UWord a3, UWord a4, UWord a5,         \
        UWord a6, UWord a7, UWord a8, UWord a9 )                  \
   {                                                              \
      DEFAULT_WRAPPER_PREAMBLE(basename)                          \
      if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;           \
      CALL_FN_W_9W(res, fn, a1,a2,a3,a4,a5,a6,a7,a8,a9);          \
      if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;            \
      return res;                                                 \
   }

#define DEFAULT_WRAPPER_W_10W(basename)                           \
   UWord WRAPPER_FOR(PMPI_##basename)                             \
      ( UWord a1, UWord a2, UWord a3, UWord a4, UWord a5,         \
        UWord a6, UWord a7, UWord a8, UWord a9, UWord a10 )       \
   {                                                              \
      DEFAULT_WRAPPER_PREAMBLE(basename)                          \
      if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;           \
      CALL_FN_W_10W(res, fn, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10);     \
      if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;            \
      return res;                                                 \
   }

#define DEFAULT_WRAPPER_W_12W(basename)                           \
   UWord WRAPPER_FOR(PMPI_##basename)                             \
      ( UWord a1, UWord a2, UWord a3, UWord a4, UWord a5,         \
        UWord a6, UWord a7, UWord a8, UWord a9, UWord a10,        \
        UWord a11, UWord a12 )                                    \
   {                                                              \
      DEFAULT_WRAPPER_PREAMBLE(basename)                          \
      if (cONFIG_DER) VALGRIND_DISABLE_ERROR_REPORTING;           \
      CALL_FN_W_12W(res, fn, a1,a2,a3,a4,a5,a6,                   \
                             a7,a8,a9,a10,a11,a12);               \
      if (cONFIG_DER) VALGRIND_ENABLE_ERROR_REPORTING;            \
      return res;                                                 \
   }


/* Canned summary of MPI-1.1/MPI-2 entry points, as derived from mpi.h
   from Open MPI svn rev 9191 (somewhere between Open MPI versions
   1.0.1 and 1.1.0). */

/* If a function is commented out in this list, it's because it has a
   proper wrapper written elsewhere (above here). */

DEFAULT_WRAPPER_W_2W(Abort)
DEFAULT_WRAPPER_W_9W(Accumulate)
DEFAULT_WRAPPER_W_1W(Add_error_class)
DEFAULT_WRAPPER_W_2W(Add_error_code)
DEFAULT_WRAPPER_W_2W(Add_error_string)
/* DEFAULT_WRAPPER_W_2W(Address) */
DEFAULT_WRAPPER_W_7W(Allgather)
DEFAULT_WRAPPER_W_8W(Allgatherv)
DEFAULT_WRAPPER_W_3W(Alloc_mem)
/* DEFAULT_WRAPPER_W_6W(Allreduce) */
/* DEFAULT_WRAPPER_W_7W(Alltoall) */
DEFAULT_WRAPPER_W_9W(Alltoallv)
DEFAULT_WRAPPER_W_9W(Alltoallw)
DEFAULT_WRAPPER_W_2W(Attr_delete)
DEFAULT_WRAPPER_W_4W(Attr_get)
DEFAULT_WRAPPER_W_3W(Attr_put)
DEFAULT_WRAPPER_W_1W(Barrier)
/* DEFAULT_WRAPPER_W_5W(Bcast) */
/* DEFAULT_WRAPPER_W_6W(Bsend) */
DEFAULT_WRAPPER_W_7W(Bsend_init)
DEFAULT_WRAPPER_W_2W(Buffer_attach)
DEFAULT_WRAPPER_W_2W(Buffer_detach)
/* DEFAULT_WRAPPER_W_1W(Cancel) */
DEFAULT_WRAPPER_W_4W(Cart_coords)
DEFAULT_WRAPPER_W_6W(Cart_create)
DEFAULT_WRAPPER_W_5W(Cart_get)
DEFAULT_WRAPPER_W_5W(Cart_map)
DEFAULT_WRAPPER_W_3W(Cart_rank)
DEFAULT_WRAPPER_W_5W(Cart_shift)
DEFAULT_WRAPPER_W_3W(Cart_sub)
DEFAULT_WRAPPER_W_2W(Cartdim_get)
DEFAULT_WRAPPER_W_1W(Close_port)
DEFAULT_WRAPPER_W_5W(Comm_accept)
DEFAULT_WRAPPER_W_1W(Comm_c2f)
DEFAULT_WRAPPER_W_2W(Comm_call_errhandler)
DEFAULT_WRAPPER_W_3W(Comm_compare)
DEFAULT_WRAPPER_W_5W(Comm_connect)
DEFAULT_WRAPPER_W_2W(Comm_create_errhandler)
DEFAULT_WRAPPER_W_4W(Comm_create_keyval)
/* DEFAULT_WRAPPER_W_3W(Comm_create) */
DEFAULT_WRAPPER_W_2W(Comm_delete_attr)
DEFAULT_WRAPPER_W_1W(Comm_disconnect)
/* DEFAULT_WRAPPER_W_2W(Comm_dup) */
DEFAULT_WRAPPER_W_1W(Comm_f2c)
DEFAULT_WRAPPER_W_1W(Comm_free_keyval)
/* DEFAULT_WRAPPER_W_1W(Comm_free) */
DEFAULT_WRAPPER_W_4W(Comm_get_attr)
DEFAULT_WRAPPER_W_2W(Comm_get_errhandler)
DEFAULT_WRAPPER_W_3W(Comm_get_name)
DEFAULT_WRAPPER_W_1W(Comm_get_parent)
DEFAULT_WRAPPER_W_2W(Comm_group)
DEFAULT_WRAPPER_W_2W(Comm_join)
/* DEFAULT_WRAPPER_W_2W(Comm_rank) */
DEFAULT_WRAPPER_W_2W(Comm_remote_group)
DEFAULT_WRAPPER_W_2W(Comm_remote_size)
DEFAULT_WRAPPER_W_3W(Comm_set_attr)
DEFAULT_WRAPPER_W_2W(Comm_set_errhandler)
DEFAULT_WRAPPER_W_2W(Comm_set_name)
/* DEFAULT_WRAPPER_W_2W(Comm_size) */
DEFAULT_WRAPPER_W_8W(Comm_spawn)
DEFAULT_WRAPPER_W_9W(Comm_spawn_multiple)
DEFAULT_WRAPPER_W_4W(Comm_split)
DEFAULT_WRAPPER_W_2W(Comm_test_inter)
DEFAULT_WRAPPER_W_3W(Dims_create)
DEFAULT_WRAPPER_W_1W(Errhandler_c2f)
DEFAULT_WRAPPER_W_2W(Errhandler_create)
DEFAULT_WRAPPER_W_1W(Errhandler_f2c)
DEFAULT_WRAPPER_W_1W(Errhandler_free)
DEFAULT_WRAPPER_W_2W(Errhandler_get)
DEFAULT_WRAPPER_W_2W(Errhandler_set)
DEFAULT_WRAPPER_W_2W(Error_class)
/* DEFAULT_WRAPPER_W_3W(Error_string) */
DEFAULT_WRAPPER_W_6W(Exscan)
DEFAULT_WRAPPER_W_1W(File_c2f)
DEFAULT_WRAPPER_W_1W(File_f2c)
DEFAULT_WRAPPER_W_2W(File_call_errhandler)
DEFAULT_WRAPPER_W_2W(File_create_errhandler)
DEFAULT_WRAPPER_W_2W(File_set_errhandler)
DEFAULT_WRAPPER_W_2W(File_get_errhandler)
DEFAULT_WRAPPER_W_5W(File_open)
DEFAULT_WRAPPER_W_1W(File_close)
DEFAULT_WRAPPER_W_2W(File_delete)
DEFAULT_WRAPPER_W_2W(File_set_size)
DEFAULT_WRAPPER_W_2W(File_preallocate)
DEFAULT_WRAPPER_W_2W(File_get_size)
DEFAULT_WRAPPER_W_2W(File_get_group)
DEFAULT_WRAPPER_W_2W(File_get_amode)
DEFAULT_WRAPPER_W_2W(File_set_info)
DEFAULT_WRAPPER_W_2W(File_get_info)
DEFAULT_WRAPPER_W_6W(File_set_view)
DEFAULT_WRAPPER_W_5W(File_get_view)
DEFAULT_WRAPPER_W_6W(File_read_at)
DEFAULT_WRAPPER_W_6W(File_read_at_all)
DEFAULT_WRAPPER_W_6W(File_write_at)
DEFAULT_WRAPPER_W_6W(File_write_at_all)
DEFAULT_WRAPPER_W_6W(File_iread_at)
DEFAULT_WRAPPER_W_6W(File_iwrite_at)
DEFAULT_WRAPPER_W_5W(File_read)
DEFAULT_WRAPPER_W_5W(File_read_all)
DEFAULT_WRAPPER_W_5W(File_write)
DEFAULT_WRAPPER_W_5W(File_write_all)
DEFAULT_WRAPPER_W_5W(File_iread)
DEFAULT_WRAPPER_W_5W(File_iwrite)
DEFAULT_WRAPPER_W_3W(File_seek)
DEFAULT_WRAPPER_W_2W(File_get_position)
DEFAULT_WRAPPER_W_3W(File_get_byte_offset)
DEFAULT_WRAPPER_W_5W(File_read_shared)
DEFAULT_WRAPPER_W_5W(File_write_shared)
DEFAULT_WRAPPER_W_5W(File_iread_shared)
DEFAULT_WRAPPER_W_5W(File_iwrite_shared)
DEFAULT_WRAPPER_W_5W(File_read_ordered)
DEFAULT_WRAPPER_W_5W(File_write_ordered)
DEFAULT_WRAPPER_W_3W(File_seek_shared)
DEFAULT_WRAPPER_W_2W(File_get_position_shared)
DEFAULT_WRAPPER_W_5W(File_read_at_all_begin)
DEFAULT_WRAPPER_W_3W(File_read_at_all_end)
DEFAULT_WRAPPER_W_5W(File_write_at_all_begin)
DEFAULT_WRAPPER_W_3W(File_write_at_all_end)
DEFAULT_WRAPPER_W_4W(File_read_all_begin)
DEFAULT_WRAPPER_W_3W(File_read_all_end)
DEFAULT_WRAPPER_W_4W(File_write_all_begin)
DEFAULT_WRAPPER_W_3W(File_write_all_end)
DEFAULT_WRAPPER_W_4W(File_read_ordered_begin)
DEFAULT_WRAPPER_W_3W(File_read_ordered_end)
DEFAULT_WRAPPER_W_4W(File_write_ordered_begin)
DEFAULT_WRAPPER_W_3W(File_write_ordered_end)
DEFAULT_WRAPPER_W_3W(File_get_type_extent)
DEFAULT_WRAPPER_W_2W(File_set_atomicity)
DEFAULT_WRAPPER_W_2W(File_get_atomicity)
DEFAULT_WRAPPER_W_1W(File_sync)
/* DEFAULT_WRAPPER_W_0W(Finalize) */
DEFAULT_WRAPPER_W_1W(Finalized)
DEFAULT_WRAPPER_W_1W(Free_mem)
/* DEFAULT_WRAPPER_W_8W(Gather) */
DEFAULT_WRAPPER_W_9W(Gatherv)
DEFAULT_WRAPPER_W_2W(Get_address)
/* DEFAULT_WRAPPER_W_3W(Get_count) */
DEFAULT_WRAPPER_W_3W(Get_elements)
DEFAULT_WRAPPER_W_8W(Get)
DEFAULT_WRAPPER_W_2W(Get_processor_name)
DEFAULT_WRAPPER_W_2W(Get_version)
DEFAULT_WRAPPER_W_6W(Graph_create)
DEFAULT_WRAPPER_W_5W(Graph_get)
DEFAULT_WRAPPER_W_5W(Graph_map)
DEFAULT_WRAPPER_W_3W(Graph_neighbors_count)
DEFAULT_WRAPPER_W_4W(Graph_neighbors)
DEFAULT_WRAPPER_W_3W(Graphdims_get)
DEFAULT_WRAPPER_W_1W(Grequest_complete)
DEFAULT_WRAPPER_W_5W(Grequest_start)
DEFAULT_WRAPPER_W_1W(Group_c2f)
DEFAULT_WRAPPER_W_3W(Group_compare)
DEFAULT_WRAPPER_W_3W(Group_difference)
DEFAULT_WRAPPER_W_4W(Group_excl)
DEFAULT_WRAPPER_W_1W(Group_f2c)
DEFAULT_WRAPPER_W_1W(Group_free)
DEFAULT_WRAPPER_W_4W(Group_incl)
DEFAULT_WRAPPER_W_3W(Group_intersection)
DEFAULT_WRAPPER_W_4W(Group_range_excl)
DEFAULT_WRAPPER_W_4W(Group_range_incl)
DEFAULT_WRAPPER_W_2W(Group_rank)
DEFAULT_WRAPPER_W_2W(Group_size)
DEFAULT_WRAPPER_W_5W(Group_translate_ranks)
DEFAULT_WRAPPER_W_3W(Group_union)
/* DEFAULT_WRAPPER_W_7W(Ibsend) */
DEFAULT_WRAPPER_W_1W(Info_c2f)
DEFAULT_WRAPPER_W_1W(Info_create)
DEFAULT_WRAPPER_W_2W(Info_delete)
DEFAULT_WRAPPER_W_2W(Info_dup)
DEFAULT_WRAPPER_W_1W(Info_f2c)
DEFAULT_WRAPPER_W_1W(Info_free)
DEFAULT_WRAPPER_W_5W(Info_get)
DEFAULT_WRAPPER_W_2W(Info_get_nkeys)
DEFAULT_WRAPPER_W_3W(Info_get_nthkey)
DEFAULT_WRAPPER_W_4W(Info_get_valuelen)
DEFAULT_WRAPPER_W_3W(Info_set)
/* DEFAULT_WRAPPER_W_2W(Init) */
/* DEFAULT_WRAPPER_W_1W(Initialized) */
DEFAULT_WRAPPER_W_4W(Init_thread)
DEFAULT_WRAPPER_W_6W(Intercomm_create)
DEFAULT_WRAPPER_W_3W(Intercomm_merge)
/* DEFAULT_WRAPPER_W_5W(Iprobe) */
/* DEFAULT_WRAPPER_W_7W(Irecv) */
/* DEFAULT_WRAPPER_W_7W(Irsend) */
/* DEFAULT_WRAPPER_W_7W(Isend) */
/* DEFAULT_WRAPPER_W_7W(Issend) */
DEFAULT_WRAPPER_W_1W(Is_thread_main)
DEFAULT_WRAPPER_W_4W(Keyval_create)
DEFAULT_WRAPPER_W_1W(Keyval_free)
DEFAULT_WRAPPER_W_3W(Lookup_name)
DEFAULT_WRAPPER_W_1W(Op_c2f)
/* DEFAULT_WRAPPER_W_3W(Op_create) */
DEFAULT_WRAPPER_W_2W(Open_port)
DEFAULT_WRAPPER_W_1W(Op_f2c)
DEFAULT_WRAPPER_W_1W(Op_free)
DEFAULT_WRAPPER_W_7W(Pack_external)
DEFAULT_WRAPPER_W_4W(Pack_external_size)
/* DEFAULT_WRAPPER_W_7W(Pack) */
DEFAULT_WRAPPER_W_4W(Pack_size)
/* int MPI_Pcontrol(const int level, ...) */
/* DEFAULT_WRAPPER_W_4W(Probe) */
DEFAULT_WRAPPER_W_3W(Publish_name)
DEFAULT_WRAPPER_W_8W(Put)
DEFAULT_WRAPPER_W_1W(Query_thread)
DEFAULT_WRAPPER_W_7W(Recv_init)
/* DEFAULT_WRAPPER_W_7W(Recv) */
/* DEFAULT_WRAPPER_W_7W(Reduce) */
DEFAULT_WRAPPER_W_6W(Reduce_scatter)
DEFAULT_WRAPPER_W_5W(Register_datarep)
DEFAULT_WRAPPER_W_1W(Request_c2f)
DEFAULT_WRAPPER_W_1W(Request_f2c)
DEFAULT_WRAPPER_W_1W(Request_free)
DEFAULT_WRAPPER_W_3W(Request_get_status)
/* DEFAULT_WRAPPER_W_6W(Rsend) */
DEFAULT_WRAPPER_W_7W(Rsend_init)
DEFAULT_WRAPPER_W_6W(Scan)
/* DEFAULT_WRAPPER_W_8W(Scatter) */
DEFAULT_WRAPPER_W_9W(Scatterv)
DEFAULT_WRAPPER_W_7W(Send_init)
/* DEFAULT_WRAPPER_W_6W(Send) */
/* DEFAULT_WRAPPER_W_12W(Sendrecv) */
DEFAULT_WRAPPER_W_9W(Sendrecv_replace)
DEFAULT_WRAPPER_W_7W(Ssend_init)
/* DEFAULT_WRAPPER_W_6W(Ssend) */
DEFAULT_WRAPPER_W_1W(Start)
DEFAULT_WRAPPER_W_2W(Startall)
DEFAULT_WRAPPER_W_2W(Status_c2f)
DEFAULT_WRAPPER_W_2W(Status_f2c)
DEFAULT_WRAPPER_W_2W(Status_set_cancelled)
DEFAULT_WRAPPER_W_3W(Status_set_elements)
/* DEFAULT_WRAPPER_W_4W(Testall) */
DEFAULT_WRAPPER_W_5W(Testany)
/* DEFAULT_WRAPPER_W_3W(Test) */
DEFAULT_WRAPPER_W_2W(Test_cancelled)
DEFAULT_WRAPPER_W_5W(Testsome)
DEFAULT_WRAPPER_W_2W(Topo_test)
DEFAULT_WRAPPER_W_1W(Type_c2f)
/* DEFAULT_WRAPPER_W_1W(Type_commit) */
DEFAULT_WRAPPER_W_3W(Type_contiguous)
DEFAULT_WRAPPER_W_10W(Type_create_darray)
DEFAULT_WRAPPER_W_3W(Type_create_f90_complex)
DEFAULT_WRAPPER_W_2W(Type_create_f90_integer)
DEFAULT_WRAPPER_W_3W(Type_create_f90_real)
DEFAULT_WRAPPER_W_5W(Type_create_hindexed)
DEFAULT_WRAPPER_W_5W(Type_create_hvector)
DEFAULT_WRAPPER_W_4W(Type_create_keyval)
DEFAULT_WRAPPER_W_5W(Type_create_indexed_block)
DEFAULT_WRAPPER_W_5W(Type_create_struct)
DEFAULT_WRAPPER_W_7W(Type_create_subarray)
DEFAULT_WRAPPER_W_4W(Type_create_resized)
DEFAULT_WRAPPER_W_2W(Type_delete_attr)
DEFAULT_WRAPPER_W_2W(Type_dup)
/* DEFAULT_WRAPPER_W_2W(Type_extent) */
/* DEFAULT_WRAPPER_W_1W(Type_free) */
DEFAULT_WRAPPER_W_1W(Type_free_keyval)
DEFAULT_WRAPPER_W_1W(Type_f2c)
DEFAULT_WRAPPER_W_4W(Type_get_attr)
/* DEFAULT_WRAPPER_W_7W(Type_get_contents) */
/* DEFAULT_WRAPPER_W_5W(Type_get_envelope) */
DEFAULT_WRAPPER_W_3W(Type_get_extent)
DEFAULT_WRAPPER_W_3W(Type_get_name)
DEFAULT_WRAPPER_W_3W(Type_get_true_extent)
DEFAULT_WRAPPER_W_5W(Type_hindexed)
DEFAULT_WRAPPER_W_5W(Type_hvector)
DEFAULT_WRAPPER_W_5W(Type_indexed)
DEFAULT_WRAPPER_W_2W(Type_lb)
DEFAULT_WRAPPER_W_3W(Type_match_size)
DEFAULT_WRAPPER_W_3W(Type_set_attr)
DEFAULT_WRAPPER_W_2W(Type_set_name)
DEFAULT_WRAPPER_W_2W(Type_size)
DEFAULT_WRAPPER_W_5W(Type_struct)
DEFAULT_WRAPPER_W_2W(Type_ub)
DEFAULT_WRAPPER_W_5W(Type_vector)
/* DEFAULT_WRAPPER_W_7W(Unpack) */
DEFAULT_WRAPPER_W_3W(Unpublish_name)
DEFAULT_WRAPPER_W_7W(Unpack_external)
/* DEFAULT_WRAPPER_W_3W(Waitall) */
/* DEFAULT_WRAPPER_W_4W(Waitany) */
/* DEFAULT_WRAPPER_W_2W(Wait) */
DEFAULT_WRAPPER_W_5W(Waitsome)
DEFAULT_WRAPPER_W_1W(Win_c2f)
DEFAULT_WRAPPER_W_2W(Win_call_errhandler)
DEFAULT_WRAPPER_W_1W(Win_complete)
DEFAULT_WRAPPER_W_6W(Win_create)
DEFAULT_WRAPPER_W_2W(Win_create_errhandler)
DEFAULT_WRAPPER_W_4W(Win_create_keyval)
DEFAULT_WRAPPER_W_2W(Win_delete_attr)
DEFAULT_WRAPPER_W_1W(Win_f2c)
DEFAULT_WRAPPER_W_2W(Win_fence)
DEFAULT_WRAPPER_W_1W(Win_free)
DEFAULT_WRAPPER_W_1W(Win_free_keyval)
DEFAULT_WRAPPER_W_4W(Win_get_attr)
DEFAULT_WRAPPER_W_2W(Win_get_errhandler)
DEFAULT_WRAPPER_W_2W(Win_get_group)
DEFAULT_WRAPPER_W_3W(Win_get_name)
DEFAULT_WRAPPER_W_4W(Win_lock)
DEFAULT_WRAPPER_W_3W(Win_post)
DEFAULT_WRAPPER_W_3W(Win_set_attr)
DEFAULT_WRAPPER_W_2W(Win_set_errhandler)
DEFAULT_WRAPPER_W_2W(Win_set_name)
DEFAULT_WRAPPER_W_3W(Win_start)
DEFAULT_WRAPPER_W_2W(Win_test)
DEFAULT_WRAPPER_W_2W(Win_unlock)
DEFAULT_WRAPPER_W_1W(Win_wait)
/* double MPI_Wtick(void) */
/* double MPI_Wtime(void) */


/*------------------------------------------------------------*/
/*---                                                      ---*/
/*---                                                      ---*/
/*---                                                      ---*/
/*------------------------------------------------------------*/

/*---------------------------------------------------------------*/
/*--- end                                           mpiwrap.c ---*/
/*---------------------------------------------------------------*/