/*
 * Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana
 *                         University Research and Technology
 *                         Corporation.  All rights reserved.
 * Copyright (c) 2004-2005 The University of Tennessee and The University
 *                         of Tennessee Research Foundation.  All rights
 *                         reserved.
 * Copyright (c) 2004-2005 High Performance Computing Center Stuttgart, 
 *                         University of Stuttgart.  All rights reserved.
 * Copyright (c) 2004-2005 The Regents of the University of California.
 *                         All rights reserved.
 * $COPYRIGHT$
 * 
 * Additional copyrights may follow
 * 
 * $HEADER$
 */

#ifndef OMPI_F77_CONSTANTS_H
#define OMPI_F77_CONSTANTS_H


/*
 * Several variables are used to link against MPI F77 constants which
 * correspond to addresses, e.g. MPI_BOTTOM, and are implemented via
 * common blocks.  They must have the same size and alignment
 * constraints as the corresponding F77 common blocks.
 *
 * We use common blocks so that in the C wrapper functions, we can
 * compare the address that comes in against known addresses (e.g., if
 * the "status" argument in MPI_RECV is the address of the common
 * block for the fortran equivalent of MPI_STATUS_IGNORE, then we know
 * to pass the C MPI_STATUS_IGNORE to the C MPI_Recv function.
 *
 * This mojo makes a type that will be aligned on 16 bytes (same as
 * common blocks -- at least it seems to work with all the fortran
 * compilers that we care about... haven't found one yet that doesn't
 * work...)
 */

#if defined(HAVE_LONG_DOUBLE) && OMPI_ALIGNMENT_LONG_DOUBLE == 16
typedef struct { long double bogus[1]; } ompi_fortran_common_t;
#else
typedef struct { double bogus[2]; } ompi_fortran_common_t;
#endif

/*
 * This part sucks.  :-(
 *
 * Since we made the fundamental decision to support all 4 common
 * fortran compiler symbol conventions within the same library for
 * those compilers who support weak symbols, we need to have 4 symbols
 * for each of the fortran address constants.  As described above, we
 * have to have known *pointer* values for the fortran addresses
 * (e.g., MPI_STATUS_IGNORE).  So when the fortran wrapper for
 * MPI_RECV gets (MPI_Fint *status), it can check (status ==
 * some_sentinel_value) to know that it got the Fortran equivalent of
 * MPI_STATUS_IGNORE and therefore pass the C MPI_STATUS_IGNORE to the
 * C MPI_Recv.
 *
 * We do this by having a "common" block in mpif.h:
 *
 * DOUBLE PRECISION MPI_STATUS_IGNORE
 * common /mpi_fortran_status_ignore/ MPI_STATUS_IGNORE
 *
 * This makes the fortran variable MPI_STATUS_IGNORE effectively be an
 * alias for the C variable "mpi_fortran_status_ignore" -- but the C
 * symbol name is according to the fortran compiler's naming symbol
 * convention bais.  So it could be MPI_FORTRAN_STATUS_IGNORE,
 * mpi_fortran_status_ignore, mpi_fortran_status_ignore_, or
 * mpi_fortran_status_ignore__.
 *
 * Hence, we have to have *4* C symbols for this, and them compare for
 * all of them in the fortran MPI_RECV wrapper.  :-( I can't think of
 * any better way to do this.
 *
 * I'm putting these 4 comparisons in macros (on systems where we
 * don't support the 4 symbols -- e.g., OSX, where we don't have weak
 * symbols -- it'll only be one comparison), so if anyone things of
 * something better than this, you should only need to modify this
 * file.
 */

#define DECL(upper_case, lower_case, single_u, double_u) \
OMPI_DECLSPEC extern ompi_fortran_common_t upper_case; \
OMPI_DECLSPEC extern ompi_fortran_common_t lower_case; \
OMPI_DECLSPEC extern ompi_fortran_common_t single_u; \
OMPI_DECLSPEC extern ompi_fortran_common_t double_u

DECL(MPI_FORTRAN_BOTTOM, mpi_fortran_bottom,
     mpi_fortran_bottom_, mpi_fortran_bottom__);
DECL(MPI_FORTRAN_IN_PLACE, mpi_fortran_in_place,
     mpi_fortran_in_place_, mpi_fortran_in_place__);
DECL(MPI_FORTRAN_ARGV_NULL, mpi_fortran_argv_null,
     mpi_fortran_argv_null_, mpi_fortran_argv_null__);
DECL(MPI_FORTRAN_ARGVS_NULL, mpi_fortran_argvs_null,
     mpi_fortran_argvs_null_, mpi_fortran_argvs_null__);
DECL(MPI_FORTRAN_ERRCODES_IGNORE, mpi_fortran_errcodes_ignore,
     mpi_fortran_errcodes_ignore_, mpi_fortran_errcodes_ignore__);
DECL(MPI_FORTRAN_STATUS_IGNORE, mpi_fortran_status_ignore,
     mpi_fortran_status_ignore_, mpi_fortran_status_ignore__);
DECL(MPI_FORTRAN_STATUSES_IGNORE, mpi_fortran_statuses_ignore,
     mpi_fortran_statuses_ignore_, mpi_fortran_statuses_ignore__);

/*
 * Create macros to do the checking.  Only check for all 4 if we have
 * weak symbols.  Otherwise, just check for the one relevant symbol.
 */
#if OMPI_HAVE_WEAK_SYMBOLS
#define OMPI_IS_FORTRAN_BOTTOM(addr) \
  (addr == (void*) &MPI_FORTRAN_BOTTOM || \
   addr == (void*) &mpi_fortran_bottom || \
   addr == (void*) &mpi_fortran_bottom_ || \
   addr == (void*) &mpi_fortran_bottom__)
#define OMPI_IS_FORTRAN_IN_PLACE(addr) \
  (addr == (void*) &MPI_FORTRAN_IN_PLACE || \
   addr == (void*) &mpi_fortran_in_place || \
   addr == (void*) &mpi_fortran_in_place_ || \
   addr == (void*) &mpi_fortran_in_place__)
#define OMPI_IS_FORTRAN_ARGV_NULL(addr) \
  (addr == (void*) &MPI_FORTRAN_ARGV_NULL || \
   addr == (void*) &mpi_fortran_argv_null || \
   addr == (void*) &mpi_fortran_argv_null_ || \
   addr == (void*) &mpi_fortran_argv_null__)
#define OMPI_IS_FORTRAN_ARGVS_NULL(addr) \
  (addr == (void*) &MPI_FORTRAN_ARGVS_NULL || \
   addr == (void*) &mpi_fortran_argvs_null || \
   addr == (void*) &mpi_fortran_argvs_null_ || \
   addr == (void*) &mpi_fortran_argvs_null__)
#define OMPI_IS_FORTRAN_ERRCODES_IGNORE(addr) \
  (addr == (void*) &MPI_FORTRAN_ERRCODES_IGNORE || \
   addr == (void*) &mpi_fortran_errcodes_ignore || \
   addr == (void*) &mpi_fortran_errcodes_ignore_ || \
   addr == (void*) &mpi_fortran_errcodes_ignore__)
#define OMPI_IS_FORTRAN_STATUS_IGNORE(addr) \
  (addr == (void*) &MPI_FORTRAN_STATUS_IGNORE || \
   addr == (void*) &mpi_fortran_status_ignore || \
   addr == (void*) &mpi_fortran_status_ignore_ || \
   addr == (void*) &mpi_fortran_status_ignore__)
#define OMPI_IS_FORTRAN_STATUSES_IGNORE(addr) \
  (addr == (void*) &MPI_FORTRAN_STATUSES_IGNORE || \
   addr == (void*) &mpi_fortran_statuses_ignore || \
   addr == (void*) &mpi_fortran_statuses_ignore_ || \
   addr == (void*) &mpi_fortran_statuses_ignore__)

#elif OMPI_F77_CAPS
#define OMPI_IS_FORTRAN_BOTTOM(addr) \
  (addr == (void*) &MPI_FORTRAN_BOTTOM)
#define OMPI_IS_FORTRAN_IN_PLACE(addr) \
  (addr == (void*) &MPI_FORTRAN_IN_PLACE)
#define OMPI_IS_FORTRAN_ARGV_NULL(addr) \
  (addr == (void*) &MPI_FORTRAN_ARGV_NULL)
#define OMPI_IS_FORTRAN_ARGVS_NULL(addr) \
  (addr == (void*) &MPI_FORTRAN_ARGVS_NULL)
#define OMPI_IS_FORTRAN_ERRCODES_IGNORE(addr) \
  (addr == (void*) &MPI_FORTRAN_ERRCODES_IGNORE)
#define OMPI_IS_FORTRAN_STATUS_IGNORE(addr) \
  (addr == (void*) &MPI_FORTRAN_STATUS_IGNORE)
#define OMPI_IS_FORTRAN_STATUSES_IGNORE(addr) \
  (addr == (void*) &MPI_FORTRAN_STATUSES_IGNORE)

#elif OMPI_F77_PLAIN
#define OMPI_IS_FORTRAN_BOTTOM(addr) \
   (addr == (void*) &mpi_fortran_bottom)
#define OMPI_IS_FORTRAN_IN_PLACE(addr) \
   (addr == (void*) &mpi_fortran_in_place)
#define OMPI_IS_FORTRAN_ARGV_NULL(addr) \
   (addr == (void*) &mpi_fortran_argv_null)
#define OMPI_IS_FORTRAN_ARGVS_NULL(addr) \
   (addr == (void*) &mpi_fortran_argvs_null)
#define OMPI_IS_FORTRAN_ERRCODES_IGNORE(addr) \
   (addr == (void*) &mpi_fortran_errcodes_ignore)
#define OMPI_IS_FORTRAN_STATUS_IGNORE(addr) \
   (addr == (void*) &mpi_fortran_status_ignore)
#define OMPI_IS_FORTRAN_STATUSES_IGNORE(addr) \
   (addr == (void*) &mpi_fortran_statuses_ignore)

#elif OMPI_F77_SINGLE_UNDERSCORE
#define OMPI_IS_FORTRAN_BOTTOM(addr) \
   (addr == (void*) &mpi_fortran_bottom_)
#define OMPI_IS_FORTRAN_IN_PLACE(addr) \
   (addr == (void*) &mpi_fortran_in_place_)
#define OMPI_IS_FORTRAN_ARGV_NULL(addr) \
   (addr == (void*) &mpi_fortran_argv_null_)
#define OMPI_IS_FORTRAN_ARGVS_NULL(addr) \
   (addr == (void*) &mpi_fortran_argvs_null_)
#define OMPI_IS_FORTRAN_ERRCODES_IGNORE(addr) \
   (addr == (void*) &mpi_fortran_errcodes_ignore_)
#define OMPI_IS_FORTRAN_STATUS_IGNORE(addr) \
   (addr == (void*) &mpi_fortran_status_ignore_)
#define OMPI_IS_FORTRAN_STATUSES_IGNORE(addr) \
   (addr == (void*) &mpi_fortran_statuses_ignore_)

#else
#define OMPI_IS_FORTRAN_BOTTOM(addr) \
   (addr == (void*) &mpi_fortran_bottom__)
#define OMPI_IS_FORTRAN_IN_PLACE(addr) \
   (addr == (void*) &mpi_fortran_in_place__)
#define OMPI_IS_FORTRAN_ARGV_NULL(addr) \
   (addr == (void*) &mpi_fortran_argv_null__)
#define OMPI_IS_FORTRAN_ARGVS_NULL(addr) \
   (addr == (void*) &mpi_fortran_argvs_null__)
#define OMPI_IS_FORTRAN_ERRCODES_IGNORE(addr) \
   (addr == (void*) &mpi_fortran_errcodes_ignore__)
#define OMPI_IS_FORTRAN_STATUS_IGNORE(addr) \
   (addr == (void*) &mpi_fortran_status_ignore__)
#define OMPI_IS_FORTRAN_STATUSES_IGNORE(addr) \
   (addr == (void*) &mpi_fortran_statuses_ignore__)

#endif /* weak / specific symbol type */

/* Convert between Fortran and C MPI_BOTTOM */
#define OMPI_ADDR(addr)  (OMPI_IS_FORTRAN_BOTTOM(addr) ? MPI_BOTTOM : (addr))
#define OMPI_IN_PLACE(addr)  (OMPI_IS_FORTRAN_IN_PLACE(addr) ? MPI_IN_PLACE : (addr))

#endif /* OMPI_F77_CONSTANTS_H */