I meant to write you a short comment but it grew somehow a little bit over the limit...
The MPI standard body and implementors have struggled for ages with this C to Fortran memory passing problem. Why not reuse their efforts instead of rediscovering the fact, that a round wheel works better than a square one?
Just take a look at the MPI standard function MPI_ALLOC_MEM
which is supposed to allocate special memory in MPI and return it to the user code. The MPI-2.2 standard defines its Fortran interface as:
MPI_ALLOC_MEM(SIZE, INFO, BASEPTR, IERROR)
INTEGER INFO, IERROR
INTEGER(KIND=MPI_ADDRESS_KIND) SIZE, BASEPTR
The modern Fortran 2008 interface in MPI-3.0 uses ISO_C_BINDING
and comes as:
MPI_Alloc_mem(size, info, baseptr, ierror)
USE, INTRINSIC :: ISO_C_BINDING, ONLY : C_PTR
INTEGER(KIND=MPI_ADDRESS_KIND), INTENT(IN) :: size
TYPE(MPI_Info), INTENT(IN) :: info
TYPE(C_PTR), INTENT(OUT) :: baseptr
INTEGER, OPTIONAL, INTENT(OUT) :: ierror
The standard gives the following example on how to use the call:
USE mpi_f08
USE, INTRINSIC :: ISO_C_BINDING
TYPE(C_PTR) :: p
REAL, DIMENSION(:,:), POINTER :: a
INTEGER, DIMENSION(2) :: shape
INTEGER(KIND=MPI_ADDRESS_KIND) :: size
shape = (/100,100/)
size = 4 * shape(1) * shape(2)
CALL MPI_Alloc_mem(size,MPI_INFO_NULL,p,ierr)
CALL C_F_POINTER(p, a, shape)
...
a(3,5) = 2.71
...
CALL MPI_Free_mem(a, ierr)
Basically the the C_F_POINTER
routine from ISO_C_BINDING
binds the C pointer to the Fortran pointer and then the memory, pointed by the former becomes available through the latter.
This is how Open MPI implements the F08 MPI_Alloc_mem
:
subroutine MPI_Alloc_mem_f08(size,info,baseptr,ierror)
use, intrinsic :: ISO_C_BINDING, only : C_PTR
use :: mpi_f08_types, only : MPI_Info, MPI_ADDRESS_KIND
use :: mpi_f08, only : ompi_alloc_mem_f
implicit none
INTEGER(MPI_ADDRESS_KIND), INTENT(IN) :: size
TYPE(MPI_Info), INTENT(IN) :: info
TYPE(C_PTR), INTENT(OUT) :: baseptr
INTEGER, OPTIONAL, INTENT(OUT) :: ierror
integer :: c_ierror
call ompi_alloc_mem_f(size,info%MPI_VAL,baseptr,c_ierror)
if (present(ierror)) ierror = c_ierror
end subroutine MPI_Alloc_mem_f08
ompi_alloc_mem_f
is a C function that interfaces the internal C implementation to Fortran:
void ompi_alloc_mem_f(MPI_Aint *size, MPI_Fint *info, char *baseptr, MPI_Fint *ierr)
{
int ierr_c;
MPI_Info c_info = MPI_Info_f2c(*info);
ierr_c = MPI_Alloc_mem(*size, c_info, baseptr);
if (NULL != ierr) *ierr = OMPI_INT_2_FINT(ierr_c);
}
So you can see that the TYPE(C_PTR)
baseptr
argument from Fortran simply comes in as a pointer, passed (as usual) by reference. This is not quite evident here, since the MPI standard defines the last argument to MPI_Alloc_mem
, where a pointer to the allocated memory is returned, as void *
while it is in fact a void
pointer passed by reference (i.e. void **
). Also the dummy baseptr
argument is actually void **
but is declared simply as char *
because of reasons :) The same function is used to implement the old Fortran interface, so the char *baseptr
maps to an INTEGER(KIND=MPI_ADDRESS_KIND)
actual argument.
The lesson is that while MPI_ADDRESS_KIND
integers in Fortran are meant to store both pointer and pointer difference values, you should not use MPI_Aint
as pointer argument type in C but rather regular double pointers like void **
.