Hi,
I’m hitting an internal LLVM IR verification failure with nvfortran 26.5 when a type-bound procedure is called on a polymorphic dummy argument from inside an !$OMP PARALLEL DO region, writing into module-procedure SAVE allocatables.
The error is:
/path/to/nvhpc-26.5/Linux_x86_64/26.5/compilers/share/llvm/bin/opt: /tmp/nvfortranDZu2kxSBciuLt.ll:1835:31: error: use of undefined value '%.O0006'
%106 = getelementptr i8, ptr %.O0006, i64 72, !dbg !633
Compile line:
nvfortran -g -traceback -Kieee -Mpreprocess -fast -O3 -fPIC -mp repro.F90
nvfortran --version:
nvfortran 26.5-0 64-bit target on x86-64 Linux -tp haswell
Platform:
bash-5.1$ uname -a
Linux xxx 6.18.33.2-microsoft-standard-WSL2 #1 SMP PREEMPT_DYNAMIC Thu Jun 18 21:54:43 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux
bash-5.1$ cat /etc/redhat-release
AlmaLinux release 9.8 (Olive Jaguar)
The build succeeds if I remove -mp. It still fails without optimization, so this does not appear to be an optimizer-aggressiveness issue.
Reproducer attached / inline below:
module grid_mod
implicit none
private
public :: base_grid, concrete_grid, other_grid
type, abstract :: base_grid
integer :: nx = 0, ny = 0
contains
procedure(field_pos_iface), deferred :: field_pos
end type base_grid
abstract interface
function field_pos_iface(self, i, j) result(pos)
import :: base_grid
class(base_grid), intent(in) :: self
integer, intent(in) :: i, j
integer :: pos
end function field_pos_iface
end interface
type, extends(base_grid) :: concrete_grid
contains
procedure :: field_pos => concrete_field_pos
end type concrete_grid
type, extends(base_grid) :: other_grid
contains
procedure :: field_pos => other_field_pos
end type other_grid
contains
function concrete_field_pos(self, i, j) result(pos)
class(concrete_grid), intent(in) :: self
integer, intent(in) :: i, j
integer :: pos
if (i < 1 .or. i > self%nx .or. j < 1 .or. j > self%ny) then
pos = -1
else
pos = i + (j - 1) * self%nx
end if
end function concrete_field_pos
function other_field_pos(self, i, j) result(pos)
class(other_grid), intent(in) :: self
integer, intent(in) :: i, j
integer :: pos
if (i < 1 .or. i > self%nx .or. j < 1 .or. j > self%ny) then
pos = -1
else
pos = i + (j - 1) * self%ny
end if
end function other_field_pos
end module grid_mod
module interp_mod
use grid_mod
implicit none
private
public :: compute
contains
subroutine compute(grid_in, no)
class(base_grid), intent(in) :: grid_in
integer, intent(in) :: no
integer :: n, i, j
integer :: ijx(4), ijy(4)
real :: xij, yij, xf, yf
real :: wx(4), wy(4)
! saved allocatables in static storage — as in the real code
integer, allocatable, save :: nxy(:,:,:), nc(:)
real, allocatable, save :: wxy(:,:,:)
integer, save :: nox = -1
! saved POLYMORPHIC allocatable — the unusual bit
class(base_grid), allocatable, save :: prev_grid_in
logical :: same_gridi, to_station_points
if (.not. allocated(prev_grid_in)) then
allocate(prev_grid_in, source = grid_in)
same_gridi = .false.
else
same_gridi = (prev_grid_in%nx == grid_in%nx)
if (.not. same_gridi) then
deallocate(prev_grid_in)
allocate(prev_grid_in, source = grid_in)
end if
end if
select type(grid_in)
type is(other_grid)
to_station_points = .true.
class default
to_station_points = .false.
end select
if (nox /= no) then
if (nox >= 0) deallocate(nxy, wxy, nc)
allocate(nxy(4,4,no), wxy(4,4,no), nc(no))
nox = no
end if
if (.not. same_gridi .or. to_station_points) then
!$OMP PARALLEL DO PRIVATE(N,XIJ,YIJ,IJX,IJY,XF,YF,J,I,WX,WY) SCHEDULE(STATIC)
do n = 1, no
xij = real(n)
yij = real(n)
ijx(1:4) = floor(xij - 1) + (/0,1,2,3/)
ijy(1:4) = floor(yij - 1) + (/0,1,2,3/)
xf = xij - ijx(2)
yf = yij - ijy(2)
do j = 1, 4
do i = 1, 4
nxy(i,j,n) = grid_in%field_pos(ijx(i), ijy(j))
end do
end do
wx(1) = xf*(1-xf)*(2-xf)/(-6.)
wx(2) = (xf+1)*(1-xf)*(2-xf)/2.
wx(3) = (xf+1)*xf*(2-xf)/2.
wx(4) = (xf+1)*xf*(1-xf)/(-6.)
wy(1) = yf*(1-yf)*(2-yf)/(-6.)
wy(2) = (yf+1)*(1-yf)*(2-yf)/2.
wy(3) = (yf+1)*yf*(2-yf)/2.
wy(4) = (yf+1)*yf*(1-yf)/(-6.)
do j = 1, 4
do i = 1, 4
wxy(i,j,n) = wx(i)*wy(j)
end do
end do
end do
!$OMP END PARALLEL DO
end if
print *, sum(nxy), sum(wxy)
end subroutine compute
end module interp_mod
program repro
use grid_mod
use interp_mod
implicit none
integer, parameter :: no = 100
type(concrete_grid) :: g
type(other_grid) :: o
g%nx = 10
g%ny = 10
o%nx = 10
o%ny = 10
call compute(g, no)
call compute(o, no)
end program repro
This was originally found building NCEPLIBS-ip ( GitHub - NOAA-EMC/NCEPLIBS-ip: Fortran 90 subprograms to be used for interpolating between nearly all grids used at NCEP. · GitHub ), specifically src/bicubic_interp_mod.F90, which uses this pattern — a class(ip_grid) dummy with a field_pos type-bound function called inside an OpenMP parallel loop. The same pattern appears in several sibling interpolation modules in that library, so it’s probably not an isolated occurrence. NCEPLIBS-ip is a NOAA production interpolation library used across US operational NWP software stacks.
Thanks,
Dom
–
Disclaimer: I used Claude Opus 5 to create the reproducer and create the bug report.