AMPI: Add fsglobals (filesystem) and pipglobals (Process-in-Process) privatization...
[charm.git] / src / libs / ck-libs / ampi / ampiimpl.h
1 #ifndef _AMPIIMPL_H
2 #define _AMPIIMPL_H
3
4 #include <string.h> /* for strlen */
5 #include <algorithm>
6 #include <numeric>
7 #include <forward_list>
8 #include <bitset>
9
10 #include "ampi.h"
11 #include "ddt.h"
12 #include "charm++.h"
13
14 using std::vector;
15
16 //Uncomment for debug print statements
17 #define AMPI_DEBUG(...) //CkPrintf(__VA_ARGS__)
18
19 /*
20  * All MPI_* routines must be defined using the AMPI_API_IMPL macro.
21  * All calls inside AMPI to MPI_* routines must use MPI_* as the name.
22  * There are two reasons for this:
23  *
24  * 1. AMPI supports the PMPI interface only on Linux.
25  *
26  * 2. When AMPI is built on top of MPI, we rename the user's MPI_* calls as AMPI_*.
27  */
28 #define STRINGIFY_INTERNAL(a) #a
29 #define STRINGIFY(a) STRINGIFY_INTERNAL(a)
30
31 #if AMPI_HAVE_PMPI
32   #define AMPI_API_IMPL(ret, name, ...) \
33     _Pragma(STRINGIFY(weak name)) \
34     _Pragma(STRINGIFY(weak P##name = name)) \
35     CLINKAGE \
36     ret name(__VA_ARGS__)
37 #else // not Linux (no PMPI support):
38   #define AMPI_API_IMPL(ret, name, ...) \
39     CLINKAGE \
40     ret name(__VA_ARGS__)
41 #endif
42
43 extern char * ampi_binary_path;
44
45 #if AMPIMSGLOG
46 #include "ckliststring.h"
47 static CkListString msgLogRanks;
48 static int msgLogWrite;
49 static int msgLogRead;
50 static char *msgLogFilename;
51
52 #if CMK_USE_ZLIB && 0
53 #include <zlib.h>
54 namespace PUP{
55 class zdisk : public er {
56  protected:
57   gzFile F;//Disk file to read from/write to
58   zdisk(unsigned int type,gzFile f):er(type),F(f) {}
59   zdisk(const zdisk &p);                        //You don't want to copy
60   void operator=(const zdisk &p);       // You don't want to copy
61
62   //For seeking (pack/unpack in different orders)
63   virtual void impl_startSeek(seekBlock &s); /*Begin a seeking block*/
64   virtual int impl_tell(seekBlock &s); /*Give the current offset*/
65   virtual void impl_seek(seekBlock &s,int off); /*Seek to the given offset*/
66 };
67
68 //For packing to a disk file
69 class tozDisk : public zdisk {
70  protected:
71   //Generic bottleneck: pack n items of size itemSize from p.
72   virtual void bytes(void *p,int n,size_t itemSize,dataType t);
73  public:
74   //Write data to the given file pointer
75   // (must be opened for binary write)
76   // You must close the file yourself when done.
77   tozDisk(gzFile f):zdisk(IS_PACKING,f) {}
78 };
79
80 //For unpacking from a disk file
81 class fromzDisk : public zdisk {
82  protected:
83   //Generic bottleneck: unpack n items of size itemSize from p.
84   virtual void bytes(void *p,int n,size_t itemSize,dataType t);
85  public:
86   //Write data to the given file pointer
87   // (must be opened for binary read)
88   // You must close the file yourself when done.
89   fromzDisk(gzFile f):zdisk(IS_UNPACKING,f) {}
90 };
91 }; // namespace PUP
92 #endif
93 #endif // AMPIMSGLOG
94
95 /* AMPI sends messages inline to PE-local destination VPs if: BigSim is not being used and
96  * if tracing is not being used (see bug #1640 for more details on the latter). */
97 #ifndef AMPI_LOCAL_IMPL
98 #define AMPI_LOCAL_IMPL ( !CMK_BIGSIM_CHARM && !CMK_TRACE_ENABLED )
99 #endif
100
101 /* AMPI uses RDMA sends if BigSim is not being used and the underlying comm
102  * layer supports it (except for GNI, which has experimental RDMA support). */
103 #ifndef AMPI_RDMA_IMPL
104 #define AMPI_RDMA_IMPL ( !CMK_BIGSIM_CHARM && CMK_ONESIDED_IMPL && !CMK_CONVERSE_UGNI )
105 #endif
106
107 /* contiguous messages larger than or equal to this threshold are sent via RDMA */
108 #ifndef AMPI_RDMA_THRESHOLD_DEFAULT
109 #if CMK_USE_IBVERBS || CMK_OFI || CMK_CONVERSE_UGNI
110 #define AMPI_RDMA_THRESHOLD_DEFAULT 65536
111 #else
112 #define AMPI_RDMA_THRESHOLD_DEFAULT 32768
113 #endif
114 #endif
115
116 /* contiguous messages larger than or equal to this threshold that are being sent
117  * within a process are sent via RDMA. */
118 #ifndef AMPI_SMP_RDMA_THRESHOLD_DEFAULT
119 #define AMPI_SMP_RDMA_THRESHOLD_DEFAULT 16384
120 #endif
121
122 extern int AMPI_RDMA_THRESHOLD;
123 extern int AMPI_SMP_RDMA_THRESHOLD;
124
125 #define AMPI_ALLTOALL_THROTTLE   64
126 #define AMPI_ALLTOALL_SHORT_MSG  256
127 #if CMK_BIGSIM_CHARM
128 #define AMPI_ALLTOALL_LONG_MSG   4194304
129 #else
130 #define AMPI_ALLTOALL_LONG_MSG   32768
131 #endif
132
133 typedef void (*MPI_MigrateFn)(void);
134
135 /*
136  * AMPI Message Matching (Amm) Interface:
137  * messages are matched on 2 ints: [tag, src]
138  */
139 #define AMM_TAG   0
140 #define AMM_SRC   1
141 #define AMM_NTAGS 2
142
143 // Number of AmmEntry<T>'s in AmmEntryPool for pt2pt msgs:
144 #ifndef AMPI_AMM_PT2PT_POOL_SIZE
145 #define AMPI_AMM_PT2PT_POOL_SIZE 32
146 #endif
147
148 // Number of AmmEntry<T>'s in AmmEntryPool for coll msgs:
149 #ifndef AMPI_AMM_COLL_POOL_SIZE
150 #define AMPI_AMM_COLL_POOL_SIZE 4
151 #endif
152
153 class AmpiRequestList;
154
155 typedef void (*AmmPupMessageFn)(PUP::er& p, void **msg);
156
157 template <class T>
158 class AmmEntry {
159  public:
160   int tags[AMM_NTAGS]; // [tag, src]
161   AmmEntry<T>* next;
162   T msg; // T is either an AmpiRequest* or an AmpiMsg*
163   AmmEntry(T m) noexcept { tags[AMM_TAG] = m->getTag(); tags[AMM_SRC] = m->getSrcRank(); next = NULL; msg = m; }
164   AmmEntry(int tag, int src, T m) noexcept { tags[AMM_TAG] = tag; tags[AMM_SRC] = src; next = NULL; msg = m; }
165   AmmEntry() = default;
166   ~AmmEntry() = default;
167 };
168
169 template <class T, size_t N>
170 class Amm {
171  public:
172   AmmEntry<T>* first;
173   AmmEntry<T>** lasth;
174
175  private:
176   int startIdx;
177   std::bitset<N> validEntries;
178   std::array<AmmEntry<T>, N> entryPool;
179
180  public:
181   Amm() noexcept : first(NULL), lasth(&first), startIdx(0) { validEntries.reset();  }
182   ~Amm() = default;
183   inline AmmEntry<T>* newEntry(int tag, int src, T msg) noexcept {
184     if (validEntries.all()) {
185       return new AmmEntry<T>(tag, src, msg);
186     } else {
187       for (int i=startIdx; i<validEntries.size(); i++) {
188         if (!validEntries[i]) {
189           validEntries[i] = 1;
190           AmmEntry<T>* ent = new (&entryPool[i]) AmmEntry<T>(tag, src, msg);
191           startIdx = i+1;
192           return ent;
193         }
194       }
195       CkAbort("AMPI> failed to find a free entry in pool!");
196       return NULL;
197     }
198   }
199   inline AmmEntry<T>* newEntry(T msg) noexcept {
200     if (validEntries.all()) {
201       return new AmmEntry<T>(msg);
202     } else {
203       for (int i=startIdx; i<validEntries.size(); i++) {
204         if (!validEntries[i]) {
205           validEntries[i] = 1;
206           AmmEntry<T>* ent = new (&entryPool[i]) AmmEntry<T>(msg);
207           startIdx = i+1;
208           return ent;
209         }
210       }
211       CkAbort("AMPI> failed to find a free entry in pool!");
212       return NULL;
213     }
214   }
215   inline void deleteEntry(AmmEntry<T> *ent) noexcept {
216     if (ent >= &entryPool.front() && ent <= &entryPool.back()) {
217       int idx = (int)((intptr_t)ent - (intptr_t)&entryPool.front()) / sizeof(AmmEntry<T>);
218       validEntries[idx] = 0;
219       startIdx = std::min(idx, startIdx);
220     } else {
221       delete ent;
222     }
223   }
224   void freeAll() noexcept;
225   void flushMsgs() noexcept;
226   inline bool match(const int tags1[AMM_NTAGS], const int tags2[AMM_NTAGS]) const noexcept;
227   inline void put(T msg) noexcept;
228   inline void put(int tag, int src, T msg) noexcept;
229   inline T get(int tag, int src, int* rtags=NULL) noexcept;
230   inline T probe(int tag, int src, int* rtags) noexcept;
231   inline int size() const noexcept;
232   void pup(PUP::er& p, AmmPupMessageFn msgpup) noexcept;
233 };
234
235 PUPfunctionpointer(MPI_User_function*)
236
237 /*
238  * OpStruct's are used to lookup an MPI_User_function* and check its commutativity.
239  * They are also used to create AmpiOpHeader's, which are transmitted in reductions
240  * that are user-defined or else lack an equivalent Charm++ reducer type.
241  */
242 class OpStruct {
243  public:
244   MPI_User_function* func;
245   bool isCommutative;
246  private:
247   bool isValid;
248
249  public:
250   OpStruct() = default;
251   OpStruct(MPI_User_function* f) noexcept : func(f), isCommutative(true), isValid(true) {}
252   OpStruct(MPI_User_function* f, bool c) noexcept : func(f), isCommutative(c), isValid(true) {}
253   void init(MPI_User_function* f, bool c) noexcept {
254     func = f;
255     isCommutative = c;
256     isValid = true;
257   }
258   bool isFree() const noexcept { return !isValid; }
259   void free() noexcept { isValid = false; }
260   void pup(PUP::er &p) {
261     p|func;  p|isCommutative;  p|isValid;
262   }
263 };
264
265 class AmpiOpHeader {
266  public:
267   MPI_User_function* func;
268   MPI_Datatype dtype;
269   int len;
270   int szdata;
271   AmpiOpHeader(MPI_User_function* f,MPI_Datatype d,int l,int szd) noexcept :
272     func(f),dtype(d),len(l),szdata(szd) { }
273 };
274
275 //------------------- added by YAN for one-sided communication -----------
276 /* the index is unique within a communicator */
277 class WinStruct{
278  public:
279   MPI_Comm comm;
280   int index;
281
282 private:
283   bool areRecvsPosted;
284   bool inEpoch;
285   vector<int> exposureRankList;
286   vector<int> accessRankList;
287   vector<MPI_Request> requestList;
288
289 public:
290   WinStruct() noexcept : comm(MPI_COMM_NULL), index(-1), areRecvsPosted(false), inEpoch(false) {
291     exposureRankList.clear(); accessRankList.clear(); requestList.clear();
292   }
293   WinStruct(MPI_Comm comm_, int index_) noexcept : comm(comm_), index(index_), areRecvsPosted(false), inEpoch(false) {
294     exposureRankList.clear(); accessRankList.clear(); requestList.clear();
295   }
296   void pup(PUP::er &p) noexcept {
297     p|comm; p|index; p|areRecvsPosted; p|inEpoch; p|exposureRankList; p|accessRankList; p|requestList;
298   }
299   void clearEpochAccess() noexcept {
300     accessRankList.clear(); inEpoch = false;
301   }
302   void clearEpochExposure() noexcept {
303     exposureRankList.clear(); areRecvsPosted = false; requestList.clear(); inEpoch=false;
304   }
305   vector<int>& getExposureRankList() noexcept {return exposureRankList;}
306   vector<int>& getAccessRankList() noexcept {return accessRankList;}
307   void setExposureRankList(vector<int> &tmpExposureRankList) noexcept {exposureRankList = tmpExposureRankList;}
308   void setAccessRankList(vector<int> &tmpAccessRankList) noexcept {accessRankList = tmpAccessRankList;}
309   vector<int>& getRequestList() noexcept {return requestList;}
310   bool AreRecvsPosted() const noexcept {return areRecvsPosted;}
311   void setAreRecvsPosted(bool setR) noexcept {areRecvsPosted = setR;}
312   bool isInEpoch() const noexcept {return inEpoch;}
313   void setInEpoch(bool arg) noexcept {inEpoch = arg;}
314 };
315
316 class lockQueueEntry {
317  public:
318   int requestRank;
319   int lock_type;
320   lockQueueEntry (int _requestRank, int _lock_type) noexcept
321     : requestRank(_requestRank), lock_type(_lock_type) {}
322   lockQueueEntry() = default;
323 };
324
325 typedef CkQ<lockQueueEntry *> LockQueue;
326
327 class ampiParent;
328
329 class win_obj {
330  public:
331   void *baseAddr;
332   MPI_Aint winSize;
333   int disp_unit;
334   MPI_Comm comm;
335
336   int owner; // Rank of owner of the lock, -1 if not locked
337   LockQueue lockQueue; // queue of waiting processors for the lock
338                        // top of queue is the one holding the lock
339                        // queue is empty if lock is not applied
340   std::string winName;
341   bool initflag;
342
343   vector<int> keyvals; // list of keyval attributes
344
345   void setName(const char *src) noexcept;
346   void getName(char *src,int *len) noexcept;
347
348  public:
349   void pup(PUP::er &p) noexcept;
350
351   win_obj() noexcept;
352   win_obj(const char *name, void *base, MPI_Aint size, int disp_unit, MPI_Comm comm) noexcept;
353   ~win_obj() noexcept;
354
355   int create(const char *name, void *base, MPI_Aint size, int disp_unit,
356              MPI_Comm comm) noexcept;
357   int free() noexcept;
358
359   vector<int>& getKeyvals() { return keyvals; }
360
361   int put(void *orgaddr, int orgcnt, int orgunit,
362           MPI_Aint targdisp, int targcnt, int targunit) noexcept;
363
364   int get(void *orgaddr, int orgcnt, int orgunit,
365           MPI_Aint targdisp, int targcnt, int targunit) noexcept;
366   int accumulate(void *orgaddr, int count, MPI_Aint targdisp, MPI_Datatype targtype,
367                  MPI_Op op, ampiParent* pptr) noexcept;
368
369   int iget(int orgcnt, MPI_Datatype orgtype,
370           MPI_Aint targdisp, int targcnt, MPI_Datatype targtype) noexcept;
371   int igetWait(MPI_Request *req, MPI_Status *status) noexcept;
372   int igetFree(MPI_Request *req, MPI_Status *status) noexcept;
373
374   int fence() noexcept;
375
376   int lock(int requestRank, int lock_type) noexcept;
377   int unlock(int requestRank) noexcept;
378
379   int wait() noexcept;
380   int post() noexcept;
381   int start() noexcept;
382   int complete() noexcept;
383
384   void lockTopQueue() noexcept;
385   void enqueue(int requestRank, int lock_type) noexcept;
386   void dequeue() noexcept;
387   bool emptyQueue() noexcept;
388 };
389 //-----------------------End of code by YAN ----------------------
390
391 class KeyvalPair{
392  protected:
393   std::string key;
394   std::string val;
395  public:
396   KeyvalPair() = default;
397   KeyvalPair(const char* k, const char* v) noexcept;
398   ~KeyvalPair() = default;
399   void pup(PUP::er& p) noexcept {
400     p|key;
401     p|val;
402   }
403   friend class InfoStruct;
404 };
405
406 class InfoStruct{
407   CkPupPtrVec<KeyvalPair> nodes;
408   bool valid;
409  public:
410   InfoStruct() noexcept : valid(true) { }
411   void setvalid(bool valid_) noexcept { valid = valid_; }
412   bool getvalid() const noexcept { return valid; }
413   int set(const char* k, const char* v) noexcept;
414   int dup(InfoStruct& src) noexcept;
415   int get(const char* k, int vl, char*& v, int *flag) const noexcept;
416   int deletek(const char* k) noexcept;
417   int get_valuelen(const char* k, int* vl, int *flag) const noexcept;
418   int get_nkeys(int *nkeys) const noexcept;
419   int get_nthkey(int n,char* k) const noexcept;
420   void myfree() noexcept;
421   void pup(PUP::er& p) noexcept;
422 };
423
424 class CProxy_ampi;
425 class CProxyElement_ampi;
426
427 //Virtual class describing a virtual topology: Cart, Graph, DistGraph
428 class ampiTopology {
429  private:
430   vector<int> v; // dummy variable for const& returns from virtual functions
431
432  public:
433   virtual ~ampiTopology() noexcept {};
434   virtual void pup(PUP::er &p) noexcept =0;
435   virtual int getType() const noexcept =0;
436   virtual void dup(ampiTopology* topo) noexcept =0;
437   virtual const vector<int> &getnbors() const noexcept =0;
438   virtual void setnbors(const vector<int> &nbors_) noexcept =0;
439
440   virtual const vector<int> &getdims() const noexcept {CkAbort("AMPI: instance of invalid Virtual Topology class."); return v;}
441   virtual const vector<int> &getperiods() const noexcept {CkAbort("AMPI: instance of invalid Virtual Topology class."); return v;}
442   virtual int getndims() const noexcept {CkAbort("AMPI: instance of invalid Virtual Topology class."); return -1;}
443   virtual void setdims(const vector<int> &dims_) noexcept {CkAbort("AMPI: instance of invalid Virtual Topology class.");}
444   virtual void setperiods(const vector<int> &periods_) noexcept {CkAbort("AMPI: instance of invalid Virtual Topology class.");}
445   virtual void setndims(int ndims_) noexcept {CkAbort("AMPI: instance of invalid Virtual Topology class.");}
446
447   virtual int getnvertices() const noexcept {CkAbort("AMPI: instance of invalid Virtual Topology class."); return -1;}
448   virtual const vector<int> &getindex() const noexcept {CkAbort("AMPI: instance of invalid Virtual Topology class."); return v;}
449   virtual const vector<int> &getedges() const noexcept {CkAbort("AMPI: instance of invalid Virtual Topology class."); return v;}
450   virtual void setnvertices(int nvertices_) noexcept {CkAbort("AMPI: instance of invalid Virtual Topology class.");}
451   virtual void setindex(const vector<int> &index_) noexcept {CkAbort("AMPI: instance of invalid Virtual Topology class.");}
452   virtual void setedges(const vector<int> &edges_) noexcept {CkAbort("AMPI: instance of invalid Virtual Topology class.");}
453
454   virtual int getInDegree() const noexcept {CkAbort("AMPI: instance of invalid Virtual Topology class."); return -1;}
455   virtual const vector<int> &getSources() const noexcept {CkAbort("AMPI: instance of invalid Virtual Topology class."); return v;}
456   virtual const vector<int> &getSourceWeights() const noexcept {CkAbort("AMPI: instance of invalid Virtual Topology class."); return v;}
457   virtual int getOutDegree() const noexcept {CkAbort("AMPI: instance of invalid Virtual Topology class."); return -1;}
458   virtual const vector<int> &getDestinations() const noexcept {CkAbort("AMPI: instance of invalid Virtual Topology class."); return v;}
459   virtual const vector<int> &getDestWeights() const noexcept {CkAbort("AMPI: instance of invalid Virtual Topology class."); return v;}
460   virtual bool areSourcesWeighted() const noexcept {CkAbort("AMPI: instance of invalid Virtual Topology class."); return false;}
461   virtual bool areDestsWeighted() const noexcept {CkAbort("AMPI: instance of invalid Virtual Topology class."); return false;}
462   virtual void setAreSourcesWeighted(bool val) noexcept {CkAbort("AMPI: instance of invalid Virtual Topology class.");}
463   virtual void setAreDestsWeighted(bool val) noexcept {CkAbort("AMPI: instance of invalid Virtual Topology class.");}
464   virtual void setInDegree(int degree) noexcept {CkAbort("AMPI: instance of invalid Virtual Topology class.");}
465   virtual void setSources(const vector<int> &sources) noexcept {CkAbort("AMPI: instance of invalid Virtual Topology class.");}
466   virtual void setSourceWeights(const vector<int> &sourceWeights) noexcept {CkAbort("AMPI: instance of invalid Virtual Topology class.");}
467   virtual void setOutDegree(int degree) noexcept {CkAbort("AMPI: instance of invalid Virtual Topology class.");}
468   virtual void setDestinations(const vector<int> &destinations) noexcept {CkAbort("AMPI: instance of invalid Virtual Topology class.");}
469   virtual void setDestWeights(const vector<int> &destWeights) noexcept {CkAbort("AMPI: instance of invalid Virtual Topology class.");}
470 };
471
472 class ampiCartTopology final : public ampiTopology {
473  private:
474   int ndims;
475   vector<int> dims, periods, nbors;
476
477  public:
478   ampiCartTopology() noexcept : ndims(-1) {}
479
480   void pup(PUP::er &p) noexcept {
481     p|ndims;
482     p|dims;
483     p|periods;
484     p|nbors;
485   }
486
487   inline int getType() const noexcept {return MPI_CART;}
488   inline void dup(ampiTopology* topo) noexcept {
489     CkAssert(topo->getType() == MPI_CART);
490     setndims(topo->getndims());
491     setdims(topo->getdims());
492     setperiods(topo->getperiods());
493     setnbors(topo->getnbors());
494   }
495
496   inline const vector<int> &getdims() const noexcept {return dims;}
497   inline const vector<int> &getperiods() const noexcept {return periods;}
498   inline int getndims() const noexcept {return ndims;}
499   inline const vector<int> &getnbors() const noexcept {return nbors;}
500
501   inline void setdims(const vector<int> &d) noexcept {dims = d; dims.shrink_to_fit();}
502   inline void setperiods(const vector<int> &p) noexcept {periods = p; periods.shrink_to_fit();}
503   inline void setndims(int nd) noexcept {ndims = nd;}
504   inline void setnbors(const vector<int> &n) noexcept {nbors = n; nbors.shrink_to_fit();}
505 };
506
507 class ampiGraphTopology final : public ampiTopology {
508  private:
509   int nvertices;
510   vector<int> index, edges, nbors;
511
512  public:
513   ampiGraphTopology() noexcept : nvertices(-1) {}
514
515   void pup(PUP::er &p) noexcept {
516     p|nvertices;
517     p|index;
518     p|edges;
519     p|nbors;
520   }
521
522   inline int getType() const noexcept {return MPI_GRAPH;}
523   inline void dup(ampiTopology* topo) noexcept {
524     CkAssert(topo->getType() == MPI_GRAPH);
525     setnvertices(topo->getnvertices());
526     setindex(topo->getindex());
527     setedges(topo->getedges());
528     setnbors(topo->getnbors());
529   }
530
531   inline int getnvertices() const noexcept {return nvertices;}
532   inline const vector<int> &getindex() const noexcept {return index;}
533   inline const vector<int> &getedges() const noexcept {return edges;}
534   inline const vector<int> &getnbors() const noexcept {return nbors;}
535
536   inline void setnvertices(int nv) noexcept {nvertices = nv;}
537   inline void setindex(const vector<int> &i) noexcept {index = i; index.shrink_to_fit();}
538   inline void setedges(const vector<int> &e) noexcept {edges = e; edges.shrink_to_fit();}
539   inline void setnbors(const vector<int> &n) noexcept {nbors = n; nbors.shrink_to_fit();}
540 };
541
542 class ampiDistGraphTopology final : public ampiTopology {
543  private:
544   int inDegree, outDegree;
545   bool sourcesWeighted, destsWeighted;
546   vector<int> sources, sourceWeights, destinations, destWeights, nbors;
547
548  public:
549   ampiDistGraphTopology() noexcept : inDegree(-1), outDegree(-1), sourcesWeighted(false), destsWeighted(false) {}
550
551   void pup(PUP::er &p) noexcept {
552     p|inDegree;
553     p|outDegree;
554     p|sourcesWeighted;
555     p|destsWeighted;
556     p|sources;
557     p|sourceWeights;
558     p|destinations;
559     p|destWeights;
560     p|nbors;
561   }
562
563   inline int getType() const noexcept {return MPI_DIST_GRAPH;}
564   inline void dup(ampiTopology* topo) noexcept {
565     CkAssert(topo->getType() == MPI_DIST_GRAPH);
566     setAreSourcesWeighted(topo->areSourcesWeighted());
567     setAreDestsWeighted(topo->areDestsWeighted());
568     setInDegree(topo->getInDegree());
569     setSources(topo->getSources());
570     setSourceWeights(topo->getSourceWeights());
571     setOutDegree(topo->getOutDegree());
572     setDestinations(topo->getDestinations());
573     setDestWeights(topo->getDestWeights());
574     setnbors(topo->getnbors());
575   }
576
577   inline int getInDegree() const noexcept {return inDegree;}
578   inline const vector<int> &getSources() const noexcept {return sources;}
579   inline const vector<int> &getSourceWeights() const noexcept {return sourceWeights;}
580   inline int getOutDegree() const noexcept {return outDegree;}
581   inline const vector<int> &getDestinations() const noexcept {return destinations;}
582   inline const vector<int> &getDestWeights() const noexcept {return destWeights;}
583   inline bool areSourcesWeighted() const noexcept {return sourcesWeighted;}
584   inline bool areDestsWeighted() const noexcept {return destsWeighted;}
585   inline const vector<int> &getnbors() const noexcept {return nbors;}
586
587   inline void setAreSourcesWeighted(bool v) noexcept {sourcesWeighted = v ? 1 : 0;}
588   inline void setAreDestsWeighted(bool v) noexcept {destsWeighted = v ? 1 : 0;}
589   inline void setInDegree(int d) noexcept {inDegree = d;}
590   inline void setSources(const vector<int> &s) noexcept {sources = s; sources.shrink_to_fit();}
591   inline void setSourceWeights(const vector<int> &sw) noexcept {sourceWeights = sw; sourceWeights.shrink_to_fit();}
592   inline void setOutDegree(int d) noexcept {outDegree = d;}
593   inline void setDestinations(const vector<int> &d) noexcept {destinations = d; destinations.shrink_to_fit();}
594   inline void setDestWeights(const vector<int> &dw) noexcept {destWeights = dw; destWeights.shrink_to_fit();}
595   inline void setnbors(const vector<int> &nbors_) noexcept {nbors = nbors_; nbors.shrink_to_fit();}
596 };
597
598 /* KeyValue class for attribute caching */
599 class KeyvalNode {
600  public:
601   void *val;
602   MPI_Copy_function *copy_fn;
603   MPI_Delete_function *delete_fn;
604   void *extra_state;
605   int refCount;
606   bool isValSet;
607
608   KeyvalNode() : val(NULL), copy_fn(NULL), delete_fn(NULL), extra_state(NULL), refCount(1), isValSet(false) { }
609   KeyvalNode(MPI_Copy_function *cf, MPI_Delete_function *df, void* es) :
610              val(NULL), copy_fn(cf), delete_fn(df), extra_state(es), refCount(1), isValSet(false) { }
611   bool hasVal() const { return isValSet; }
612   void clearVal() { isValSet = false; }
613   void setVal(void *v) { val = v; isValSet = true; }
614   void* getVal() const { return val; }
615   void incRefCount() { refCount++; }
616   int decRefCount() { CkAssert(refCount > 0); refCount--; return refCount; }
617   void pup(PUP::er& p) {
618     p((char *)val, sizeof(void *));
619     p((char *)copy_fn, sizeof(void *));
620     p((char *)delete_fn, sizeof(void *));
621     p((char *)extra_state, sizeof(void *));
622     p|refCount;
623     p|isValSet;
624   }
625 };
626
627 // Only store Group ranks explicitly when they can't be
628 // lazily and transiently created via std::iota()
629 class groupStruct {
630  private:
631   int sz; // -1 if ranks is valid, otherwise the size to pass to std::iota()
632   vector<int> ranks;
633
634  private:
635   bool ranksIsIota() const noexcept {
636     for (int i=0; i<ranks.size(); i++)
637       if (ranks[i] != i)
638         return false;
639     return true;
640   }
641
642  public:
643   groupStruct() noexcept : sz(0) {}
644   groupStruct(int s) noexcept : sz(s) {}
645   groupStruct(vector<int> r) noexcept : sz(-1), ranks(std::move(r)) {
646     if (ranksIsIota()) {
647       sz = ranks.size();
648       ranks.clear();
649     }
650     ranks.shrink_to_fit();
651   }
652   groupStruct &operator=(const groupStruct &obj) noexcept {
653     sz = obj.sz;
654     ranks = obj.ranks;
655     return *this;
656   }
657   ~groupStruct() = default;
658   void pup(PUP::er& p) noexcept {
659     p|sz;
660     p|ranks;
661   }
662   bool isIota() const noexcept {return (sz != -1);}
663   int operator[](int i) const noexcept {return (isIota()) ? i : ranks[i];}
664   int size() const noexcept {return (isIota()) ? sz : ranks.size();}
665   vector<int> getRanks() const noexcept {
666     if (isIota()) {
667       // Lazily create ranks:
668       vector<int> tmpRanks(sz);
669       std::iota(tmpRanks.begin(), tmpRanks.end(), 0);
670       tmpRanks.shrink_to_fit();
671       return tmpRanks;
672     }
673     else {
674       return ranks;
675     }
676   }
677 };
678
679 enum AmpiCommType : uint8_t {
680    WORLD = 0
681   ,INTRA = 1
682   ,INTER = 2
683 };
684
685 //Describes an AMPI communicator
686 class ampiCommStruct {
687  private:
688   MPI_Comm comm; //Communicator
689   CkArrayID ampiID; //ID of corresponding ampi array
690   int size; //Number of processes in communicator
691   AmpiCommType commType; //COMM_WORLD, intracomm, intercomm?
692   groupStruct indices;  //indices[r] gives the array index for rank r
693   groupStruct remoteIndices;  // remote group for inter-communicator
694
695   ampiTopology *ampiTopo; // Virtual topology
696   int topoType; // Type of virtual topology: MPI_CART, MPI_GRAPH, MPI_DIST_GRAPH, or MPI_UNDEFINED
697
698   // For communicator attributes (MPI_*_get_attr): indexed by keyval
699   vector<int> keyvals;
700
701   // For communicator names
702   std::string commName;
703
704  public:
705   ampiCommStruct(int ignored=0) noexcept
706     : size(-1), commType(INTRA), ampiTopo(NULL), topoType(MPI_UNDEFINED)
707   {}
708   ampiCommStruct(MPI_Comm comm_,const CkArrayID &id_,int size_) noexcept
709     : comm(comm_), ampiID(id_),size(size_), commType(WORLD), indices(size_),
710       ampiTopo(NULL), topoType(MPI_UNDEFINED)
711   {}
712   ampiCommStruct(MPI_Comm comm_,const CkArrayID &id_, const vector<int> &indices_) noexcept
713     : comm(comm_), ampiID(id_), size(indices_.size()), commType(INTRA), indices(indices_),
714       ampiTopo(NULL), topoType(MPI_UNDEFINED)
715   {}
716   ampiCommStruct(MPI_Comm comm_, const CkArrayID &id_, const vector<int> &indices_,
717                  const vector<int> &remoteIndices_) noexcept
718     : comm(comm_), ampiID(id_), size(indices_.size()), commType(INTER), indices(indices_),
719       remoteIndices(remoteIndices_), ampiTopo(NULL), topoType(MPI_UNDEFINED)
720   {}
721
722   ~ampiCommStruct() noexcept {
723     if (ampiTopo != NULL)
724       delete ampiTopo;
725   }
726
727   // Overloaded copy constructor. Used when creating virtual topologies.
728   ampiCommStruct(const ampiCommStruct &obj, int topoNumber=MPI_UNDEFINED) noexcept {
729     switch (topoNumber) {
730       case MPI_CART:
731         ampiTopo = new ampiCartTopology();
732         break;
733       case MPI_GRAPH:
734         ampiTopo = new ampiGraphTopology();
735         break;
736       case MPI_DIST_GRAPH:
737         ampiTopo = new ampiDistGraphTopology();
738         break;
739       default:
740         ampiTopo = NULL;
741         break;
742     }
743     topoType       = topoNumber;
744     comm           = obj.comm;
745     ampiID         = obj.ampiID;
746     size           = obj.size;
747     commType       = obj.commType;
748     indices        = obj.indices;
749     remoteIndices  = obj.remoteIndices;
750     keyvals        = obj.keyvals;
751     commName       = obj.commName;
752   }
753
754   ampiCommStruct &operator=(const ampiCommStruct &obj) noexcept {
755     if (this == &obj) {
756       return *this;
757     }
758     switch (obj.topoType) {
759       case MPI_CART:
760         ampiTopo = new ampiCartTopology(*(static_cast<ampiCartTopology*>(obj.ampiTopo)));
761         break;
762       case MPI_GRAPH:
763         ampiTopo = new ampiGraphTopology(*(static_cast<ampiGraphTopology*>(obj.ampiTopo)));
764         break;
765       case MPI_DIST_GRAPH:
766         ampiTopo = new ampiDistGraphTopology(*(static_cast<ampiDistGraphTopology*>(obj.ampiTopo)));
767         break;
768       default:
769         ampiTopo = NULL;
770         break;
771     }
772     topoType       = obj.topoType;
773     comm           = obj.comm;
774     ampiID         = obj.ampiID;
775     size           = obj.size;
776     commType       = obj.commType;
777     indices        = obj.indices;
778     remoteIndices  = obj.remoteIndices;
779     keyvals        = obj.keyvals;
780     commName       = obj.commName;
781     return *this;
782   }
783
784   const ampiTopology* getTopologyforNeighbors() const noexcept {
785     return ampiTopo;
786   }
787
788   ampiTopology* getTopology() noexcept {
789     return ampiTopo;
790   }
791
792   inline bool isinter() const noexcept {return commType==INTER;}
793   void setArrayID(const CkArrayID &nID) noexcept {ampiID=nID;}
794
795   MPI_Comm getComm() const noexcept {return comm;}
796   inline vector<int> getIndices() const noexcept {return indices.getRanks();}
797   inline vector<int> getRemoteIndices() const noexcept {return remoteIndices.getRanks();}
798   vector<int> &getKeyvals() noexcept {return keyvals;}
799
800   void setName(const char *src) noexcept {
801     CkDDT_SetName(commName, src);
802   }
803
804   void getName(char *name, int *len) const noexcept {
805     int length = *len = commName.size();
806     memcpy(name, commName.data(), length);
807     name[length] = '\0';
808   }
809
810   //Get the proxy for the entire array
811   CProxy_ampi getProxy() const noexcept;
812
813   //Get the array index for rank r in this communicator
814   int getIndexForRank(int r) const noexcept {
815 #if CMK_ERROR_CHECKING
816     if (r>=size) CkAbort("AMPI> You passed in an out-of-bounds process rank!");
817 #endif
818     return indices[r];
819   }
820   int getIndexForRemoteRank(int r) const noexcept {
821 #if CMK_ERROR_CHECKING
822     if (r>=remoteIndices.size()) CkAbort("AMPI> You passed in an out-of-bounds intercomm remote process rank!");
823 #endif
824     return remoteIndices[r];
825   }
826   //Get the rank for this array index (Warning: linear time)
827   int getRankForIndex(int i) const noexcept {
828     if (indices.isIota()) return i;
829     else {
830       const vector<int>& ind = indices.getRanks();
831       for (int r=0;r<ind.size();r++)
832         if (ind[r]==i) return r;
833       return -1; /*That index isn't in this communicator*/
834     }
835   }
836
837   int getSize() const noexcept {return size;}
838
839   void pup(PUP::er &p) noexcept {
840     p|comm;
841     p|ampiID;
842     p|size;
843     p|commType;
844     p|indices;
845     p|remoteIndices;
846     p|keyvals;
847     p|commName;
848     p|topoType;
849     if (topoType != MPI_UNDEFINED) {
850       if (p.isUnpacking()) {
851         switch (topoType) {
852           case MPI_CART:
853             ampiTopo = new ampiCartTopology();
854             break;
855           case MPI_GRAPH:
856             ampiTopo = new ampiGraphTopology();
857             break;
858           case MPI_DIST_GRAPH:
859             ampiTopo = new ampiDistGraphTopology();
860             break;
861           default:
862             CkAbort("AMPI> Communicator has an invalid topology!");
863             break;
864         }
865       }
866       ampiTopo->pup(p);
867     } else {
868       ampiTopo = NULL;
869     }
870     if (p.isDeleting()) {
871       delete ampiTopo; ampiTopo = NULL;
872     }
873   }
874 };
875 PUPmarshall(ampiCommStruct)
876
877 class mpi_comm_worlds{
878   ampiCommStruct comms[MPI_MAX_COMM_WORLDS];
879  public:
880   ampiCommStruct &operator[](int i) noexcept {return comms[i];}
881   void pup(PUP::er &p) noexcept {
882     for (int i=0;i<MPI_MAX_COMM_WORLDS;i++)
883       comms[i].pup(p);
884   }
885 };
886
887 // group operations
888 inline void outputOp(const vector<int>& vec) noexcept {
889   if (vec.size() > 50) {
890     CkPrintf("vector too large to output!\n");
891     return;
892   }
893   CkPrintf("output vector: size=%d {",vec.size());
894   for (int i=0; i<vec.size(); i++) {
895     CkPrintf(" %d ", vec[i]);
896   }
897   CkPrintf("}\n");
898 }
899
900 inline int getPosOp(int idx, const vector<int>& vec) noexcept {
901   for (int r=0; r<vec.size(); r++) {
902     if (vec[r] == idx) {
903       return r;
904     }
905   }
906   return MPI_UNDEFINED;
907 }
908
909 inline vector<int> unionOp(const vector<int>& vec1, const vector<int>& vec2) noexcept {
910   vector<int> newvec(vec1);
911   for (int i=0; i<vec2.size(); i++) {
912     if (getPosOp(vec2[i], vec1) == MPI_UNDEFINED) {
913       newvec.push_back(vec2[i]);
914     }
915   }
916   return newvec;
917 }
918
919 inline vector<int> intersectOp(const vector<int>& vec1, const vector<int>& vec2) noexcept {
920   vector<int> newvec;
921   for (int i=0; i<vec1.size(); i++) {
922     if (getPosOp(vec1[i], vec2) != MPI_UNDEFINED) {
923       newvec.push_back(vec1[i]);
924     }
925   }
926   return newvec;
927 }
928
929 inline vector<int> diffOp(const vector<int>& vec1, const vector<int>& vec2) noexcept {
930   vector<int> newvec;
931   for (int i=0; i<vec1.size(); i++) {
932     if (getPosOp(vec1[i], vec2) == MPI_UNDEFINED) {
933       newvec.push_back(vec1[i]);
934     }
935   }
936   return newvec;
937 }
938
939 inline int* translateRanksOp(int n, const vector<int>& vec1, const int* ranks1,
940                              const vector<int>& vec2, int *ret) noexcept {
941   for (int i=0; i<n; i++) {
942     ret[i] = (ranks1[i] == MPI_PROC_NULL) ? MPI_PROC_NULL : getPosOp(vec1[ranks1[i]], vec2);
943   }
944   return ret;
945 }
946
947 inline int compareVecOp(const vector<int>& vec1, const vector<int>& vec2) noexcept {
948   int pos, ret = MPI_IDENT;
949   if (vec1.size() != vec2.size()) {
950     return MPI_UNEQUAL;
951   }
952   for (int i=0; i<vec1.size(); i++) {
953     pos = getPosOp(vec1[i], vec2);
954     if (pos == MPI_UNDEFINED) {
955       return MPI_UNEQUAL;
956     }
957     else if (pos != i) {
958       ret = MPI_SIMILAR;
959     }
960   }
961   return ret;
962 }
963
964 inline vector<int> inclOp(int n, const int* ranks, const vector<int>& vec) noexcept {
965   vector<int> retvec(n);
966   for (int i=0; i<n; i++) {
967     retvec[i] = vec[ranks[i]];
968   }
969   return retvec;
970 }
971
972 inline vector<int> exclOp(int n, const int* ranks, const vector<int>& vec) noexcept {
973   vector<int> retvec;
974   bool add = true;
975   for (int j=0; j<vec.size(); j++) {
976     for (int i=0; i<n; i++) {
977       if (j == ranks[i]) {
978         add = false;
979         break;
980       }
981     }
982     if (add) {
983       retvec.push_back(vec[j]);
984     }
985     else {
986       add = true;
987     }
988   }
989   return retvec;
990 }
991
992 inline vector<int> rangeInclOp(int n, int ranges[][3], const vector<int>& vec,
993                                int *flag) noexcept {
994   vector<int> retvec;
995   int first, last, stride;
996   for (int i=0; i<n; i++) {
997     first  = ranges[i][0];
998     last   = ranges[i][1];
999     stride = ranges[i][2];
1000     if (stride != 0) {
1001       for (int j=0; j<=(last-first)/stride; j++) {
1002         retvec.push_back(vec[first+stride*j]);
1003       }
1004     }
1005     else {
1006       *flag = MPI_ERR_ARG;
1007       return vector<int>();
1008     }
1009   }
1010   *flag = MPI_SUCCESS;
1011   return retvec;
1012 }
1013
1014 inline vector<int> rangeExclOp(int n, int ranges[][3], const vector<int>& vec,
1015                                int *flag) noexcept {
1016   vector<int> ranks;
1017   int first, last, stride;
1018   for (int i=0; i<n; i++) {
1019     first  = ranges[i][0];
1020     last   = ranges[i][1];
1021     stride = ranges[i][2];
1022     if (stride != 0) {
1023       for (int j=0; j<=(last-first)/stride; j++) {
1024         ranks.push_back(first+stride*j);
1025       }
1026     }
1027     else {
1028       *flag = MPI_ERR_ARG;
1029       return vector<int>();
1030     }
1031   }
1032   *flag = MPI_SUCCESS;
1033   return exclOp(ranks.size(), &ranks[0], vec);
1034 }
1035
1036 #include "tcharm.h"
1037 #include "tcharmc.h"
1038
1039 #include "ampi.decl.h"
1040 #include "charm-api.h"
1041 #include <sys/stat.h> // for mkdir
1042
1043 extern int _mpi_nworlds;
1044
1045 //MPI_ANY_TAG is defined in ampi.h to MPI_TAG_UB_VALUE+1
1046 #define MPI_ATA_SEQ_TAG     MPI_TAG_UB_VALUE+2
1047 #define MPI_BCAST_TAG       MPI_TAG_UB_VALUE+3
1048 #define MPI_REDN_TAG        MPI_TAG_UB_VALUE+4
1049 #define MPI_SCATTER_TAG     MPI_TAG_UB_VALUE+5
1050 #define MPI_SCAN_TAG        MPI_TAG_UB_VALUE+6
1051 #define MPI_EXSCAN_TAG      MPI_TAG_UB_VALUE+7
1052 #define MPI_ATA_TAG         MPI_TAG_UB_VALUE+8
1053 #define MPI_NBOR_TAG        MPI_TAG_UB_VALUE+9
1054 #define MPI_RMA_TAG         MPI_TAG_UB_VALUE+10
1055 #define MPI_EPOCH_START_TAG MPI_TAG_UB_VALUE+11
1056 #define MPI_EPOCH_END_TAG   MPI_TAG_UB_VALUE+12
1057
1058 #define AMPI_COLL_SOURCE 0
1059 #define AMPI_COLL_COMM   MPI_COMM_WORLD
1060
1061 enum AmpiReqType : uint8_t {
1062   AMPI_INVALID_REQ = 0,
1063   AMPI_I_REQ       = 1,
1064   AMPI_ATA_REQ     = 2,
1065   AMPI_SEND_REQ    = 3,
1066   AMPI_SSEND_REQ   = 4,
1067   AMPI_REDN_REQ    = 5,
1068   AMPI_GATHER_REQ  = 6,
1069   AMPI_GATHERV_REQ = 7,
1070   AMPI_G_REQ       = 8,
1071 #if CMK_CUDA
1072   AMPI_GPU_REQ     = 9
1073 #endif
1074 };
1075
1076 inline void operator|(PUP::er &p, AmpiReqType &r) {
1077   pup_bytes(&p, (void *)&r, sizeof(AmpiReqType));
1078 }
1079
1080 enum AmpiReqSts : char {
1081   AMPI_REQ_PENDING   = 0,
1082   AMPI_REQ_BLOCKED   = 1,
1083   AMPI_REQ_COMPLETED = 2
1084 };
1085
1086 enum AmpiSendType : bool {
1087   BLOCKING_SEND = false,
1088   I_SEND = true
1089 };
1090
1091 #define MyAlign8(x) (((x)+7)&(~7))
1092
1093 /**
1094 Represents an MPI request that has been initiated
1095 using Isend, Irecv, Ialltoall, Send_init, etc.
1096 */
1097 class AmpiRequest {
1098  public:
1099   void *buf          = nullptr;
1100   int count          = 0;
1101   MPI_Datatype type  = MPI_DATATYPE_NULL;
1102   int tag            = MPI_ANY_TAG; // the order must match MPI_Status
1103   int src            = MPI_ANY_SOURCE;
1104   MPI_Comm comm      = MPI_COMM_NULL;
1105   MPI_Request reqIdx = MPI_REQUEST_NULL;
1106   bool complete      = false;
1107   bool blocked       = false; // this req is currently blocked on
1108
1109 #if CMK_BIGSIM_CHARM
1110  public:
1111   void *event        = nullptr; // the event point that corresponds to this message
1112   int eventPe        = -1; // the PE that the event is located on
1113 #endif
1114
1115  public:
1116   AmpiRequest() =default;
1117   virtual ~AmpiRequest() =default;
1118
1119   /// Activate this persistent request.
1120   ///  Only meaningful for persistent Ireq, SendReq, and SsendReq requests.
1121   virtual void start(MPI_Request reqIdx) noexcept {}
1122
1123   /// Used by AmmEntry's constructor
1124   virtual int getTag() const noexcept { return tag; }
1125   virtual int getSrcRank() const noexcept { return src; }
1126
1127   /// Return true if this request is finished (progress):
1128   virtual bool test(MPI_Status *sts=MPI_STATUS_IGNORE) noexcept =0;
1129
1130   /// Block until this request is finished,
1131   ///  returning a valid MPI error code.
1132   virtual int wait(MPI_Status *sts) noexcept =0;
1133
1134   /// Mark this request for cancellation.
1135   /// Supported only for IReq requests
1136   virtual void cancel() noexcept {}
1137
1138   /// Mark this request persistent.
1139   /// Supported only for IReq, SendReq, and SsendReq requests
1140   virtual void setPersistent(bool p) noexcept {}
1141   virtual bool isPersistent() const noexcept { return false; }
1142
1143   /// Receive an AmpiMsg
1144   virtual void receive(ampi *ptr, AmpiMsg *msg, bool deleteMsg=true) noexcept =0;
1145
1146   /// Receive a CkReductionMsg
1147   virtual void receive(ampi *ptr, CkReductionMsg *msg) noexcept =0;
1148
1149   /// Receive an Rdma message
1150   virtual void receiveRdma(ampi *ptr, char *sbuf, int slength, int ssendReq,
1151                            int srcRank, MPI_Comm scomm) noexcept { }
1152
1153   /// Set the request's index into AmpiRequestList
1154   void setReqIdx(MPI_Request idx) noexcept { reqIdx = idx; }
1155   MPI_Request getReqIdx() const noexcept { return reqIdx; }
1156
1157   /// Free the request's datatype
1158   void free(CkDDT* ddt) noexcept {
1159     if (type != MPI_DATATYPE_NULL) ddt->freeType(type);
1160   }
1161
1162   /// Set whether the request is currently blocked on
1163   void setBlocked(bool b) noexcept { blocked = b; }
1164   bool isBlocked() const noexcept { return blocked; }
1165
1166   /// Returns the type of request:
1167   ///  AMPI_I_REQ, AMPI_ATA_REQ, AMPI_SEND_REQ, AMPI_SSEND_REQ,
1168   ///  AMPI_REDN_REQ, AMPI_GATHER_REQ, AMPI_GATHERV_REQ, AMPI_G_REQ
1169   virtual AmpiReqType getType() const noexcept =0;
1170
1171   /// Returns whether this request will need to be matched.
1172   /// It is used to determine whether this request should be inserted into postedReqs.
1173   /// AMPI_SEND_REQ, AMPI_SSEND_REQ, and AMPI_ATA_REQ should not be posted.
1174   virtual bool isUnmatched() const noexcept =0;
1175
1176   /// Returns whether this type is pooled or not:
1177   /// Only AMPI_I_REQ, AMPI_SEND_REQ, and AMPI_SSEND_REQs are pooled.
1178   virtual bool isPooledType() const noexcept { return false; }
1179
1180   /// Return the actual number of bytes that were received.
1181   virtual int getNumReceivedBytes(CkDDT *ddt) const noexcept {
1182     // by default, return number of bytes requested
1183     return count * ddt->getSize(type);
1184   }
1185
1186   virtual void pup(PUP::er &p) noexcept {
1187     p((char *)&buf, sizeof(void *)); //supposed to work only with Isomalloc
1188     p(count);
1189     p(type);
1190     p(tag);
1191     p(src);
1192     p(comm);
1193     p(reqIdx);
1194     p(complete);
1195     p(blocked);
1196 #if CMK_BIGSIM_CHARM
1197     //needed for bigsim out-of-core emulation
1198     //as the "log" is not moved from memory, this pointer is safe
1199     //to be reused
1200     p((char *)&event, sizeof(void *));
1201     p(eventPe);
1202 #endif
1203   }
1204
1205   virtual void print() const noexcept =0;
1206 };
1207
1208 // This is used in the constructors of the AmpiRequest types below,
1209 // assuming arguments: (MPI_Datatype type_, CkDDT* ddt_, AmpiReqSts sts_)
1210 #define AMPI_REQUEST_COMMON_INIT           \
1211 {                                          \
1212   complete = (sts_ == AMPI_REQ_COMPLETED); \
1213   blocked  = (sts_ == AMPI_REQ_BLOCKED);   \
1214   if (type_ != MPI_DATATYPE_NULL) {        \
1215     ddt_->getType(type_)->incRefCount();   \
1216   }                                        \
1217 }
1218
1219 class IReq final : public AmpiRequest {
1220  public:
1221   bool cancelled  = false; // track if request is cancelled
1222   bool persistent = false; // Is this a persistent recv request?
1223   int length      = 0; // recv'ed length in bytes
1224
1225   IReq(void *buf_, int count_, MPI_Datatype type_, int src_, int tag_,
1226        MPI_Comm comm_, CkDDT *ddt_, AmpiReqSts sts_=AMPI_REQ_PENDING) noexcept
1227   {
1228     buf   = buf_;
1229     count = count_;
1230     type  = type_;
1231     src   = src_;
1232     tag   = tag_;
1233     comm  = comm_;
1234     AMPI_REQUEST_COMMON_INIT
1235   }
1236   IReq() =default;
1237   ~IReq() =default;
1238   bool test(MPI_Status *sts=MPI_STATUS_IGNORE) noexcept override;
1239   int wait(MPI_Status *sts) noexcept override;
1240   void cancel() noexcept override { if (!complete) cancelled = true; }
1241   AmpiReqType getType() const noexcept override { return AMPI_I_REQ; }
1242   bool isUnmatched() const noexcept override { return !complete; }
1243   bool isPooledType() const noexcept override { return true; }
1244   void setPersistent(bool p) noexcept override { persistent = p; }
1245   bool isPersistent() const noexcept override { return persistent; }
1246   void start(MPI_Request reqIdx) noexcept override;
1247   void receive(ampi *ptr, AmpiMsg *msg, bool deleteMsg=true) noexcept override;
1248   void receive(ampi *ptr, CkReductionMsg *msg) noexcept override {}
1249   void receiveRdma(ampi *ptr, char *sbuf, int slength, int ssendReq, int srcRank, MPI_Comm scomm) noexcept override;
1250   int getNumReceivedBytes(CkDDT *ptr) const noexcept override {
1251     return length;
1252   }
1253   void pup(PUP::er &p) noexcept override {
1254     AmpiRequest::pup(p);
1255     p|cancelled;
1256     p|persistent;
1257     p|length;
1258   }
1259   void print() const noexcept override;
1260 };
1261
1262 class RednReq final : public AmpiRequest {
1263  public:
1264   MPI_Op op = MPI_OP_NULL;
1265
1266   RednReq(void *buf_, int count_, MPI_Datatype type_, MPI_Comm comm_,
1267           MPI_Op op_, CkDDT* ddt_, AmpiReqSts sts_=AMPI_REQ_PENDING) noexcept
1268   {
1269     buf   = buf_;
1270     count = count_;
1271     type  = type_;
1272     src   = AMPI_COLL_SOURCE;
1273     tag   = MPI_REDN_TAG;
1274     comm  = comm_;
1275     op    = op_;
1276     AMPI_REQUEST_COMMON_INIT
1277   }
1278   RednReq() =default;
1279   ~RednReq() =default;
1280   bool test(MPI_Status *sts=MPI_STATUS_IGNORE) noexcept override;
1281   int wait(MPI_Status *sts) noexcept override;
1282   void cancel() noexcept override {}
1283   AmpiReqType getType() const noexcept override { return AMPI_REDN_REQ; }
1284   bool isUnmatched() const noexcept override { return !complete; }
1285   void receive(ampi *ptr, AmpiMsg *msg, bool deleteMsg=true) noexcept override {}
1286   void receive(ampi *ptr, CkReductionMsg *msg) noexcept override;
1287   void pup(PUP::er &p) noexcept override {
1288     AmpiRequest::pup(p);
1289     p|op;
1290   }
1291   void print() const noexcept override;
1292 };
1293
1294 class GatherReq final : public AmpiRequest {
1295  public:
1296   GatherReq(void *buf_, int count_, MPI_Datatype type_, MPI_Comm comm_,
1297             CkDDT *ddt_, AmpiReqSts sts_=AMPI_REQ_PENDING) noexcept
1298   {
1299     buf   = buf_;
1300     count = count_;
1301     type  = type_;
1302     src   = AMPI_COLL_SOURCE;
1303     tag   = MPI_REDN_TAG;
1304     comm  = comm_;
1305     AMPI_REQUEST_COMMON_INIT
1306   }
1307   GatherReq() =default;
1308   ~GatherReq() =default;
1309   bool test(MPI_Status *sts=MPI_STATUS_IGNORE) noexcept override;
1310   int wait(MPI_Status *sts) noexcept override;
1311   void cancel() noexcept override {}
1312   AmpiReqType getType() const noexcept override { return AMPI_GATHER_REQ; }
1313   bool isUnmatched() const noexcept override { return !complete; }
1314   void receive(ampi *ptr, AmpiMsg *msg, bool deleteMsg=true) noexcept override {}
1315   void receive(ampi *ptr, CkReductionMsg *msg) noexcept override;
1316   void pup(PUP::er &p) noexcept override {
1317     AmpiRequest::pup(p);
1318   }
1319   void print() const noexcept override;
1320 };
1321
1322 class GathervReq final : public AmpiRequest {
1323  public:
1324   vector<int> recvCounts;
1325   vector<int> displs;
1326
1327   GathervReq(void *buf_, int count_, MPI_Datatype type_, MPI_Comm comm_, const int *rc,
1328              const int *d, CkDDT* ddt_, AmpiReqSts sts_=AMPI_REQ_PENDING) noexcept
1329   {
1330     buf   = buf_;
1331     count = count_;
1332     type  = type_;
1333     src   = AMPI_COLL_SOURCE;
1334     tag   = MPI_REDN_TAG;
1335     comm  = comm_;
1336     recvCounts.assign(rc, rc+count);
1337     displs.assign(d, d+count);
1338     AMPI_REQUEST_COMMON_INIT
1339   }
1340   GathervReq() =default;
1341   ~GathervReq() =default;
1342   bool test(MPI_Status *sts=MPI_STATUS_IGNORE) noexcept override;
1343   int wait(MPI_Status *sts) noexcept override;
1344   AmpiReqType getType() const noexcept override { return AMPI_GATHERV_REQ; }
1345   bool isUnmatched() const noexcept override { return !complete; }
1346   void receive(ampi *ptr, AmpiMsg *msg, bool deleteMsg=true) noexcept override {}
1347   void receive(ampi *ptr, CkReductionMsg *msg) noexcept override;
1348   void pup(PUP::er &p) noexcept override {
1349     AmpiRequest::pup(p);
1350     p|recvCounts;
1351     p|displs;
1352   }
1353   void print() const noexcept override;
1354 };
1355
1356 class SendReq final : public AmpiRequest {
1357   bool persistent = false; // is this a persistent send request?
1358
1359  public:
1360   SendReq(MPI_Datatype type_, MPI_Comm comm_, CkDDT* ddt_, AmpiReqSts sts_=AMPI_REQ_PENDING) noexcept
1361   {
1362     type = type_;
1363     comm = comm_;
1364     AMPI_REQUEST_COMMON_INIT
1365   }
1366   SendReq(void* buf_, int count_, MPI_Datatype type_, int dest_, int tag_,
1367           MPI_Comm comm_, CkDDT* ddt_, AmpiReqSts sts_=AMPI_REQ_PENDING) noexcept
1368   {
1369     buf   = buf_;
1370     count = count_;
1371     type  = type_;
1372     src   = dest_;
1373     tag   = tag_;
1374     comm  = comm_;
1375     AMPI_REQUEST_COMMON_INIT
1376   }
1377   SendReq() noexcept {}
1378   ~SendReq() noexcept {}
1379   bool test(MPI_Status *sts=MPI_STATUS_IGNORE) noexcept override;
1380   int wait(MPI_Status *sts) noexcept override;
1381   void setPersistent(bool p) noexcept override { persistent = p; }
1382   bool isPersistent() const noexcept override { return persistent; }
1383   void start(MPI_Request reqIdx) noexcept override;
1384   void receive(ampi *ptr, AmpiMsg *msg, bool deleteMsg=true) noexcept override {}
1385   void receive(ampi *ptr, CkReductionMsg *msg) noexcept override {}
1386   AmpiReqType getType() const noexcept override { return AMPI_SEND_REQ; }
1387   bool isUnmatched() const noexcept override { return false; }
1388   bool isPooledType() const noexcept override { return true; }
1389   void pup(PUP::er &p) noexcept override {
1390     AmpiRequest::pup(p);
1391     p|persistent;
1392   }
1393   void print() const noexcept override;
1394 };
1395
1396 class SsendReq final : public AmpiRequest {
1397  private:
1398   bool persistent = false; // is this a persistent Ssend request?
1399
1400  public:
1401   SsendReq(MPI_Datatype type_, MPI_Comm comm_, CkDDT* ddt_, AmpiReqSts sts_=AMPI_REQ_PENDING) noexcept
1402   {
1403     type = type_;
1404     comm = comm_;
1405     AMPI_REQUEST_COMMON_INIT
1406   }
1407   SsendReq(void* buf_, int count_, MPI_Datatype type_, int dest_, int tag_, MPI_Comm comm_,
1408            CkDDT* ddt_, AmpiReqSts sts_=AMPI_REQ_PENDING) noexcept
1409   {
1410     buf   = buf_;
1411     count = count_;
1412     type  = type_;
1413     src   = dest_;
1414     tag   = tag_;
1415     comm  = comm_;
1416     AMPI_REQUEST_COMMON_INIT
1417   }
1418   SsendReq(void* buf_, int count_, MPI_Datatype type_, int dest_, int tag_, MPI_Comm comm_,
1419            int src_, CkDDT* ddt_, AmpiReqSts sts_=AMPI_REQ_PENDING) noexcept
1420   {
1421     buf   = buf_;
1422     count = count_;
1423     type  = type_;
1424     src   = dest_;
1425     tag   = tag_;
1426     comm  = comm_;
1427     AMPI_REQUEST_COMMON_INIT
1428   }
1429   SsendReq() =default;
1430   ~SsendReq() =default;
1431   bool test(MPI_Status *sts=MPI_STATUS_IGNORE) noexcept override;
1432   int wait(MPI_Status *sts) noexcept override;
1433   void setPersistent(bool p) noexcept override { persistent = p; }
1434   bool isPersistent() const noexcept override { return persistent; }
1435   void start(MPI_Request reqIdx) noexcept override;
1436   void receive(ampi *ptr, AmpiMsg *msg, bool deleteMsg=true) noexcept override {}
1437   void receive(ampi *ptr, CkReductionMsg *msg) noexcept override {}
1438   AmpiReqType getType() const noexcept override { return AMPI_SSEND_REQ; }
1439   bool isUnmatched() const noexcept override { return false; }
1440   bool isPooledType() const noexcept override { return true; }
1441   void pup(PUP::er &p) noexcept override {
1442     AmpiRequest::pup(p);
1443     p|persistent;
1444   }
1445   void print() const noexcept override;
1446 };
1447
1448 #if CMK_CUDA
1449 class GPUReq : public AmpiRequest {
1450  public:
1451   GPUReq() noexcept;
1452   ~GPUReq() =default;
1453   bool test(MPI_Status *sts=MPI_STATUS_IGNORE) noexcept override;
1454   int wait(MPI_Status *sts) noexcept override;
1455   void receive(ampi *ptr, AmpiMsg *msg, bool deleteMsg=true) noexcept override;
1456   void receive(ampi *ptr, CkReductionMsg *msg) noexcept override;
1457   AmpiReqType getType() const noexcept override { return AMPI_GPU_REQ; }
1458   bool isUnmatched() const noexcept override { return false; }
1459   void setComplete() noexcept;
1460   void print() const noexcept override;
1461 };
1462 #endif
1463
1464 class ATAReq final : public AmpiRequest {
1465  public:
1466   vector<MPI_Request> reqs;
1467
1468   ATAReq(int numReqs_) noexcept : reqs(numReqs_) {}
1469   ATAReq() =default;
1470   ~ATAReq() =default;
1471   bool test(MPI_Status *sts=MPI_STATUS_IGNORE) noexcept override;
1472   int wait(MPI_Status *sts) noexcept override;
1473   void receive(ampi *ptr, AmpiMsg *msg, bool deleteMsg=true) noexcept override {}
1474   void receive(ampi *ptr, CkReductionMsg *msg) noexcept override {}
1475   int getCount() const noexcept { return reqs.size(); }
1476   AmpiReqType getType() const noexcept override { return AMPI_ATA_REQ; }
1477   bool isUnmatched() const noexcept override { return false; }
1478   void pup(PUP::er &p) noexcept override {
1479     AmpiRequest::pup(p);
1480     p|reqs;
1481   }
1482   void print() const noexcept override;
1483 };
1484
1485 class GReq final : public AmpiRequest {
1486  private:
1487   MPI_Grequest_query_function* queryFn;
1488   MPI_Grequest_free_function* freeFn;
1489   MPI_Grequest_cancel_function* cancelFn;
1490   MPIX_Grequest_poll_function* pollFn;
1491   MPIX_Grequest_wait_function* waitFn;
1492   void* extraState;
1493
1494  public:
1495   GReq(MPI_Grequest_query_function* q, MPI_Grequest_free_function* f, MPI_Grequest_cancel_function* c, void* es) noexcept
1496     : queryFn(q), freeFn(f), cancelFn(c), pollFn(nullptr), waitFn(nullptr), extraState(es) {}
1497   GReq(MPI_Grequest_query_function *q, MPI_Grequest_free_function* f, MPI_Grequest_cancel_function* c, MPIX_Grequest_poll_function* p, void* es) noexcept
1498     : queryFn(q), freeFn(f), cancelFn(c), pollFn(p), waitFn(nullptr), extraState(es) {}
1499   GReq(MPI_Grequest_query_function *q, MPI_Grequest_free_function* f, MPI_Grequest_cancel_function* c, MPIX_Grequest_poll_function* p, MPIX_Grequest_wait_function* w, void* es) noexcept
1500     : queryFn(q), freeFn(f), cancelFn(c), pollFn(p), waitFn(w), extraState(es) {}
1501   GReq() =default;
1502   ~GReq() noexcept { (*freeFn)(extraState); }
1503   bool test(MPI_Status *sts=MPI_STATUS_IGNORE) noexcept override;
1504   int wait(MPI_Status *sts) noexcept override;
1505   void receive(ampi *ptr, AmpiMsg *msg, bool deleteMsg=true) noexcept override {}
1506   void receive(ampi *ptr, CkReductionMsg *msg) noexcept override {}
1507   void cancel() noexcept override { (*cancelFn)(extraState, complete); }
1508   AmpiReqType getType() const noexcept override { return AMPI_G_REQ; }
1509   bool isUnmatched() const noexcept override { return false; }
1510   void pup(PUP::er &p) noexcept override {
1511     AmpiRequest::pup(p);
1512     p((char *)queryFn, sizeof(void *));
1513     p((char *)freeFn, sizeof(void *));
1514     p((char *)cancelFn, sizeof(void *));
1515     p((char *)pollFn, sizeof(void *));
1516     p((char *)waitFn, sizeof(void *));
1517     p((char *)extraState, sizeof(void *));
1518   }
1519   void print() const noexcept override;
1520 };
1521
1522 class AmpiRequestPool;
1523
1524 class AmpiRequestList {
1525  private:
1526   vector<AmpiRequest*> reqs; // indexed by MPI_Request
1527   int startIdx; // start next search from this index
1528   AmpiRequestPool* reqPool;
1529  public:
1530   AmpiRequestList() noexcept : startIdx(0) {}
1531   AmpiRequestList(int size, AmpiRequestPool* reqPoolPtr) noexcept
1532     : reqs(size), startIdx(0), reqPool(reqPoolPtr) {}
1533   ~AmpiRequestList() noexcept {}
1534
1535   inline AmpiRequest* operator[](int n) noexcept {
1536 #if CMK_ERROR_CHECKING
1537     return reqs.at(n);
1538 #else
1539     return reqs[n];
1540 #endif
1541   }
1542   void free(AmpiRequestPool& reqPool, int idx, CkDDT *ddt) noexcept;
1543   void freeNonPersReq(int &idx) noexcept;
1544   inline int insert(AmpiRequest* req) noexcept {
1545     for (int i=startIdx; i<reqs.size(); i++) {
1546       if (reqs[i] == NULL) {
1547         req->setReqIdx(i);
1548         reqs[i] = req;
1549         startIdx = i+1;
1550         return i;
1551       }
1552     }
1553     reqs.push_back(req);
1554     int idx = reqs.size()-1;
1555     req->setReqIdx(idx);
1556     startIdx = idx+1;
1557     return idx;
1558   }
1559
1560   inline void checkRequest(MPI_Request idx) const noexcept {
1561     if (idx != MPI_REQUEST_NULL && (idx < 0 || idx >= reqs.size()))
1562       CkAbort("Invalid MPI_Request\n");
1563   }
1564
1565   inline void unblockReqs(MPI_Request *requests, int numReqs) noexcept {
1566     for (int i=0; i<numReqs; i++) {
1567       if (requests[i] != MPI_REQUEST_NULL) {
1568         reqs[requests[i]]->setBlocked(false);
1569       }
1570     }
1571   }
1572
1573   void pup(PUP::er &p, AmpiRequestPool* reqPool) noexcept;
1574
1575   void print() const noexcept {
1576     for (int i=0; i<reqs.size(); i++) {
1577       if (reqs[i] == NULL) continue;
1578       CkPrintf("AmpiRequestList Element %d [%p]: \n", i+1, reqs[i]);
1579       reqs[i]->print();
1580     }
1581   }
1582 };
1583
1584 //A simple memory buffer
1585 class memBuf {
1586   CkVec<char> buf;
1587  public:
1588   memBuf() =default;
1589   memBuf(int size) noexcept : buf(size) {}
1590   void setSize(int s) noexcept {buf.resize(s);}
1591   int getSize() const noexcept {return buf.size();}
1592   const void *getData() const noexcept {return (const void *)&buf[0];}
1593   void *getData() noexcept {return (void *)&buf[0];}
1594 };
1595
1596 template <class T>
1597 inline void pupIntoBuf(memBuf &b,T &t) noexcept {
1598   PUP::sizer ps;ps|t;
1599   b.setSize(ps.size());
1600   PUP::toMem pm(b.getData()); pm|t;
1601 }
1602
1603 template <class T>
1604 inline void pupFromBuf(const void *data,T &t) noexcept {
1605   PUP::fromMem p(data); p|t;
1606 }
1607
1608 #define COLL_SEQ_IDX      -1
1609
1610 class AmpiMsgPool;
1611
1612 class AmpiMsg final : public CMessage_AmpiMsg {
1613  private:
1614   int ssendReq; //Index to the sender's request
1615   int tag; //MPI tag
1616   int srcRank; //Communicator rank for source
1617   int length; //Number of bytes in this message
1618   int origLength; // true size of allocation
1619   MPI_Comm comm; // Communicator
1620  public:
1621   char *data; //Payload
1622 #if CMK_BIGSIM_CHARM
1623  public:
1624   void *event;
1625   int  eventPe; // the PE that the event is located
1626 #endif
1627
1628  public:
1629   AmpiMsg() noexcept { data = NULL; }
1630   AmpiMsg(int sreq, int t, int sRank, int l) noexcept :
1631     ssendReq(sreq), tag(t), srcRank(sRank), length(l), origLength(l)
1632   { /* only called from AmpiMsg::pup() since the refnum (seq) will get pup'ed by the runtime */ }
1633   AmpiMsg(CMK_REFNUM_TYPE seq, int sreq, int t, int sRank, int l) noexcept :
1634     ssendReq(sreq), tag(t), srcRank(sRank), length(l), origLength(l)
1635   { CkSetRefNum(this, seq); }
1636   inline void setSsendReq(int s) noexcept { CkAssert(s >= 0); ssendReq = s; }
1637   inline void setSeq(CMK_REFNUM_TYPE s) noexcept { CkAssert(s >= 0); UsrToEnv(this)->setRef(s); }
1638   inline void setSrcRank(int sr) noexcept { srcRank = sr; }
1639   inline void setLength(int l) noexcept { length = l; }
1640   inline void setTag(int t) noexcept { tag = t; }
1641   inline void setComm(MPI_Comm c) noexcept { comm = c; }
1642   inline CMK_REFNUM_TYPE getSeq() const noexcept { return UsrToEnv(this)->getRef(); }
1643   inline int getSsendReq() const noexcept { return ssendReq; }
1644   inline int getSeqIdx() const noexcept {
1645     // seqIdx is srcRank, unless this message was part of a collective
1646     if (tag >= MPI_BCAST_TAG && tag <= MPI_ATA_TAG) {
1647       return COLL_SEQ_IDX;
1648     }
1649     else {
1650       return srcRank;
1651     }
1652   }
1653   inline int getSrcRank() const noexcept { return srcRank; }
1654   inline int getLength() const noexcept { return length; }
1655   inline char* getData() const noexcept { return data; }
1656   inline int getTag() const noexcept { return tag; }
1657   inline MPI_Comm getComm() const noexcept { return comm; }
1658   static AmpiMsg* pup(PUP::er &p, AmpiMsg *m) noexcept
1659   {
1660     int ref, ssendReq, tag, srcRank, length, origLength;
1661     MPI_Comm comm;
1662     if(p.isPacking() || p.isSizing()) {
1663       ref = CkGetRefNum(m);
1664       ssendReq = m->ssendReq;
1665       tag = m->tag;
1666       srcRank = m->srcRank;
1667       length = m->length;
1668       origLength = m->origLength;
1669       comm = m->comm;
1670     }
1671     p(ref); p(ssendReq); p(tag); p(srcRank); p(length); p(origLength); p(comm);
1672     if(p.isUnpacking()) {
1673       m = new (origLength, 0) AmpiMsg(ref, ssendReq, tag, srcRank, origLength);
1674       m->setLength(length);
1675       m->setComm(comm);
1676     }
1677     p(m->data, length);
1678     if(p.isDeleting()) {
1679       delete m;
1680       m = 0;
1681     }
1682     return m;
1683   }
1684
1685   friend AmpiMsgPool;
1686 };
1687
1688 #define AMPI_MSG_POOL_SIZE   32 // Max # of AmpiMsg's allowed in the pool
1689 #define AMPI_POOLED_MSG_SIZE 64 // Max # of Bytes in pooled msgs' payload
1690
1691 class AmpiMsgPool {
1692  private:
1693   std::forward_list<AmpiMsg *> msgs; // list of free msgs
1694   int msgLength; // AmpiMsg::length of messages in the pool
1695   int maxMsgs; // max # of msgs in the pool
1696   int currMsgs; // current # of msgs in the pool
1697
1698  public:
1699   AmpiMsgPool(int _numMsgs = 0, int _msgLength = 0) noexcept
1700     : msgLength(_msgLength), maxMsgs(_numMsgs), currMsgs(0) {}
1701   ~AmpiMsgPool() =default;
1702   inline void clear() noexcept {
1703     while (!msgs.empty()) {
1704       delete msgs.front();
1705       msgs.pop_front();
1706     }
1707     currMsgs = 0;
1708   }
1709   inline AmpiMsg* newAmpiMsg(CMK_REFNUM_TYPE seq, int ssendReq, int tag, int srcRank, int len) noexcept {
1710     if (msgs.empty() || msgs.front()->origLength < len) {
1711       int newlen = std::max(msgLength, len);
1712       AmpiMsg* msg = new (newlen, 0) AmpiMsg(seq, ssendReq, tag, srcRank, newlen);
1713       msg->setLength(len);
1714       return msg;
1715     } else {
1716       AmpiMsg* msg = msgs.front();
1717       msgs.pop_front();
1718       currMsgs--;
1719       msg->setSeq(seq);
1720       msg->setSsendReq(ssendReq);
1721       msg->setTag(tag);
1722       msg->setSrcRank(srcRank);
1723       msg->setLength(len);
1724       return msg;
1725     }
1726   }
1727   inline void deleteAmpiMsg(AmpiMsg* msg) noexcept {
1728     /* msg->origLength is the true size of the message's data buffer, while
1729      * msg->length is the space taken by the payload within it. */
1730     if (currMsgs != maxMsgs && msg->origLength >= msgLength && msg->origLength < 2*msgLength) {
1731       msgs.push_front(msg);
1732       currMsgs++;
1733     } else {
1734       delete msg;
1735     }
1736   }
1737   void pup(PUP::er& p) {
1738     p|msgLength;
1739     p|maxMsgs;
1740     // Don't PUP the msgs in the free list or currMsgs, let the pool fill lazily
1741   }
1742 };
1743
1744 // Number of requests in the pool
1745 #ifndef AMPI_REQ_POOL_SIZE
1746 #define AMPI_REQ_POOL_SIZE 64
1747 #endif
1748
1749 // Helper macro for pool size and alignment calculations
1750 #define DefinePooledReqX(name, func) \
1751 static const size_t ireq##name = func(IReq); \
1752 static const size_t sreq##name = func(SendReq); \
1753 static const size_t ssreq##name = func(SsendReq); \
1754 static const size_t pooledReq##name = (ireq##name >= sreq##name && ireq##name >= ssreq##name) ? ireq##name : \
1755                                       (sreq##name >= ireq##name && sreq##name >= ssreq##name) ? sreq##name : \
1756                                       (ssreq##name);
1757
1758 // This defines 'static const size_t pooledReqSize = ... ;'
1759 DefinePooledReqX(Size, sizeof)
1760
1761 // This defines 'static const size_t pooledReqAlign = ... ;'
1762 DefinePooledReqX(Align, alignof)
1763
1764 // Pool of IReq, SendReq, and SsendReq objects:
1765 // These are different sizes, but we use a single pool for them so
1766 // that iteration over these objects is fast, as in AMPI_Waitall.
1767 // We also try to always allocate new requests from the start to the end
1768 // of the pool, so that forward iteration over requests is fast.
1769 class AmpiRequestPool {
1770  private:
1771   std::bitset<AMPI_REQ_POOL_SIZE> validReqs; // reqs in the pool are either valid (being used by a real req) or invalid
1772   int startIdx = 0; // start next search from this index
1773   alignas(pooledReqAlign) std::array<char, AMPI_REQ_POOL_SIZE*pooledReqSize> reqs; // pool of memory for requests
1774
1775  public:
1776   AmpiRequestPool() =default;
1777   ~AmpiRequestPool() =default;
1778   template <typename T, typename... Args>
1779   inline T* newReq(Args&&... args) noexcept {
1780     if (validReqs.all()) {
1781       return new T(std::forward<Args>(args)...);
1782     } else {
1783       for (int i=startIdx; i<validReqs.size(); i++) {
1784         if (!validReqs[i]) {
1785           validReqs[i] = 1;
1786           startIdx = i+1;
1787           T* req = new (&reqs[i*pooledReqSize]) T(std::forward<Args>(args)...);
1788           return req;
1789         }
1790       }
1791       CkAbort("AMPI> failed to find a free request in pool!");
1792       return NULL;
1793     }
1794   }
1795   inline void deleteReq(AmpiRequest* req) noexcept {
1796     if (req->isPooledType() &&
1797         ((char*)req >= &reqs.front() && (char*)req <= &reqs.back()))
1798     {
1799       int idx = (int)((intptr_t)req - (intptr_t)&reqs[0]) / pooledReqSize;
1800       validReqs[idx] = 0;
1801       startIdx = std::min(idx, startIdx);
1802     } else {
1803       delete req;
1804     }
1805   }
1806   void pup(PUP::er& p) noexcept {
1807     // Nothing to do here, because AmpiRequestList::pup will be the
1808     // one to actually PUP the AmpiRequest objects to/from the pool
1809   }
1810 };
1811
1812 /**
1813   Our local representation of another AMPI
1814  array element.  Used to keep track of incoming
1815  and outgoing message sequence numbers, and
1816  the out-of-order message list.
1817 */
1818 class AmpiOtherElement {
1819 private:
1820   /// Next incoming and outgoing message sequence number
1821   CMK_REFNUM_TYPE seqIncoming, seqOutgoing;
1822
1823   /// Number of messages in out-of-order queue (normally 0)
1824   uint16_t numOutOfOrder;
1825
1826 public:
1827   /// seqIncoming starts from 1, b/c 0 means unsequenced
1828   /// seqOutgoing starts from 0, b/c this will be incremented for the first real seq #
1829   AmpiOtherElement() noexcept : seqIncoming(1), seqOutgoing(0), numOutOfOrder(0) {}
1830
1831   /// Handle wrap around of unsigned type CMK_REFNUM_TYPE
1832   inline void incSeqIncoming() noexcept { seqIncoming++; if (seqIncoming==0) seqIncoming=1; }
1833   inline CMK_REFNUM_TYPE getSeqIncoming() const noexcept { return seqIncoming; }
1834
1835   inline void incSeqOutgoing() noexcept { seqOutgoing++; if (seqOutgoing==0) seqOutgoing=1; }
1836   inline CMK_REFNUM_TYPE getSeqOutgoing() const noexcept { return seqOutgoing; }
1837
1838   inline void incNumOutOfOrder() noexcept { numOutOfOrder++; }
1839   inline void decNumOutOfOrder() noexcept { numOutOfOrder--; }
1840   inline uint16_t getNumOutOfOrder() const noexcept { return numOutOfOrder; }
1841 };
1842 PUPbytes(AmpiOtherElement)
1843
1844 class AmpiSeqQ : private CkNoncopyable {
1845   CkMsgQ<AmpiMsg> out; // all out of order messages
1846   std::unordered_map<int, AmpiOtherElement> elements; // element info: indexed by seqIdx (comm rank)
1847
1848 public:
1849   AmpiSeqQ() =default;
1850   AmpiSeqQ(int commSize) noexcept {
1851     elements.reserve(std::min(commSize, 64));
1852   }
1853   ~AmpiSeqQ() =default;
1854   void pup(PUP::er &p) noexcept;
1855
1856   /// Insert this message in the table.  Returns the number
1857   /// of messages now available for the element.
1858   ///   If 0, the message was out-of-order and is buffered.
1859   ///   If 1, this message can be immediately processed.
1860   ///   If >1, this message can be immediately processed,
1861   ///     and you should call "getOutOfOrder" repeatedly.
1862   inline int put(int seqIdx, AmpiMsg *msg) noexcept {
1863     AmpiOtherElement &el = elements[seqIdx];
1864     if (msg->getSeq() == el.getSeqIncoming()) { // In order:
1865       el.incSeqIncoming();
1866       return 1+el.getNumOutOfOrder();
1867     }
1868     else { // Out of order: stash message
1869       putOutOfOrder(seqIdx, msg);
1870       return 0;
1871     }
1872   }
1873
1874   /// Is this message in order (return >0) or not (return 0)?
1875   /// Same as put() except we don't call putOutOfOrder() here,
1876   /// so the caller should do that separately
1877   inline int isInOrder(int srcRank, CMK_REFNUM_TYPE seq) noexcept {
1878     AmpiOtherElement &el = elements[srcRank];
1879     if (seq == el.getSeqIncoming()) { // In order:
1880       el.incSeqIncoming();
1881       return 1+el.getNumOutOfOrder();
1882     }
1883     else { // Out of order: caller should stash message
1884       return 0;
1885     }
1886   }
1887
1888   /// Get an out-of-order message from the table.
1889   /// (in-order messages never go into the table)
1890   AmpiMsg *getOutOfOrder(int seqIdx) noexcept;
1891
1892   /// Stash an out-of-order message
1893   void putOutOfOrder(int seqIdx, AmpiMsg *msg) noexcept;
1894
1895   /// Increment the outgoing sequence number.
1896   inline void incCollSeqOutgoing() noexcept {
1897     elements[COLL_SEQ_IDX].incSeqOutgoing();
1898   }
1899
1900   /// Return the next outgoing sequence number, and increment it.
1901   inline CMK_REFNUM_TYPE nextOutgoing(int destRank) noexcept {
1902     AmpiOtherElement &el = elements[destRank];
1903     el.incSeqOutgoing();
1904     return el.getSeqOutgoing();
1905   }
1906 };
1907 PUPmarshall(AmpiSeqQ)
1908
1909
1910 inline CProxy_ampi ampiCommStruct::getProxy() const noexcept {return ampiID;}
1911 const ampiCommStruct &universeComm2CommStruct(MPI_Comm universeNo) noexcept;
1912
1913 // Max value of a predefined MPI_Op (values defined in ampi.h)
1914 #define AMPI_MAX_PREDEFINED_OP 13
1915
1916 /*
1917 An ampiParent holds all the communicators and the TCharm thread
1918 for its children, which are bound to it.
1919 */
1920 class ampiParent final : public CBase_ampiParent {
1921  private:
1922   TCharm *thread;
1923   CProxy_TCharm threads;
1924
1925  public: // Communication state:
1926   int numBlockedReqs; // number of requests currently blocked on
1927   bool resumeOnRecv, resumeOnColl;
1928   AmpiRequestList ampiReqs;
1929   AmpiRequestPool reqPool;
1930   AmpiRequest *blockingReq;
1931   CkDDT myDDT;
1932
1933  private:
1934   MPI_Comm worldNo; //My MPI_COMM_WORLD
1935   ampi *worldPtr; //AMPI element corresponding to MPI_COMM_WORLD
1936
1937   CkPupPtrVec<ampiCommStruct> splitComm;     //Communicators from MPI_Comm_split
1938   CkPupPtrVec<ampiCommStruct> groupComm;     //Communicators from MPI_Comm_group
1939   CkPupPtrVec<ampiCommStruct> cartComm;      //Communicators from MPI_Cart_create
1940   CkPupPtrVec<ampiCommStruct> graphComm;     //Communicators from MPI_Graph_create
1941   CkPupPtrVec<ampiCommStruct> distGraphComm; //Communicators from MPI_Dist_graph_create
1942   CkPupPtrVec<ampiCommStruct> interComm;     //Communicators from MPI_Intercomm_create
1943   CkPupPtrVec<ampiCommStruct> intraComm;     //Communicators from MPI_Intercomm_merge
1944
1945   CkPupPtrVec<groupStruct> groups; // "Wild" groups that don't have a communicator
1946   CkPupPtrVec<WinStruct> winStructList; //List of windows for one-sided communication
1947   CkPupPtrVec<InfoStruct> infos; // list of all MPI_Infos
1948   const std::array<MPI_User_function*, AMPI_MAX_PREDEFINED_OP+1>& predefinedOps; // owned by ampiNodeMgr
1949   vector<OpStruct> userOps; // list of any user-defined MPI_Ops
1950   vector<AmpiMsg *> matchedMsgs; // for use with MPI_Mprobe and MPI_Mrecv
1951
1952   /* MPI_*_get_attr C binding returns a *pointer* to an integer,
1953    *  so there needs to be some storage somewhere to point to.
1954    * All builtin keyvals are ints, except for MPI_WIN_BASE, which
1955    *  is a pointer, and MPI_WIN_SIZE, which is an MPI_Aint. */
1956   int* kv_builtin_storage;
1957   MPI_Aint* win_size_storage;
1958   void** win_base_storage;
1959   CkPupPtrVec<KeyvalNode> kvlist;
1960   void* bsendBuffer;   // NOTE: we don't actually use this for buffering of MPI_Bsend's,
1961   int bsendBufferSize; //       we only keep track of it to return it from MPI_Buffer_detach
1962
1963   // Intercommunicator creation:
1964   bool isTmpRProxySet;
1965   CProxy_ampi tmpRProxy;
1966
1967   MPI_MigrateFn userAboutToMigrateFn, userJustMigratedFn;
1968
1969  public:
1970   bool ampiInitCallDone;
1971
1972  private:
1973   bool kv_set_builtin(int keyval, void* attribute_val) noexcept;
1974   bool kv_get_builtin(int keyval) noexcept;
1975
1976  public:
1977   void prepareCtv() noexcept;
1978
1979   MPI_Message putMatchedMsg(AmpiMsg* msg) noexcept {
1980     // Search thru matchedMsgs for any NULL ones first:
1981     for (int i=0; i<matchedMsgs.size(); i++) {
1982       if (matchedMsgs[i] == NULL) {
1983         matchedMsgs[i] = msg;
1984         return i;
1985       }
1986     }
1987     // No NULL entries, so create a new one:
1988     matchedMsgs.push_back(msg);
1989     return matchedMsgs.size() - 1;
1990   }
1991   AmpiMsg* getMatchedMsg(MPI_Message message) noexcept {
1992     if (message == MPI_MESSAGE_NO_PROC || message == MPI_MESSAGE_NULL) {
1993       return NULL;
1994     }
1995     CkAssert(message >= 0 && message < matchedMsgs.size());
1996     AmpiMsg* msg = matchedMsgs[message];
1997     // Mark this matchedMsg index NULL and free from back of vector:
1998     matchedMsgs[message] = NULL;
1999     while (matchedMsgs.back() == NULL) {
2000       matchedMsgs.pop_back();
2001     }
2002     return msg;
2003   }
2004
2005   inline void attachBuffer(void *buffer, int size) noexcept {
2006     bsendBuffer = buffer;
2007     bsendBufferSize = size;
2008   }
2009   inline void detachBuffer(void *buffer, int *size) noexcept {
2010     *(void **)buffer = bsendBuffer;
2011     *size = bsendBufferSize;
2012   }
2013   inline bool isSplit(MPI_Comm comm) const noexcept {
2014     return (comm>=MPI_COMM_FIRST_SPLIT && comm<MPI_COMM_FIRST_GROUP);
2015   }
2016   const ampiCommStruct &getSplit(MPI_Comm comm) const noexcept {
2017     int idx=comm-MPI_COMM_FIRST_SPLIT;
2018     if (idx>=splitComm.size()) CkAbort("Bad split communicator used");
2019     return *splitComm[idx];
2020   }
2021   void splitChildRegister(const ampiCommStruct &s) noexcept;
2022
2023   inline bool isGroup(MPI_Comm comm) const noexcept {
2024     return (comm>=MPI_COMM_FIRST_GROUP && comm<MPI_COMM_FIRST_CART);
2025   }
2026   const ampiCommStruct &getGroup(MPI_Comm comm) const noexcept {
2027     int idx=comm-MPI_COMM_FIRST_GROUP;
2028     if (idx>=groupComm.size()) CkAbort("Bad group communicator used");
2029     return *groupComm[idx];
2030   }
2031   void groupChildRegister(const ampiCommStruct &s) noexcept;
2032   inline bool isInGroups(MPI_Group group) const noexcept {
2033     return (group>=0 && group<groups.size());
2034   }
2035
2036   void cartChildRegister(const ampiCommStruct &s) noexcept;
2037   void graphChildRegister(const ampiCommStruct &s) noexcept;
2038   void distGraphChildRegister(const ampiCommStruct &s) noexcept;
2039   void interChildRegister(const ampiCommStruct &s) noexcept;
2040   void intraChildRegister(const ampiCommStruct &s) noexcept;
2041
2042  public:
2043   ampiParent(MPI_Comm worldNo_,CProxy_TCharm threads_,int nRanks_) noexcept;
2044   ampiParent(CkMigrateMessage *msg) noexcept;
2045   void ckAboutToMigrate() noexcept;
2046   void ckJustMigrated() noexcept;
2047   void ckJustRestored() noexcept;
2048   void setUserAboutToMigrateFn(MPI_MigrateFn f) noexcept;
2049   void setUserJustMigratedFn(MPI_MigrateFn f) noexcept;
2050   ~ampiParent() noexcept;
2051
2052   //Children call this when they are first created, or just migrated
2053   TCharm *registerAmpi(ampi *ptr,ampiCommStruct s,bool forMigration) noexcept;
2054
2055   // exchange proxy info between two ampi proxies
2056   void ExchangeProxy(CProxy_ampi rproxy) noexcept {
2057     if(!isTmpRProxySet){ tmpRProxy=rproxy; isTmpRProxySet=true; }
2058     else{ tmpRProxy.setRemoteProxy(rproxy); rproxy.setRemoteProxy(tmpRProxy); isTmpRProxySet=false; }
2059   }
2060
2061   //Grab the next available split/group communicator
2062   MPI_Comm getNextSplit() const noexcept {return MPI_COMM_FIRST_SPLIT+splitComm.size();}
2063   MPI_Comm getNextGroup() const noexcept {return MPI_COMM_FIRST_GROUP+groupComm.size();}
2064   MPI_Comm getNextCart() const noexcept {return MPI_COMM_FIRST_CART+cartComm.size();}
2065   MPI_Comm getNextGraph() const noexcept {return MPI_COMM_FIRST_GRAPH+graphComm.size();}
2066   MPI_Comm getNextDistGraph() const noexcept {return MPI_COMM_FIRST_DIST_GRAPH+distGraphComm.size();}
2067   MPI_Comm getNextInter() const noexcept {return MPI_COMM_FIRST_INTER+interComm.size();}
2068   MPI_Comm getNextIntra() const noexcept {return MPI_COMM_FIRST_INTRA+intraComm.size();}
2069
2070   inline bool isCart(MPI_Comm comm) const noexcept {
2071     return (comm>=MPI_COMM_FIRST_CART && comm<MPI_COMM_FIRST_GRAPH);
2072   }
2073   ampiCommStruct &getCart(MPI_Comm comm) const noexcept {
2074     int idx=comm-MPI_COMM_FIRST_CART;
2075     if (idx>=cartComm.size()) CkAbort("AMPI> Bad cartesian communicator used!\n");
2076     return *cartComm[idx];
2077   }
2078   inline bool isGraph(MPI_Comm comm) const noexcept {
2079     return (comm>=MPI_COMM_FIRST_GRAPH && comm<MPI_COMM_FIRST_DIST_GRAPH);
2080   }
2081   ampiCommStruct &getGraph(MPI_Comm comm) const noexcept {
2082     int idx=comm-MPI_COMM_FIRST_GRAPH;
2083     if (idx>=graphComm.size()) CkAbort("AMPI> Bad graph communicator used!\n");
2084     return *graphComm[idx];
2085   }
2086   inline bool isDistGraph(MPI_Comm comm) const noexcept {
2087     return (comm >= MPI_COMM_FIRST_DIST_GRAPH && comm < MPI_COMM_FIRST_INTER);
2088   }
2089   ampiCommStruct &getDistGraph(MPI_Comm comm) const noexcept {
2090     int idx = comm-MPI_COMM_FIRST_DIST_GRAPH;
2091     if (idx>=distGraphComm.size()) CkAbort("Bad distributed graph communicator used");
2092     return *distGraphComm[idx];
2093   }
2094   inline bool isInter(MPI_Comm comm) const noexcept {
2095     return (comm>=MPI_COMM_FIRST_INTER && comm<MPI_COMM_FIRST_INTRA);
2096   }
2097   const ampiCommStruct &getInter(MPI_Comm comm) const noexcept {
2098     int idx=comm-MPI_COMM_FIRST_INTER;
2099     if (idx>=interComm.size()) CkAbort("AMPI> Bad inter-communicator used!\n");
2100     return *interComm[idx];
2101   }
2102   inline bool isIntra(MPI_Comm comm) const noexcept {
2103     return (comm>=MPI_COMM_FIRST_INTRA && comm<MPI_COMM_FIRST_RESVD);
2104   }
2105   const ampiCommStruct &getIntra(MPI_Comm comm) const noexcept {
2106     int idx=comm-MPI_COMM_FIRST_INTRA;
2107     if (idx>=intraComm.size()) CkAbort("Bad intra-communicator used");
2108     return *intraComm[idx];
2109   }
2110
2111   void pup(PUP::er &p) noexcept;
2112
2113   void startCheckpoint(const char* dname) noexcept;
2114   void Checkpoint(int len, const char* dname) noexcept;
2115   void ResumeThread() noexcept;
2116   TCharm* getTCharmThread() const noexcept {return thread;}
2117   inline ampiParent* blockOnRecv() noexcept;
2118   inline CkDDT* getDDT() noexcept { return &myDDT; }
2119
2120 #if CMK_LBDB_ON
2121   void setMigratable(bool mig) noexcept {
2122     thread->setMigratable(mig);
2123   }
2124 #endif
2125
2126   const ampiCommStruct &getWorldStruct() const noexcept;
2127
2128   inline const ampiCommStruct &comm2CommStruct(MPI_Comm comm) const noexcept {
2129     if (comm==MPI_COMM_WORLD) return getWorldStruct();
2130     if (comm==worldNo) return getWorldStruct();
2131     if (isSplit(comm)) return getSplit(comm);
2132     if (isGroup(comm)) return getGroup(comm);
2133     if (isCart(comm)) return getCart(comm);
2134     if (isGraph(comm)) return getGraph(comm);
2135     if (isDistGraph(comm)) return getDistGraph(comm);
2136     if (isInter(comm)) return getInter(comm);
2137     if (isIntra(comm)) return getIntra(comm);
2138     return universeComm2CommStruct(comm);
2139   }
2140
2141   inline vector<int>& getKeyvals(MPI_Comm comm) noexcept {
2142     ampiCommStruct &cs = *(ampiCommStruct *)&comm2CommStruct(comm);
2143     return cs.getKeyvals();
2144   }
2145
2146   inline ampi *comm2ampi(MPI_Comm comm) const noexcept {
2147     if (comm==MPI_COMM_WORLD) return worldPtr;
2148     if (comm==worldNo) return worldPtr;
2149     if (isSplit(comm)) {
2150       const ampiCommStruct &st=getSplit(comm);
2151       return st.getProxy()[thisIndex].ckLocal();
2152     }
2153     if (isGroup(comm)) {
2154       const ampiCommStruct &st=getGroup(comm);
2155       return st.getProxy()[thisIndex].ckLocal();
2156     }
2157     if (isCart(comm)) {
2158       const ampiCommStruct &st = getCart(comm);
2159       return st.getProxy()[thisIndex].ckLocal();
2160     }
2161     if (isGraph(comm)) {
2162       const ampiCommStruct &st = getGraph(comm);
2163       return st.getProxy()[thisIndex].ckLocal();
2164     }
2165     if (isDistGraph(comm)) {
2166       const ampiCommStruct &st = getDistGraph(comm);
2167       return st.getProxy()[thisIndex].ckLocal();
2168     }
2169     if (isInter(comm)) {
2170       const ampiCommStruct &st=getInter(comm);
2171       return st.getProxy()[thisIndex].ckLocal();
2172     }
2173     if (isIntra(comm)) {
2174       const ampiCommStruct &st=getIntra(comm);
2175       return st.getProxy()[thisIndex].ckLocal();
2176     }
2177     if (comm>MPI_COMM_WORLD) return worldPtr; //Use MPI_WORLD ampi for cross-world messages:
2178     CkAbort("Invalid communicator used!");
2179     return NULL;
2180   }
2181
2182   inline bool hasComm(const MPI_Group group) const noexcept {
2183     MPI_Comm comm = (MPI_Comm)group;
2184     return ( comm==MPI_COMM_WORLD || comm==worldNo || isSplit(comm) || isGroup(comm) ||
2185              isCart(comm) || isGraph(comm) || isDistGraph(comm) || isIntra(comm) );
2186     //isInter omitted because its comm number != its group number
2187   }
2188   inline vector<int> group2vec(MPI_Group group) const noexcept {
2189     if (group == MPI_GROUP_NULL || group == MPI_GROUP_EMPTY) {
2190       return vector<int>();
2191     }
2192     else if (hasComm(group)) {
2193       return comm2CommStruct((MPI_Comm)group).getIndices();
2194     }
2195     else {
2196       CkAssert(isInGroups(group));
2197       return groups[group]->getRanks();
2198     }
2199   }
2200   inline MPI_Group saveGroupStruct(const vector<int>& vec) noexcept {
2201     if (vec.empty()) return MPI_GROUP_EMPTY;
2202     int idx = groups.size();
2203     groups.resize(idx+1);
2204     groups[idx]=new groupStruct(vec);
2205     return (MPI_Group)idx;
2206   }
2207   inline int getRank(const MPI_Group group) const noexcept {
2208     vector<int> vec = group2vec(group);
2209     return getPosOp(thisIndex,vec);
2210   }
2211   inline AmpiRequestList &getReqs() noexcept { return ampiReqs; }
2212   inline int getMyPe() const noexcept {
2213     return CkMyPe();
2214   }
2215   inline bool hasWorld() const noexcept {
2216     return worldPtr!=NULL;
2217   }
2218
2219   inline void checkComm(MPI_Comm comm) const noexcept {
2220     if ((comm != MPI_COMM_SELF && comm != MPI_COMM_WORLD)
2221      || (isSplit(comm) && comm-MPI_COMM_FIRST_SPLIT >= splitComm.size())
2222      || (isGroup(comm) && comm-MPI_COMM_FIRST_GROUP >= groupComm.size())
2223      || (isCart(comm)  && comm-MPI_COMM_FIRST_CART  >=  cartComm.size())
2224      || (isGraph(comm) && comm-MPI_COMM_FIRST_GRAPH >= graphComm.size())
2225      || (isDistGraph(comm) && comm-MPI_COMM_FIRST_DIST_GRAPH >= distGraphComm.size())
2226      || (isInter(comm) && comm-MPI_COMM_FIRST_INTER >= interComm.size())
2227      || (isIntra(comm) && comm-MPI_COMM_FIRST_INTRA >= intraComm.size()) )
2228       CkAbort("Invalid MPI_Comm\n");
2229   }
2230
2231   /// if intra-communicator, return comm, otherwise return null group
2232   inline MPI_Group comm2group(const MPI_Comm comm) const noexcept {
2233     if(isInter(comm)) return MPI_GROUP_NULL;   // we don't support inter-communicator in such functions
2234     ampiCommStruct s = comm2CommStruct(comm);
2235     if(comm!=MPI_COMM_WORLD && comm!=s.getComm()) CkAbort("Error in ampiParent::comm2group()");
2236     return (MPI_Group)(s.getComm());
2237   }
2238
2239   inline int getRemoteSize(const MPI_Comm comm) const noexcept {
2240     if(isInter(comm)) return getInter(comm).getRemoteIndices().size();
2241     else return -1;
2242   }
2243   inline MPI_Group getRemoteGroup(const MPI_Comm comm) noexcept {
2244     if(isInter(comm)) return saveGroupStruct(getInter(comm).getRemoteIndices());
2245     else return MPI_GROUP_NULL;
2246   }
2247
2248   int createKeyval(MPI_Copy_function *copy_fn, MPI_Delete_function *delete_fn,
2249                   int *keyval, void* extra_state) noexcept;
2250   bool getBuiltinKeyval(int keyval, void *attribute_val) noexcept;
2251   int setUserKeyval(MPI_Comm comm, int keyval, void *attribute_val) noexcept;
2252   bool getUserKeyval(MPI_Comm comm, vector<int>& keyvals, int keyval, void *attribute_val, int *flag) noexcept;
2253   int dupUserKeyvals(MPI_Comm old_comm, MPI_Comm new_comm) noexcept;
2254   int freeUserKeyval(int context, vector<int>& keyvals, int *keyval) noexcept;
2255   int freeUserKeyvals(int context, vector<int>& keyvals) noexcept;
2256
2257   int setAttr(MPI_Comm comm, vector<int>& keyvals, int keyval, void *attribute_val) noexcept;
2258   int getAttr(MPI_Comm comm, vector<int>& keyvals, int keyval, void *attribute_val, int *flag) noexcept;
2259   int deleteAttr(MPI_Comm comm, vector<int>& keyvals, int keyval) noexcept;
2260
2261   int addWinStruct(WinStruct *win) noexcept;
2262   WinStruct *getWinStruct(MPI_Win win) const noexcept;
2263   void removeWinStruct(WinStruct *win) noexcept;
2264
2265   int createInfo(MPI_Info *newinfo) noexcept;
2266   int dupInfo(MPI_Info info, MPI_Info *newinfo) noexcept;
2267   int setInfo(MPI_Info info, const char *key, const char *value) noexcept;
2268   int deleteInfo(MPI_Info info, const char *key) noexcept;
2269   int getInfo(MPI_Info info, const char *key, int valuelen, char *value, int *flag) const noexcept;
2270   int getInfoValuelen(MPI_Info info, const char *key, int *valuelen, int *flag) const noexcept;
2271   int getInfoNkeys(MPI_Info info, int *nkeys) const noexcept;
2272   int getInfoNthkey(MPI_Info info, int n, char *key) const noexcept;
2273   int freeInfo(MPI_Info info) noexcept;
2274   void defineInfoEnv(int nRanks_) noexcept;
2275   void defineInfoMigration() noexcept;
2276
2277   // An 'MPI_Op' is an integer that indexes into either:
2278   //   A) an array of predefined ops owned by ampiNodeMgr, or
2279   //   B) a vector of user-defined ops owned by ampiParent
2280   // The MPI_Op is compared to AMPI_MAX_PREDEFINED_OP to disambiguate.
2281   inline int createOp(MPI_User_function *fn, bool isCommutative) noexcept {
2282     // Search thru non-predefined op's for any invalidated ones:
2283     for (int i=0; i<userOps.size(); i++) {
2284       if (userOps[i].isFree()) {
2285         userOps[i].init(fn, isCommutative);
2286         return AMPI_MAX_PREDEFINED_OP + 1 + i;
2287       }
2288     }
2289     // No invalid entries, so create a new one:
2290     userOps.emplace_back(fn, isCommutative);
2291     return AMPI_MAX_PREDEFINED_OP + userOps.size();
2292   }
2293   inline void freeOp(MPI_Op op) noexcept {
2294     // Don't free predefined op's:
2295     if (!opIsPredefined(op)) {
2296       // Invalidate op, then free all invalid op's from the back of the userOp's vector
2297       int opIdx = op - 1 - AMPI_MAX_PREDEFINED_OP;
2298       CkAssert(opIdx < userOps.size());
2299       userOps[opIdx].free();
2300       while (!userOps.empty() && userOps.back().isFree()) {
2301         userOps.pop_back();
2302       }
2303     }
2304   }
2305   inline bool opIsPredefined(MPI_Op op) const noexcept {
2306     return (op <= AMPI_MAX_PREDEFINED_OP);
2307   }
2308   inline bool opIsCommutative(MPI_Op op) const noexcept {
2309     if (opIsPredefined(op)) {
2310       return true; // all predefined ops are commutative
2311     }
2312     else {
2313       int opIdx = op - 1 - AMPI_MAX_PREDEFINED_OP;
2314       CkAssert(opIdx < userOps.size());
2315       return userOps[opIdx].isCommutative;
2316     }
2317   }
2318   inline MPI_User_function* op2User_function(MPI_Op op) const noexcept {
2319     if (opIsPredefined(op)) {
2320       return predefinedOps[op];
2321     }
2322     else {
2323       int opIdx = op - 1 - AMPI_MAX_PREDEFINED_OP;
2324       CkAssert(opIdx < userOps.size());
2325       return userOps[opIdx].func;
2326     }
2327   }
2328   inline AmpiOpHeader op2AmpiOpHeader(MPI_Op op, MPI_Datatype type, int count) const noexcept {
2329     if (opIsPredefined(op)) {
2330       int size = myDDT.getType(type)->getSize(count);
2331       return AmpiOpHeader(predefinedOps[op], type, count, size);
2332     }
2333     else {
2334       int opIdx = op - 1 - AMPI_MAX_PREDEFINED_OP;
2335       CkAssert(opIdx < userOps.size());
2336       int size = myDDT.getType(type)->getSize(count);
2337       return AmpiOpHeader(userOps[opIdx].func, type, count, size);
2338     }
2339   }
2340   inline void applyOp(MPI_Datatype datatype, MPI_Op op, int count, const void* invec, void* inoutvec) const noexcept {
2341     // inoutvec[i] = invec[i] op inoutvec[i]
2342     MPI_User_function *func = op2User_function(op);
2343     (func)((void*)invec, inoutvec, &count, &datatype);
2344   }
2345
2346   void init() noexcept;
2347   void finalize() noexcept;
2348   void block() noexcept;
2349   void yield() noexcept;
2350
2351 #if AMPI_PRINT_MSG_SIZES
2352 // Map of AMPI routine names to message sizes and number of messages:
2353 // ["AMPI_Routine"][ [msg_size][num_msgs] ]
2354   std::unordered_map<std::string, std::map<int, int> > msgSizes;
2355   inline bool isRankRecordingMsgSizes() noexcept;
2356   inline void recordMsgSize(const char* func, int msgSize) noexcept;
2357   void printMsgSizes() noexcept;
2358 #endif
2359
2360 #if AMPIMSGLOG
2361   /* message logging */
2362   int pupBytes;
2363 #if CMK_USE_ZLIB && 0
2364   gzFile fMsgLog;
2365   PUP::tozDisk *toPUPer;
2366   PUP::fromzDisk *fromPUPer;
2367 #else
2368   FILE* fMsgLog;
2369   PUP::toDisk *toPUPer;
2370   PUP::fromDisk *fromPUPer;
2371 #endif
2372 #endif
2373 };
2374
2375 // Store a generalized request class created by MPIX_Grequest_class_create
2376 class greq_class_desc {
2377 public:
2378   MPI_Grequest_query_function *query_fn;
2379   MPI_Grequest_free_function *free_fn;
2380   MPI_Grequest_cancel_function *cancel_fn;
2381   MPIX_Grequest_poll_function *poll_fn;
2382   MPIX_Grequest_wait_function *wait_fn;
2383
2384   void pup(PUP::er &p) noexcept {
2385     p((char *)query_fn, sizeof(void *));
2386     p((char *)free_fn, sizeof(void *));
2387     p((char *)cancel_fn, sizeof(void *));
2388     p((char *)poll_fn, sizeof(void *));
2389     p((char *)wait_fn, sizeof(void *));
2390   }
2391 };
2392
2393 /*
2394 An ampi manages the communication of one thread over
2395 one MPI communicator.
2396 */
2397 class ampi final : public CBase_ampi {
2398  private:
2399   friend class IReq; // for checking resumeOnRecv
2400   friend class SendReq;
2401   friend class SsendReq;
2402   friend class RednReq;
2403   friend class GatherReq;
2404   friend class GathervReq;
2405
2406   ampiParent *parent;
2407   CProxy_ampiParent parentProxy;
2408   TCharm *thread;
2409   int myRank;
2410   AmpiSeqQ oorder;
2411
2412  public:
2413   /*
2414    * AMPI Message Matching (Amm) queues are indexed by the tag and sender.
2415    * Since ampi objects are per-communicator, there are separate Amm's per communicator.
2416    */
2417   Amm<AmpiRequest *, AMPI_AMM_PT2PT_POOL_SIZE> postedReqs;
2418   Amm<AmpiMsg *, AMPI_AMM_PT2PT_POOL_SIZE> unexpectedMsgs;
2419
2420   // Bcast requests / msgs must be kept separate from pt2pt,
2421   // so we don't match them to wildcard recv's
2422   Amm<AmpiRequest *, AMPI_AMM_COLL_POOL_SIZE> postedBcastReqs;
2423   Amm<AmpiMsg *, AMPI_AMM_COLL_POOL_SIZE> unexpectedBcastMsgs;
2424
2425   // Store generalized request classes created by MPIX_Grequest_class_create
2426   vector<greq_class_desc> greq_classes;
2427
2428  private:
2429   ampiCommStruct myComm;
2430   vector<int> tmpVec; // stores temp group info
2431   CProxy_ampi remoteProxy; // valid only for intercommunicator
2432   CkPupPtrVec<win_obj> winObjects;
2433
2434  private:
2435   void inorder(AmpiMsg *msg) noexcept;
2436   void inorderBcast(AmpiMsg *msg, bool deleteMsg) noexcept;
2437   void inorderRdma(char* buf, int size, CMK_REFNUM_TYPE seq, int tag, int srcRank,
2438                    MPI_Comm comm, int ssendReq) noexcept;
2439
2440   void init() noexcept;
2441   void findParent(bool forMigration) noexcept;
2442
2443  public: // entry methods
2444   ampi() noexcept;
2445   ampi(CkArrayID parent_,const ampiCommStruct &s) noexcept;
2446   ampi(CkMigrateMessage *msg) noexcept;
2447   void ckJustMigrated() noexcept;
2448   void ckJustRestored() noexcept;
2449   ~ampi() noexcept;
2450
2451   void pup(PUP::er &p) noexcept;
2452
2453   void allInitDone() noexcept;
2454   void setInitDoneFlag() noexcept;
2455
2456   void unblock() noexcept;
2457   void injectMsg(int size, char* buf) noexcept;
2458   void generic(AmpiMsg *) noexcept;
2459   void genericRdma(char* buf, int size, CMK_REFNUM_TYPE seq, int tag, int srcRank,
2460                    MPI_Comm destcomm, int ssendReq) noexcept;
2461   void completedRdmaSend(CkDataMsg *msg) noexcept;
2462   void ssend_ack(int sreq) noexcept;
2463   void barrierResult() noexcept;
2464   void ibarrierResult() noexcept;
2465   void bcastResult(AmpiMsg *msg) noexcept;
2466   void rednResult(CkReductionMsg *msg) noexcept;
2467   void irednResult(CkReductionMsg *msg) noexcept;
2468
2469   void splitPhase1(CkReductionMsg *msg) noexcept;
2470   void splitPhaseInter(CkReductionMsg *msg) noexcept;
2471   void commCreatePhase1(MPI_Comm nextGroupComm) noexcept;
2472   void intercommCreatePhase1(MPI_Comm nextInterComm) noexcept;
2473   void intercommMergePhase1(MPI_Comm nextIntraComm) noexcept;
2474
2475  private: // Used by the above entry methods that create new MPI_Comm objects
2476   CProxy_ampi createNewChildAmpiSync() noexcept;
2477   void insertNewChildAmpiElements(MPI_Comm newComm, CProxy_ampi newAmpi) noexcept;
2478
2479   inline void handleBlockedReq(AmpiRequest* req) noexcept {
2480     if (req->isBlocked() && parent->numBlockedReqs != 0) {
2481       parent->numBlockedReqs--;
2482     }
2483   }
2484   inline void resumeThreadIfReady() noexcept {
2485     if (parent->resumeOnRecv && parent->numBlockedReqs == 0) {
2486       thread->resume();
2487     }
2488   }
2489
2490  public: // to be used by MPI_* functions
2491   inline const ampiCommStruct &comm2CommStruct(MPI_Comm comm) const noexcept {
2492     return parent->comm2CommStruct(comm);
2493   }
2494   inline const ampiCommStruct &getCommStruct() const noexcept { return myComm; }
2495
2496   inline ampi* blockOnRecv() noexcept;
2497   inline ampi* blockOnColl() noexcept;
2498   inline void setBlockingReq(AmpiRequest *req) noexcept;
2499   MPI_Request postReq(AmpiRequest* newreq) noexcept;
2500
2501   inline CMK_REFNUM_TYPE getSeqNo(int destRank, MPI_Comm destcomm, int tag) noexcept;
2502   AmpiMsg *makeBcastMsg(const void *buf,int count,MPI_Datatype type,int root,MPI_Comm destcomm) noexcept;
2503   AmpiMsg *makeAmpiMsg(int destRank,int t,int sRank,const void *buf,int count,
2504                        MPI_Datatype type,MPI_Comm destcomm, int ssendReq=0) noexcept;
2505
2506   MPI_Request send(int t, int s, const void* buf, int count, MPI_Datatype type, int rank,
2507                    MPI_Comm destcomm, int ssendReq=0, AmpiSendType sendType=BLOCKING_SEND) noexcept;
2508   static void sendraw(int t, int s, void* buf, int len, CkArrayID aid, int idx) noexcept;
2509   inline MPI_Request sendLocalMsg(int t, int sRank, const void* buf, int size, MPI_Datatype type, int destRank,
2510                                   MPI_Comm destcomm, ampi* destPtr, int ssendReq, AmpiSendType sendType) noexcept;
2511   inline MPI_Request sendRdmaMsg(int t, int sRank, const void* buf, int size, MPI_Datatype type, int destIdx,
2512                                  int destRank, MPI_Comm destcomm, CProxy_ampi arrProxy, int ssendReq) noexcept;
2513   inline bool destLikelyWithinProcess(CProxy_ampi arrProxy, int destIdx) const noexcept {
2514     CkArray* localBranch = arrProxy.ckLocalBranch();
2515     int destPe = localBranch->lastKnown(CkArrayIndex1D(destIdx));
2516     return (CkNodeOf(destPe) == CkMyNode());
2517   }
2518   MPI_Request delesend(int t, int s, const void* buf, int count, MPI_Datatype type, int rank,
2519                        MPI_Comm destcomm, CProxy_ampi arrproxy, int ssend, AmpiSendType sendType) noexcept;
2520   inline void processAmpiMsg(AmpiMsg *msg, void* buf, MPI_Datatype type, int count) noexcept;
2521   inline void processRdmaMsg(const void *sbuf, int slength, int ssendReq, int srank, void* rbuf,
2522                              int rcount, MPI_Datatype rtype, MPI_Comm comm) noexcept;
2523   inline void processRednMsg(CkReductionMsg *msg, void* buf, MPI_Datatype type, int count) noexcept;
2524   inline void processNoncommutativeRednMsg(CkReductionMsg *msg, void* buf, MPI_Datatype type, int count,
2525                                            MPI_User_function* func) noexcept;
2526   inline void processGatherMsg(CkReductionMsg *msg, void* buf, MPI_Datatype type, int recvCount) noexcept;
2527   inline void processGathervMsg(CkReductionMsg *msg, void* buf, MPI_Datatype type,
2528                                int* recvCounts, int* displs) noexcept;
2529   inline AmpiMsg * getMessage(int t, int s, MPI_Comm comm, int *sts) const noexcept;
2530   int recv(int t,int s,void* buf,int count,MPI_Datatype type,MPI_Comm comm,MPI_Status *sts=NULL) noexcept;
2531   void irecv(void *buf, int count, MPI_Datatype type, int src,
2532              int tag, MPI_Comm comm, MPI_Request *request) noexcept;
2533   void mrecv(int tag, int src, void* buf, int count, MPI_Datatype datatype, MPI_Comm comm,
2534              MPI_Status* status, MPI_Message* message) noexcept;
2535   void imrecv(void* buf, int count, MPI_Datatype datatype, int src, int tag, MPI_Comm comm,
2536               MPI_Request* request, MPI_Message* message) noexcept;
2537   void irecvBcast(void *buf, int count, MPI_Datatype type, int src,
2538                   MPI_Comm comm, MPI_Request *request) noexcept;
2539   void sendrecv(const void *sbuf, int scount, MPI_Datatype stype, int dest, int stag,
2540                 void *rbuf, int rcount, MPI_Datatype rtype, int src, int rtag,
2541                 MPI_Comm comm, MPI_Status *sts) noexcept;
2542   void sendrecv_replace(void* buf, int count, MPI_Datatype datatype,
2543                         int dest, int sendtag, int source, int recvtag,
2544                         MPI_Comm comm, MPI_Status *status) noexcept;
2545   void probe(int t,int s,MPI_Comm comm,MPI_Status *sts) noexcept;
2546   void mprobe(int t, int s, MPI_Comm comm, MPI_Status *sts, MPI_Message *message) noexcept;
2547   int iprobe(int t,int s,MPI_Comm comm,MPI_Status *sts) noexcept;
2548   int improbe(int t, int s, MPI_Comm comm, MPI_Status *sts, MPI_Message *message) noexcept;
2549   void barrier() noexcept;
2550   void ibarrier(MPI_Request *request) noexcept;
2551   void bcast(int root, void* buf, int count, MPI_Datatype type, MPI_Comm comm) noexcept;
2552   int intercomm_bcast(int root, void* buf, int count, MPI_Datatype type, MPI_Comm intercomm) noexcept;
2553   void ibcast(int root, void* buf, int count, MPI_Datatype type, MPI_Comm comm, MPI_Request* request) noexcept;
2554   int intercomm_ibcast(int root, void* buf, int count, MPI_Datatype type, MPI_Comm intercomm, MPI_Request *request) noexcept;
2555   static void bcastraw(void* buf, int len, CkArrayID aid) noexcept;
2556   void split(int color,int key,MPI_Comm *dest, int type) noexcept;
2557   void commCreate(const vector<int>& vec,MPI_Comm *newcomm) noexcept;
2558   MPI_Comm cartCreate0D() noexcept;
2559   MPI_Comm cartCreate(vector<int>& vec, int ndims, const int* dims) noexcept;
2560   void graphCreate(const vector<int>& vec, MPI_Comm *newcomm) noexcept;
2561   void distGraphCreate(const vector<int>& vec, MPI_Comm *newcomm) noexcept;
2562   void intercommCreate(const vector<int>& rvec, int root, MPI_Comm tcomm, MPI_Comm *ncomm) noexcept;
2563
2564   inline bool isInter() const noexcept { return myComm.isinter(); }
2565   void intercommMerge(int first, MPI_Comm *ncomm) noexcept;
2566
2567   inline int getWorldRank() const noexcept {return parent->thisIndex;}
2568   /// Return our rank in this communicator
2569   inline int getRank() const noexcept {return myRank;}
2570   inline int getSize() const noexcept {return myComm.getSize();}
2571   inline MPI_Comm getComm() const noexcept {return myComm.getComm();}
2572   inline void setCommName(const char *name) noexcept {myComm.setName(name);}
2573   inline void getCommName(char *name, int *len) const noexcept {myComm.getName(name,len);}
2574   inline vector<int> getIndices() const noexcept { return myComm.getIndices(); }
2575   inline vector<int> getRemoteIndices() const noexcept { return myComm.getRemoteIndices(); }
2576   inline const CProxy_ampi &getProxy() const noexcept {return thisProxy;}
2577   inline const CProxy_ampi &getRemoteProxy() const noexcept {return remoteProxy;}
2578   inline void setRemoteProxy(CProxy_ampi rproxy) noexcept { remoteProxy = rproxy; thread->resume(); }
2579   inline int getIndexForRank(int r) const noexcept {return myComm.getIndexForRank(r);}
2580   inline int getIndexForRemoteRank(int r) const noexcept {return myComm.getIndexForRemoteRank(r);}
2581   void findNeighbors(MPI_Comm comm, int rank, vector<int>& neighbors) const noexcept;
2582   inline const vector<int>& getNeighbors() const noexcept { return myComm.getTopologyforNeighbors()->getnbors(); }
2583   inline bool opIsCommutative(MPI_Op op) const noexcept { return parent->opIsCommutative(op); }
2584   inline MPI_User_function* op2User_function(MPI_Op op) const noexcept { return parent->op2User_function(op); }
2585   void topoDup(int topoType, int rank, MPI_Comm comm, MPI_Comm *newcomm) noexcept;
2586
2587   inline AmpiRequestList& getReqs() noexcept { return parent->ampiReqs; }
2588   CkDDT *getDDT() noexcept {return &parent->myDDT;}
2589   CthThread getThread() const noexcept { return thread->getThread(); }
2590
2591  public:
2592   MPI_Win createWinInstance(void *base, MPI_Aint size, int disp_unit, MPI_Info info) noexcept;
2593   int deleteWinInstance(MPI_Win win) noexcept;
2594   int winGetGroup(WinStruct *win, MPI_Group *group) const noexcept;
2595   int winPut(const void *orgaddr, int orgcnt, MPI_Datatype orgtype, int rank,
2596              MPI_Aint targdisp, int targcnt, MPI_Datatype targtype, WinStruct *win) noexcept;
2597   int winGet(void *orgaddr, int orgcnt, MPI_Datatype orgtype, int rank,
2598              MPI_Aint targdisp, int targcnt, MPI_Datatype targtype, WinStruct *win) noexcept;
2599   int winIget(MPI_Aint orgdisp, int orgcnt, MPI_Datatype orgtype, int rank,
2600               MPI_Aint targdisp, int targcnt, MPI_Datatype targtype, WinStruct *win,
2601               MPI_Request *req) noexcept;
2602   int winIgetWait(MPI_Request *request, MPI_Status *status) noexcept;
2603   int winIgetFree(MPI_Request *request, MPI_Status *status) noexcept;
2604   void winRemotePut(int orgtotalsize, char* orgaddr, int orgcnt, MPI_Datatype orgtype,
2605                     MPI_Aint targdisp, int targcnt, MPI_Datatype targtype, int winIndex) noexcept;
2606   char* winLocalGet(int orgcnt, MPI_Datatype orgtype, MPI_Aint targdisp, int targcnt,
2607                     MPI_Datatype targtype, int winIndex) noexcept;
2608   AmpiMsg* winRemoteGet(int orgcnt, MPI_Datatype orgtype, MPI_Aint targdisp,
2609                     int targcnt, MPI_Datatype targtype, int winIndex) noexcept;
2610   AmpiMsg* winRemoteIget(MPI_Aint orgdisp, int orgcnt, MPI_Datatype orgtype, MPI_Aint targdisp,
2611                          int targcnt, MPI_Datatype targtype, int winIndex) noexcept;
2612   int winLock(int lock_type, int rank, WinStruct *win) noexcept;
2613   int winUnlock(int rank, WinStruct *win) noexcept;
2614   void winRemoteLock(int lock_type, int winIndex, int requestRank) noexcept;
2615   void winRemoteUnlock(int winIndex, int requestRank) noexcept;
2616   int winAccumulate(const void *orgaddr, int orgcnt, MPI_Datatype orgtype, int rank,
2617                     MPI_Aint targdisp, int targcnt, MPI_Datatype targtype,
2618                     MPI_Op op, WinStruct *win) noexcept;
2619   void winRemoteAccumulate(int orgtotalsize, char* orgaddr, int orgcnt, MPI_Datatype orgtype,
2620                            MPI_Aint targdisp, int targcnt, MPI_Datatype targtype,
2621                            MPI_Op op, int winIndex) noexcept;
2622   int winGetAccumulate(const void *orgaddr, int orgcnt, MPI_Datatype orgtype, void *resaddr,
2623                        int rescnt, MPI_Datatype restype, int rank, MPI_Aint targdisp,
2624                        int targcnt, MPI_Datatype targtype, MPI_Op op, WinStruct *win) noexcept;
2625   void winLocalGetAccumulate(int orgtotalsize, char* sorgaddr, int orgcnt, MPI_Datatype orgtype,
2626                              MPI_Aint targdisp, int targcnt, MPI_Datatype targtype, MPI_Op op,
2627                              char *resaddr, int winIndex) noexcept;
2628   AmpiMsg* winRemoteGetAccumulate(int orgtotalsize, char* sorgaddr, int orgcnt, MPI_Datatype orgtype,
2629                                   MPI_Aint targdisp, int targcnt, MPI_Datatype targtype, MPI_Op op,
2630                                   int winIndex) noexcept;
2631   int winCompareAndSwap(const void *orgaddr, const void *compaddr, void *resaddr, MPI_Datatype type,
2632                         int rank, MPI_Aint targdisp, WinStruct *win) noexcept;
2633   char* winLocalCompareAndSwap(int size, char* sorgaddr, char* compaddr, MPI_Datatype type,
2634                                MPI_Aint targdisp, int winIndex) noexcept;
2635   AmpiMsg* winRemoteCompareAndSwap(int size, char *sorgaddr, char *compaddr, MPI_Datatype type,
2636                                    MPI_Aint targdisp, int winIndex) noexcept;
2637   void winSetName(WinStruct *win, const char *name) noexcept;
2638   void winGetName(WinStruct *win, char *name, int *length) const noexcept;
2639   win_obj* getWinObjInstance(WinStruct *win) const noexcept;
2640   int getNewSemaId() noexcept;
2641
2642   int intercomm_scatter(int root, const void *sendbuf, int sendcount, MPI_Datatype sendtype,
2643                         void *recvbuf, int recvcount, MPI_Datatype recvtype, MPI_Comm intercomm) noexcept;
2644   int intercomm_iscatter(int root, const void *sendbuf, int sendcount, MPI_Datatype sendtype,
2645                          void *recvbuf, int recvcount, MPI_Datatype recvtype,
2646                          MPI_Comm intercomm, MPI_Request *request) noexcept;
2647   int intercomm_scatterv(int root, const void* sendbuf, const int* sendcounts, const int* displs,
2648                          MPI_Datatype sendtype, void* recvbuf, int recvcount,
2649                          MPI_Datatype recvtype, MPI_Comm intercomm) noexcept;
2650   int intercomm_iscatterv(int root, const void* sendbuf, const int* sendcounts, const int* displs,
2651                           MPI_Datatype sendtype, void* recvbuf, int recvcount,
2652                           MPI_Datatype recvtype, MPI_Comm intercomm, MPI_Request* request) noexcept;
2653 };
2654
2655 ampiParent *getAmpiParent() noexcept;
2656 bool isAmpiThread() noexcept;
2657 ampi *getAmpiInstance(MPI_Comm comm) noexcept;
2658 void checkComm(MPI_Comm comm) noexcept;
2659 void checkRequest(MPI_Request req) noexcept;
2660 void handle_MPI_BOTTOM(void* &buf, MPI_Datatype type) noexcept;
2661 void handle_MPI_BOTTOM(void* &buf1, MPI_Datatype type1, void* &buf2, MPI_Datatype type2) noexcept;
2662
2663 #if AMPI_ERROR_CHECKING
2664 int ampiErrhandler(const char* func, int errcode) noexcept;
2665 #else
2666 #define ampiErrhandler(func, errcode) (errcode)
2667 #endif
2668
2669
2670 #if CMK_TRACE_ENABLED
2671
2672 // List of AMPI functions to trace:
2673 static const char *funclist[] = {"AMPI_Abort", "AMPI_Add_error_class", "AMPI_Add_error_code", "AMPI_Add_error_string",
2674 "AMPI_Address", "AMPI_Allgather", "AMPI_Allgatherv", "AMPI_Allreduce", "AMPI_Alltoall",
2675 "AMPI_Alltoallv", "AMPI_Alltoallw", "AMPI_Attr_delete", "AMPI_Attr_get",
2676 "AMPI_Attr_put", "AMPI_Barrier", "AMPI_Bcast", "AMPI_Bsend", "AMPI_Cancel",
2677 "AMPI_Cart_coords", "AMPI_Cart_create", "AMPI_Cart_get", "AMPI_Cart_map",
2678 "AMPI_Cart_rank", "AMPI_Cart_shift", "AMPI_Cart_sub", "AMPI_Cartdim_get",
2679 "AMPI_Comm_call_errhandler", "AMPI_Comm_compare", "AMPI_Comm_create", "AMPI_Comm_create_group",
2680 "AMPI_Comm_create_errhandler", "AMPI_Comm_create_keyval", "AMPI_Comm_delete_attr",
2681 "AMPI_Comm_dup", "AMPI_Comm_dup_with_info", "AMPI_Comm_free",
2682 "AMPI_Comm_free_errhandler", "AMPI_Comm_free_keyval", "AMPI_Comm_get_attr",
2683 "AMPI_Comm_get_errhandler", "AMPI_Comm_get_info", "AMPI_Comm_get_name",
2684 "AMPI_Comm_group", "AMPI_Comm_rank", "AMPI_Comm_remote_group", "AMPI_Comm_remote_size",
2685 "AMPI_Comm_set_attr", "AMPI_Comm_set_errhandler", "AMPI_Comm_set_info", "AMPI_Comm_set_name",
2686 "AMPI_Comm_size", "AMPI_Comm_split", "AMPI_Comm_split_type", "AMPI_Comm_test_inter",
2687 "AMPI_Dims_create", "AMPI_Dist_graph_create", "AMPI_Dist_graph_create_adjacent",
2688 "AMPI_Dist_graph_neighbors", "AMPI_Dist_graph_neighbors_count",
2689 "AMPI_Errhandler_create", "AMPI_Errhandler_free", "AMPI_Errhandler_get",
2690 "AMPI_Errhandler_set", "AMPI_Error_class", "AMPI_Error_string", "AMPI_Exscan", "AMPI_Finalize",
2691 "AMPI_Finalized", "AMPI_Gather", "AMPI_Gatherv", "AMPI_Get_address", "AMPI_Get_count",
2692 "AMPI_Get_elements", "AMPI_Get_library_version", "AMPI_Get_processor_name", "AMPI_Get_version",
2693 "AMPI_Graph_create", "AMPI_Graph_get", "AMPI_Graph_map", "AMPI_Graph_neighbors",
2694 "AMPI_Graph_neighbors_count", "AMPI_Graphdims_get", "AMPI_Group_compare", "AMPI_Group_difference",
2695 "AMPI_Group_excl", "AMPI_Group_free", "AMPI_Group_incl", "AMPI_Group_intersection",
2696 "AMPI_Group_range_excl", "AMPI_Group_range_incl", "AMPI_Group_rank", "AMPI_Group_size",
2697 "AMPI_Group_translate_ranks", "AMPI_Group_union", "AMPI_Iallgather", "AMPI_Iallgatherv",
2698 "AMPI_Iallreduce", "AMPI_Ialltoall", "AMPI_Ialltoallv", "AMPI_Ialltoallw", "AMPI_Ibarrier",
2699 "AMPI_Ibcast", "AMPI_Iexscan", "AMPI_Igather", "AMPI_Igatherv", "AMPI_Ineighbor_allgather",
2700 "AMPI_Ineighbor_allgatherv", "AMPI_Ineighbor_alltoall", "AMPI_Ineighbor_alltoallv",
2701 "AMPI_Ineighbor_alltoallw", "AMPI_Init", "AMPI_Init_thread", "AMPI_Initialized", "AMPI_Intercomm_create",
2702 "AMPI_Intercomm_merge", "AMPI_Iprobe", "AMPI_Irecv", "AMPI_Ireduce", "AMPI_Ireduce_scatter",
2703 "AMPI_Ireduce_scatter_block", "AMPI_Is_thread_main", "AMPI_Iscan", "AMPI_Iscatter", "AMPI_Iscatterv",
2704 "AMPI_Isend", "AMPI_Issend", "AMPI_Keyval_create", "AMPI_Keyval_free", "AMPI_Neighbor_allgather",
2705 "AMPI_Neighbor_allgatherv", "AMPI_Neighbor_alltoall", "AMPI_Neighbor_alltoallv", "AMPI_Neighbor_alltoallw",
2706 "AMPI_Op_commutative", "AMPI_Op_create", "AMPI_Op_free", "AMPI_Pack", "AMPI_Pack_size",
2707 "AMPI_Pcontrol", "AMPI_Probe", "AMPI_Query_thread", "AMPI_Recv", "AMPI_Recv_init", "AMPI_Reduce",
2708 "AMPI_Reduce_local", "AMPI_Reduce_scatter", "AMPI_Reduce_scatter_block", "AMPI_Request_free",
2709 "AMPI_Request_get_status", "AMPI_Rsend", "AMPI_Scan", "AMPI_Scatter", "AMPI_Scatterv", "AMPI_Send",
2710 "AMPI_Send_init",  "AMPI_Sendrecv", "AMPI_Sendrecv_replace", "AMPI_Ssend", "AMPI_Ssend_init",
2711 "AMPI_Start", "AMPI_Startall", "AMPI_Status_set_cancelled", "AMPI_Status_set_elements", "AMPI_Test",
2712 "AMPI_Test_cancelled", "AMPI_Testall", "AMPI_Testany", "AMPI_Testsome", "AMPI_Topo_test",
2713 "AMPI_Type_commit", "AMPI_Type_contiguous", "AMPI_Type_create_hindexed",
2714 "AMPI_Type_create_hindexed_block", "AMPI_Type_create_hvector", "AMPI_Type_create_indexed_block",
2715 "AMPI_Type_create_keyval", "AMPI_Type_create_resized", "AMPI_Type_create_struct",
2716 "AMPI_Type_delete_attr", "AMPI_Type_dup", "AMPI_Type_extent", "AMPI_Type_free",
2717 "AMPI_Type_free_keyval", "AMPI_Type_get_attr", "AMPI_Type_get_contents", "AMPI_Type_get_envelope",
2718 "AMPI_Type_get_extent", "AMPI_Type_get_name", "AMPI_Type_get_true_extent", "AMPI_Type_hindexed",
2719 "AMPI_Type_hvector", "AMPI_Type_indexed", "AMPI_Type_lb", "AMPI_Type_set_attr",
2720 "AMPI_Type_set_name", "AMPI_Type_size", "AMPI_Type_struct", "AMPI_Type_ub", "AMPI_Type_vector",
2721 "AMPI_Type_create_darray", "AMPI_Type_create_subarray",
2722 "AMPI_Unpack", "AMPI_Wait", "AMPI_Waitall", "AMPI_Waitany", "AMPI_Waitsome", "AMPI_Wtick", "AMPI_Wtime",
2723 "AMPI_Accumulate", "AMPI_Compare_and_swap", "AMPI_Fetch_and_op", "AMPI_Get", "AMPI_Get_accumulate",
2724 "AMPI_Info_create", "AMPI_Info_delete", "AMPI_Info_dup", "AMPI_Info_free", "AMPI_Info_get",
2725 "AMPI_Info_get_nkeys", "AMPI_Info_get_nthkey", "AMPI_Info_get_valuelen",
2726 "AMPI_Info_set", "AMPI_Put", "AMPI_Raccumulate", "AMPI_Rget", "AMPI_Rget_accumulate",
2727 "AMPI_Rput", "AMPI_Win_complete", "AMPI_Win_create", "AMPI_Win_create_errhandler",
2728 "AMPI_Win_create_keyval", "AMPI_Win_delete_attr", "AMPI_Win_fence", "AMPI_Win_free",
2729 "AMPI_Win_free_keyval", "AMPI_Win_get_attr", "AMPI_Win_get_errhandler",
2730 "AMPI_Win_get_group", "AMPI_Win_get_info", "AMPI_Win_get_name", "AMPI_Win_lock",
2731 "AMPI_Win_post", "AMPI_Win_set_attr", "AMPI_Win_set_errhandler", "AMPI_Win_set_info",
2732 "AMPI_Win_set_name", "AMPI_Win_start", "AMPI_Win_test", "AMPI_Win_unlock",
2733 "AMPI_Win_wait", "AMPI_Exit" /*AMPI extensions:*/, "AMPI_Migrate",
2734 "AMPI_Load_start_measure", "AMPI_Load_stop_measure",
2735 "AMPI_Load_set_value", "AMPI_Migrate_to_pe", "AMPI_Set_migratable",
2736 "AMPI_Register_pup", "AMPI_Get_pup_data", "AMPI_Register_main",
2737 "AMPI_Register_about_to_migrate", "AMPI_Register_just_migrated",
2738 "AMPI_Iget", "AMPI_Iget_wait", "AMPI_Iget_free", "AMPI_Iget_data",
2739 "AMPI_Type_is_contiguous", "AMPI_Yield", "AMPI_Suspend",
2740 "AMPI_Resume", "AMPI_Print", "AMPI_Alltoall_medium",
2741 "AMPI_Alltoall_long", "AMPI_System"};
2742
2743 // not traced: AMPI_Trace_begin, AMPI_Trace_end
2744
2745 #endif // CMK_TRACE_ENABLED
2746
2747 //Use this to mark the start of AMPI interface routines that can only be called on AMPI threads:
2748 #if CMK_ERROR_CHECKING
2749 #define AMPI_API(routineName) \
2750   if (!isAmpiThread()) { CkAbort("AMPI> cannot call MPI routines from non-AMPI threads!"); } \
2751   TCHARM_API_TRACE(routineName, "ampi");
2752 #else
2753 #define AMPI_API(routineName) TCHARM_API_TRACE(routineName, "ampi")
2754 #endif
2755
2756 //Use this for MPI_Init and routines than can be called before AMPI threads have been initialized:
2757 #define AMPI_API_INIT(routineName) TCHARM_API_TRACE(routineName, "ampi")
2758
2759 #endif // _AMPIIMPL_H