# Do concurrent + NVIDIA GPU offload: does it support external procedures?

**URL:** <https://fortran-lang.discourse.group/t/do-concurrent-nvidia-gpu-offload-does-it-support-external-procedures/11087>\
**Category:** Help\
**Created:** [September 7, 2026, 10:04am UTC](https://fortran-lang.discourse.group/t/do-concurrent-nvidia-gpu-offload-does-it-support-external-procedures/11087 "2026-09-07T10:04:29Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![Shahid](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/shahid/32/3672_2.png) [@Shahid](https://fortran-lang.discourse.group/u/Shahid)\
**Post date:** [September 7, 2026, 10:04am UTC](https://fortran-lang.discourse.group/t/do-concurrent-nvidia-gpu-offload-does-it-support-external-procedures/11087/1 "2026-09-07T10:04:29Z")

</div>

I’ve been experimenting with `do concurrent` and NVIDIA’s automatic GPU offload (`nvfortran -stdpar=gpu`), and I’ve run into a pattern that makes me think **external module procedures called from a `do concurrent` construct aren’t currently supported for offload**. I’d like to check with the community whether this matches your understanding, or whether I’m missing a flag/attribute somewhere.

Here’s what I tested, going from “definitely works” to “breaks”:

## Case 1 — Internal (contained) `pure` subroutine, single file

Everything — the main program and a `pure` subroutine — lives in one file, with the subroutine in a `contains` block of the program.

```auto
program random_2d_example
    implicit none
    
    ! Declare variables
    integer, parameter :: n_rows = 4
    integer, parameter :: n_cols = 4
    integer :: i, j
    real, dimension(n_rows, n_cols) :: r
    real, dimension(n_rows, n_cols) :: c, p
    
    ! Initialize random number generator array
    call random_number(r)
    
    ! Generate values and process them through the pure routine
	do concurrent (i=1:n_cols,j=1:n_rows) 
            call initial_micro(r(i, j), c(i, j))
            p(i, j) = c(i, j)
    end do

contains

    pure subroutine initial_micro(r_val, c_val)
        real, intent(in) :: r_val
        real, intent(out) :: c_val

        c_val = r_val
    end subroutine initial_micro

end program random_2d_example

```

**Result:** compiles and offloads fine.

```auto
nvfortran -stdpar=gpu -gpu=cc61 -gpu=mem:managed -Minfo=accel test.f90 -o test
random_2d_example:
     15, Generating NVIDIA GPU code
         15, ! blockidx%x threadidx%x auto-collapsed
             Loop parallelized across CUDA thread blocks, CUDA threads(32) collapse(2) ! blockidx%x threadidx%x collapsed-innermost
     15, Generating implicit copy(c(:,:)) [if not already present]
         Generating implicit copyin(r(:,:)) [if not already present]
         Generating implicit copyout(p(:,:)) [if not already present]
initial_micro:
     22, Generating implicit acc routine seq
         Generating acc routine seq
         Generating NVIDIA GPU code

```

## Case 2 — `pure` subroutine inside an internal module, still single file

Same file, but now `routine` lives in a module (in the same source file).

```auto
! --- Microphysics Module ---
module micro_mod
    implicit none
    
    ! Keep public interfaces clean and structured
    private
    public :: initial_micro

contains

    pure subroutine initial_micro(r_val, c_val)
        real, intent(in) :: r_val
        real, intent(out) :: c_val

        c_val = r_val
    end subroutine initial_micro

end module micro_mod

! --- Main Program ---
program random_2d_example
    use micro_mod, only: initial_micro
    implicit none
    
    integer, parameter :: n_rows = 5
    integer, parameter :: n_cols = 4
    integer :: i, j
    real, dimension(n_rows, n_cols) :: r
    real, dimension(n_rows, n_cols) :: c, p
    
    ! Initialize random numbers
    call random_number(r)
    
    ! Offload execution to GPU via do concurrent
    do concurrent (i = 1:n_rows, j = 1:n_cols) 
        call initial_micro(r(i, j), c(i, j))
        p(i, j) = c(i, j)
    end do

    print *, "Program executed successfully. First element p(1,1):", p(1, 1)

end program random_2d_example

```

**Result:** still compiles and offloads fine.

```auto
nvfortran -stdpar=gpu -gpu=cc61 -gpu=mem:managed -Minfo=accel test_module.f90 -o test_module
initial_micro:
     11, Generating implicit acc routine seq
         Generating acc routine seq
         Generating NVIDIA GPU code
random_2d_example:
     36, Generating NVIDIA GPU code
         36, ! blockidx%x threadidx%x auto-collapsed
             Loop parallelized across CUDA thread blocks, CUDA threads(32) collapse(2) ! blockidx%x threadidx%x collapsed-innermost
     36, Generating implicit copy(c(:,:)) [if not already present]
         Generating implicit copyin(r(:,:)) [if not already present]
         Generating implicit copyout(p(:,:)) [if not already present]

```

## Case 3 — Same module, but split into a separate source file

```auto
module micro_mod
    implicit none
    private
    public :: initial_micro

contains

    elemental subroutine initial_micro(r_val, c_val)
        real, intent(in) :: r_val
        real, intent(out) :: c_val

        c_val = r_val
    end subroutine initial_micro

end module micro_mod

```

```auto
program random_2d_example
    use micro_mod, only: initial_micro
    implicit none
    
    integer, parameter :: n_rows = 5
    integer, parameter :: n_cols = 4
    integer :: i, j
    real, dimension(n_rows, n_cols) :: r
    real, dimension(n_rows, n_cols) :: c, p
    
    call random_number(r)
    
    do concurrent (i = 1:n_rows, j = 1:n_cols) 
        call initial_micro(r(i, j), c(i, j))
        p(i, j) = c(i, j)
    end do

    print *, "Program executed successfully. First element p(1,1):", p(1, 1)

end program random_2d_example

```

**Result:**

```auto
nvfortran -stdpar=gpu -gpu=cc61 -gpu=mem:managed -c micro_mod.f90
nvfortran -stdpar=gpu -gpu=cc61 -gpu=mem:managed -c main.f90
NVFORTRAN-S-1074-Procedure call in Do Concurrent is not supported yet (main.f90: 14)
 0 inform, 0 warnings, 1 severes, 0 fatal for random_2d_example

```

## My question

Based on this, it looks like offloading a `do concurrent` loop that calls an external module procedure (i.e. a procedure compiled in a separate translation unit and pulled in via `use`) is **not currently supported** , even though the exact same procedure works fine when it’s defined internally to the program or in a module within the same file.

Is this a known/expected limitation of `nvfortran`’s `-stdpar` GPU offload today (e.g. related to whole-program analysis or LTO requirements for offload regions), or is there a way to make external procedures visible to the offload compiler (some combination of IPA/LTO flags, or explicit interface attributes) that I’m missing?

---

<div class="post-metadata">

**Author:** ![jorgeg](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/jorgeg/32/6835_2.png) [@jorgeg](https://fortran-lang.discourse.group/u/jorgeg)\
**Post date:** [September 7, 2026, 11:07am UTC](https://fortran-lang.discourse.group/t/do-concurrent-nvidia-gpu-offload-does-it-support-external-procedures/11087/2 "2026-09-07T11:07:23Z")

</div>

this is a limitation yes, you have to have them in the same translation unit or via an `include` statement.

---

<div class="post-metadata">

**Author:** ![Shahid](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/shahid/32/3672_2.png) [@Shahid](https://fortran-lang.discourse.group/u/Shahid)\
**Post date:** [September 7, 2026, 2:35pm UTC](https://fortran-lang.discourse.group/t/do-concurrent-nvidia-gpu-offload-does-it-support-external-procedures/11087/3 "2026-09-07T14:35:31Z")

</div>

"This is interesting — it’s also the first time I’ve come across the use of an `include` statement in this context.

```auto
nvfortran -stdpar=gpu -gpu=cc61 -gpu=mem:managed main.f90 -o main.out
./main.out
 Program executed successfully. First element p(1,1): 0.9079230

```

**Single command:** The other interesting part is that a single command is enough to build and run the whole program — no separate compilation step needed."

---

<div class="post-metadata">

**Author:** ![jorgeg](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/jorgeg/32/6835_2.png) [@jorgeg](https://fortran-lang.discourse.group/u/jorgeg)\
**Post date:** [September 7, 2026, 10:10pm UTC](https://fortran-lang.discourse.group/t/do-concurrent-nvidia-gpu-offload-does-it-support-external-procedures/11087/4 "2026-09-07T22:10:14Z")

</div>

> [@Shahid](#):
>
> This is interesting — it’s also the first time I’ve come across the use of an `include` statement in this context.

They are ugly but useful for exactly this application, in [this](https://github.com/jorgeG94/terco) you can see I make a lot of use of them.

---

<div class="post-metadata">

**Author:** ![Shahid](https://yyz2.discourse-cdn.com/free1/user_avatar/fortran-lang.discourse.group/shahid/32/3672_2.png) [@Shahid](https://fortran-lang.discourse.group/u/Shahid)\
**Post date:** [September 8, 2026, 2:56am UTC](https://fortran-lang.discourse.group/t/do-concurrent-nvidia-gpu-offload-does-it-support-external-procedures/11087/5 "2026-09-08T02:56:59Z")

</div>

The compilation is simple. Just one line. So it is very useful. Adding a statement at the top is not a big trouble. The good thing is that we do not need to rewrite the whole program.
