This is the mail archive of the fortran@gcc.gnu.org mailing list for the GNU Fortran project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Implementing OpenACC's Fortran module


Hi!

We had some discussion about how to implement OpenACC's Fortran
interfaces, for routines such as:

| 3.2.17 acc_copyin
| Summary
| The acc_copyin routine allocates memory on the accelerator device to correspond to the
| specified host memory, and copies the data to that device memory on a non-shared memory
| accelerator.
| Format
| C or C++:
|     void* acc_copyin ( h_void*, size_t );
| Fortran:
|     subroutine acc_copyin ( a )
|       type, dimension(:[,:]...) :: a
|     subroutine acc_copyin ( a, len )
|       type :: a
|       integer :: len
| Description
| The acc_copyin routine is equivalent to the enter data directive with a copyin
| clause. In C, the arguments are a pointer to the data and length in bytes; the function returns a
| pointer to the allocated space, as with acc_malloc. Pointers assigned from this function
| may be used in deviceptr clauses to tell the compiler that the pointer target is resident on
| the accelerator. In Fortran, two forms are supported. In the first, the argument is a contiguous
| array section of intrinsic type. In the second, the first argument is a variable or array element
| and the second is the length in bytes. Memory is allocated on the device, and the data is
| copied from the host memory to the newly allocated device memory. A call to this routine
| starts a data lifetime for the specified data. This data may be accessed in using the present
| data clause. It is a runtime error to call this routine if the data is already present on the device.

For reference, OpenMP also defines such Fortran interfaces, even though,
none of these have to deal with arrays.  Stub functions are implemented
in libgomp/fortran.c as necessary.

The OpenACC as well as the OpenMP specification describe Âinclude as
well as Âmodule files.  In discussion it has become clear that ÂincludeÂ
files will be more difficult (at least, more verbose) to support than we
can do with Âmodule files, so for a first step, we won't implement
Âinclude files for Fortran OpenACC in GCC.

Jim had started to manually implement the Âmodule file, and had to write
a lot of boilerplate code for all the different data types that need to
be supported.  At the GNU Tools Cauldron, Tobias and I briefly looked
into this, and he showed that it can also be implemented in a way similar
to (untested):

    module openacc
      implicit none
    
        interface acc_copyin
          subroutine f_acc_copyin (h, s) bind (c)
            use iso_c_binding
            type (*) :: h
            integer (c_size_t) :: s
          end subroutine
          procedure f_acc_copyin_array
        end interface
    
        contains
    
          subroutine f_acc_copyin_array (h) bind (c)
            use iso_c_binding
            type (*), target, dimension(..) :: h
            call f_acc_copyin(c_loc(h), size(h, kind = c_size_t))
          end subroutine
    
    end module

(Tobias: to get around the two compiler errors, I changed the compiler
>From the system's 4.8 version to the current trunk, and added the Âkind =
c_size_t to have the SIZE intrinsic return the appropriate data type.

Tobias has now further elaborated on this (many thanks!):

On Tue, 22 Jul 2014 15:39:59 +0200, Tobias Burnus <tobias.burnus@physik.fu-berlin.de> wrote:
> A Technical Specification (TS), which was drafted and released
> after Fortran 2008, simplifies the array and type handling in
> Fortran, which was mostly requested by the MPI developers and
> which is used by the MPI 3.0 spec. Namely, the Fortran TS on
> "Further Interoperability of Fortran with C", ISO/IEC TS 29113:2012,
> ftp://ftp.nag.co.uk/sc22wg5/N1901-N1950/N1942.pdf
> 
> In particular, "TYPE(*)" simply takes any type as argument and
> "DIMENSION(..)" takes argument of any rank (scalar to maximal rank)
> as argument. The DIMENSION(..) - as DIMENSION(:) - passes the array
> as array descriptor, i.e. as struct which contains a pointer to
> the actual array data and as meta data information such as element
> size, array bounds, stride etc.
> 
> Regarding OpenACC 2.0a's
>    subroutine acc_copyout ( a, len )
>       type :: a
>       integer :: len
> 
> The "type" can directly be replaced by "type(*)".
> 
> Note: OpenACC explicitly uses "integer", which is a "default
> kind integer" in terms of Fortran, which is usually 32bit wide. I
> think one can ignore that the compiler can turn the default value
> into 64 bit ("-finteger-default-8") as I don't think many users
> will do this.
> 
> Using integer(c_size_t) is nice, but that will cause pain for the
> users as a "4" is not automatically promoted to 64bit variable
> ("4_c_size_t") in Fortran.
> 
> Thus, how about something like:
> 
>   interface
>     subroutine acc_copyout_lib (a, len) bind(C, name="acc_copyout")
>       !GCC$ ATTRIBUTES NO_ARG_CHECK :: a
>       type(*), dimension(*) :: a
>       integer(c_size_t), value :: len
>     end subroutine acc_copyout_lib
>   end interface
> 
> That subroutine would be the one in the C library. The "value" is needed
> to make the arguments passed by value. I have added dimension(*) which
> makes the subroutine take arguments to "a" of any rank including scalar
> array arguments. It does not permit passing scalars, thus, I added the
> NO_ARG_CHECK. (I think Fortran 2015 will permit scalars with type(*),
> dimension(*), but gfortran doesn't yet; thus, the !GCC$.)
> 
> 
> For the userfacing interface, one could then have:
>   private
>   public :: acc_copyout
>   interface acc_copyout
>     procedure :: acc_copyout_32, acc_copyout_64
>   end interface acc_copyout
> ...
>   contains
> ...
>   subroutine acc_copyout_32(a, len)
>     use iso_fortran_env, only: int32
>     use iso_c_binding, only: c_size_t
>     !GCC$ ATTRIBUTES NO_ARG_CHECK :: a
>     type(*), dimension(*) :: a
>     integer(int32) :: len
>     call acc_copyout_lib (a, int(len, kind=c_size_t))
>   end subroutine acc_copyout_32
> 
> and then the same for int64. That would permit using -finteger-default-8
> if one really wants and to also pass a size_t variable. I use int32 and
> int64 explicitly as "integer" and "integer(c_size_t)" would fail on a
> 32bit system. (int32 could also be replaced by c_int32_t from iso_c_binding.)
> Only using "integer" would be what OpenACC specifies - even if it limits the
> maximal size to "huge(0)" bytes (= 2 GB as one has only signed integers.
> 
> Note the use of PRIVATE to only expose those parts as public which are in the
> spec. Additionally, private procedures are (with few exception) automatically
> restricted to the file which uses them (static functions).
> 
> 
> Regarding OpenACC 2.0a's
>   subroutine acc_copyout ( a)
>     type, dimension(:[,:] ...) :: a 
> 
> in principle the following should do:
> 
>   subroutine acc_copyout_array(a)
>     use iso_c_binding, only: c_size_t
>     type(*), dimension(..) :: a
>     call acc_copyout_lib (a, int(storage_size(a)*size(a), kind=c_size_t))
>   end subroutine acc_copyout_array
> 
> However, as "type(*)" implies something without type information, one
> does not know the storage size of a single element. But if combined with
> "dimension(..)", there is actually information about the type available
> in the array descriptor. Hence, it would work - just a check in the compiler
> prevents it.
> Thus, one either remove the check for storage_size with -std=gnu - and only
> reject it with -std=f2008ts, extend the GNU-extension sizeof to support it,
> or to do something else, namely using CLASS(*) instead.
> 
> In my opinion, CLASS(*) should always use the array descriptor - including
> for scalars, which would directly give the type and size for intrinsic types.
> That's also the plan when switching to the new array descriptor. However,
> currently, gfortran creates a vtable for intrinsic types and uses the pointer
> to the vtable in addition to passing the actual data. On the pro side, it
> should be valid ISO Fortran and simply work when using CLASS(*).
> 
> Unfortunaly, I hit an ICE with the CLASS(*) code, which I will debug.
> 
> Question: What should happen if the array has strides (i.e. it is
> noncontiguous) or it has assumed size (i.e. the upper bound is not known)?
> Should the code then fail arbitrarily (garbage in, garbage out)? Or should
> this lead to an abort with a nice error message, which means a run-time
> check for it?
> 
> Next task on my side is to debug and fix the ICE. I could also ponder
> whether one could/should extend one intrinsic to work with type(*),
> dimension(..) instead of failing with an error message.
> 
>  * * *
> 
> To conclude, one has three possibilities:
> 
> 1. To use the module outlined above - and as pasted below.
> 
> or alternatively
> 
> 2. To handle the Fortran code directly in C, i.e. doing the size
>    calculation in C. Then one could use a Fortran module without object code
>    of the form:
>      interface acc_copyout
>        subroutine acc_copyout_32(a, len) bind(c)
>           ...
>        subroutine acc_copyout_64(a, len) bind(c)
>           ...
>        subroutine acc_copyout_array(a, len) bind(c)
>           ...
>      end subroutine
>    where the last function uses TYPE(*). Plus implementing the array-size
>    handling and the "integer" instead of "size_t" len in the library.
> 
> or alternatively
> 
> 3. To create a virtual module as done for iso_c_binding and iso_fortran_env,
>    which is completely handled in the compiler. That is a bit tedious as one
>    needs to handle all intrinsics on GENERIC level, but is most flexible. For
>    instance, one could reject assumed-size arrays at compile time.
> 
>    To do so, see the *.def files for iso_fortran_env/iso_c_binding, intrinsic.c
>    for the interface definition of the intrinsics, check.c for the argument
>    checking and module.c for loading them. And in trans-intrinsic.c, the actual
>    GENERIC tree is produced. See for instance c_f_pointer (GFC_ISYM_C_F_POINTER)
>    for how this can be handled.
> 
>  * * *
> 
> Here comes the currently ICEing version of how one could implement it:
> 
> module m
>   implicit none
>   private
>   public :: acc_copyout
> 
> ! DEBUG: Commented to make test code accessible
> !  interface
> !    subroutine acc_copyout_lib (a, len) bind(C, name="acc_copyout")
> !      use iso_c_binding, only: c_size_t
> !      !GCC$ ATTRIBUTES NO_ARG_CHECK :: a
> !      type(*), dimension(*) :: a
> !      integer(c_size_t), value :: len
> !    end subroutine acc_copyout_lib
> !  end interface
> 
>   interface acc_copyout
>     ! Extension: * Supports 32 and 64 bit "len" arguments;
>     !            * Accepts also array arguments
>     procedure :: acc_copyout_32, acc_copyout_64
>     ! Array version. Assumes contiguous arrays which are not
>     ! of assumed size.
>     procedure :: acc_copyout_array
>   end interface acc_copyout
> 
> contains
> 
>   subroutine acc_copyout_32(a, len)
>     use iso_fortran_env, only: int32
>     use iso_c_binding, only: c_size_t
>     !GCC$ ATTRIBUTES NO_ARG_CHECK :: a
>     type(*), dimension(*) :: a
>     integer(int32) :: len
>     call acc_copyout_lib (a, int(len, kind=c_size_t))
>   end subroutine acc_copyout_32
> 
>   subroutine acc_copyout_64(a, len)
>     use iso_fortran_env, only: int64
>     use iso_c_binding, only: c_size_t
>     !GCC$ ATTRIBUTES NO_ARG_CHECK :: a
>     type(*), dimension(*) :: a
>     integer(int64) :: len
>     call acc_copyout_lib (a, int(len, kind=c_size_t))
>   end subroutine acc_copyout_64
> 
>   subroutine acc_copyout_array(a)
>     use iso_c_binding, only: c_size_t
>     class(*), dimension(..) :: a
>     call acc_copyout_lib (a, int(storage_size(a)*size(a), kind=c_size_t))
>   end subroutine acc_copyout_array
> 
> ! DEBUG: Inserted to print "len"
> subroutine acc_copyout_lib (a, len) bind(C, name="acc_copyout")
>     use iso_c_binding, only: c_size_t
>   !GCC$ ATTRIBUTES NO_ARG_CHECK :: a
>   type(*), dimension(*) :: a
>   integer(c_size_t), value :: len
>   print *, len
> end subroutine acc_copyout_lib
> 
> end module m
> 
> use m
> implicit none
> integer :: a, b(2)
> call acc_copyout(a)
> call acc_copyout(b)
> call acc_copyout(7_8)
> call acc_copyout([1,2,3])
> end


GrÃÃe,
 Thomas

Attachment: pgpmFv2c5o7nn.pgp
Description: PGP signature


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]