1

How can I access a function on device via a function pointer?

In below code I am trying to access init0 or init1 using function pointer init. The code does work as intended if OpenACC is not enabled during compilation. However, it fails when compiled with OpenACC. Below code is saved as stackOverflow2.f95:

module modTest2

  use openacc
  implicit none

  
  type :: Container
    sequence
    integer :: n
    integer, allocatable :: arr(:)
  end type Container


  interface Container
    procedure :: new_Container
  end interface 


  abstract interface
    integer function function_template (i)
      integer, intent (in) :: i
    end function function_template
  end interface


  contains
  
    type(Container) function new_Container(n)
      integer, intent(in) :: n

      allocate(new_Container%arr(n))
    end function new_Container    
end module modTest2

program test2

  use modTest2
  implicit none


  integer :: n, x, i
  type(Container) :: c
  procedure(function_template), pointer :: init


  print *, "Enter array size: "
  read *, n
  print *, "Allocating..."
  c = Container(n)
  print *, "Allocation complete!"
  
  
  print *, "Enter initialization type (x): "
  read *, x
  print *, "Initializing..."
  select case (x)
    case (0)
      init => init0
    case default
      init => init1
  end select
  !$acc data copyin(c) copyout(c%arr)
  !$acc parallel loop present(c)
  do i = 1, n
    c%arr(i) = init(i)
  end do
  !$acc end data
  print *, "Initialization complete..."


  do i = 1, n
    print *, i, c%arr(i)
  end do
  
  
  contains
  
    integer function init0(i)
      !$acc routine
      integer, intent(in) :: i
      init0 = 10*i
    end function init0

    
    integer function init1(i)
      !$acc routine
      integer, intent(in) :: i
      init1 = 20*i
    end function init1
end program test2

Correct output is seen without OpenACC:

$ gfortran -c stackOverflow2.f95
$ gfortran stackOverflow2.o -o a.out
$ ./a.out
 Enter array size: 
3
 Allocating...
 Allocation complete!
 Enter initialization type (x): 
0
 Initializing...
 Initialization complete...
           1          10
           2          20
           3          30

Incorrect output is seen below with OpenACC (Note that NVIDIA compiler is used here):

$ /opt/nvidia/hpc_sdk/Linux_x86_64/22.1/compilers/bin/nvfortran stackOverflow2.f95 -acc; ./a.out
 Enter array size: 
3
 Allocating...
 Allocation complete!
 Enter initialization type (x): 
0
 Initializing...
 Initialization complete...
            1            0
            2            0
            3            0
DKS
  • 188
  • 10

2 Answers2

1

Sorry but function pointers (along with C++ virtual functions) are not yet supported on the device. Adding the compiler feedback flag (-Minfo=accel), you'll see the following message:

% nvfortran -acc -Minfo=accel test.f90
test2:
     62, Generating copyout(c%arr(:)) [if not already present]
         Generating copyin(c) [if not already present]
     65, Accelerator restriction: Indirect function/procedure calls are not supported

The problem being that indirect functions require a device jump table and runtime dynamic linking which is currently unavailable. While I don't have a timeline, we are exploring options on how to offer this support in the future.

Mat Colgrove
  • 5,441
  • 1
  • 10
  • 11
0

Using gfortran-11 with the below did the trick:

module modTest2

  use openacc
  implicit none

  
  type :: Container
    sequence
    integer :: n
    integer, allocatable :: arr(:)
  end type Container


  interface Container
    procedure :: new_Container
  end interface 


  abstract interface
    integer function function_template (i)
      integer, intent (in) :: i
    end function function_template
  end interface


  contains
  
    type(Container) function new_Container(n)
      integer, intent(in) :: n

      allocate(new_Container%arr(n))
    end function new_Container    
end module modTest2

program test2

  use modTest2
  implicit none


  integer :: n, x, i
  type(Container) :: c
  procedure(function_template), pointer :: init


  print *, "Enter array size: "
  read *, n
  print *, "Allocating..."
  c = Container(n)
  print *, "Allocation complete!"
  
  
  print *, "Enter initialization type (x): "
  read *, x
  print *, "Initializing..."
  select case (x)
    case (0)
      init => init0
    case default
      init => init1
  end select
  !$acc enter data copyin(c)
  !$acc enter data create(c%arr)
  !$acc parallel loop present(c)
  do i = 1, n
    c%arr(i) = init(i)
  end do
  !$acc exit data copyout(c%arr)
  !$acc exit data delete(c)
  print *, "Initialization complete..."


  do i = 1, n
    print *, i, c%arr(i)
  end do
  
  
  contains
  
    integer function init0(i)
      !$acc routine
      integer, intent(in) :: i
      init0 = 10*i
    end function init0

    
    integer function init1(i)
      !$acc routine
      integer, intent(in) :: i
      init1 = 20*i
    end function init1
end program test2

Here's the output:

$ gfortran-11 -fopenacc stackOverflow2.f95
$ gfortran-11 -fopenacc stackOverflow2.o -o stackOverflow2
$ ./stackOverflow2 
 Enter array size: 
4
 Allocating...
 Allocation complete!
 Enter initialization type (x): 
0
 Initializing...
 Initialization complete...
           1          10
           2          20
           3          30
           4          40
$ ./stackOverflow2 
 Enter array size: 
4
 Allocating...
 Allocation complete!
 Enter initialization type (x): 
9
 Initializing...
 Initialization complete...
           1          20
           2          40
           3          60
           4          80
DKS
  • 188
  • 10