Reputation: 5705
I'm working on Intel Fortran compatibility for a fairly large code (weather prediction model). On Intel Fortran (and only Intel Fortran) some character data seems to get scrambled looping through and point character pointers to it. The characters I end up with only contains 0 and 9. The character string c
simply has "00:00:00:16 (whitespace padding....)"
as its content.
I was able to reproduce the issue to come up whenever I include any (dead) OpenMP code and use -fopenmp
compiler flag. I'm a bit lost at what's going on here. Can you detect any programming errors in the following minimal reproducer? If not, I guess I will file a compiler bug.
minimal.f90
module foo
implicit none
type pointer_character
character, pointer :: c
end type
type(pointer_character), allocatable :: ptr_c(:)
integer(4) :: idx_c
contains
subroutine grpbcast_set_c(c)
implicit none
character(*), intent(in), target :: c
integer(4) :: cloc
allocate(ptr_c( 256 ))
idx_c = 0
do cloc = 1, len(c)
idx_c = idx_c + 1
ptr_c( idx_c )%c => c( cloc:cloc )
end do
print *, "testprint-2", c
print *, "testprint-3.1", ptr_c( 1 )%c
print *, "testprint-3.2", ptr_c( 2 )%c
print *, "testprint-3.3", ptr_c( 3 )%c
print *, "testprint-3.4", ptr_c( 11 )%c
end subroutine
! The following subroutine is never called, but if we include it in the module foo, it will lead to the data corruption documented in
! http://stackoverflow.com/questions/42359258/intel-fortran-substring-access-with-convert-big-endian
! If the subroutine is commented it will work
!
! In other words:
! if we comment this subroutine out, the output will be
! =====================================================
! testprint-2
! 00:00:00:16
! testprint-3.10
! testprint-3.20
! testprint-3.3:
! testprint-3.46
! =====================================================
! However if we include it, the output will be
! testprint-2
! 00:00:00:16
! testprint-3.10
! testprint-3.20
! testprint-3.30
! testprint-3.40
! =====================================================
!
! tested with: ifort 17.0.1 20161005
subroutine scatter_one_record()
implicit none
integer(4) :: k
!$OMP PARALLEL DO
do k = 1, 5
end do
!$OMP END PARALLEL DO
end subroutine
end module foo
program main
use foo, only: grpbcast_set_c
implicit none
character(len=256), target:: run_period
run_period = "00:00:00:16"
call grpbcast_set_c(run_period)
end program
Makefile
.PHONY: all
all: minimal
minimal.o: minimal.f90
ifort -fopenmp -c minimal.f90 -o minimal.o
minimal: minimal.o
ifort -fopenmp -o minimal -L./ minimal.o
ifort version
> ifort --version
ifort (IFORT) 17.0.1 20161005
compile&run
make
./minimal
..
..
Loop in question
subroutine grpbcast_set_c(c)
use nrtype, only : rp => rp
implicit none
character(*), intent(in), target :: c
integer(4) :: cloc
do cloc = 1, len(c)
idx_c = idx_c + 1
if (idx_c > max_ptr_gbcast) stop 9
ptr_c( idx_c )%c => c( cloc:cloc )
end do
end subroutine
Specification of ptr_c in module
type pointer_character
character, pointer :: c
end type
type(pointer_character), private, allocatable, save :: ptr_c(:)
Data as seen in debugger (TotalView)
Compiler Command
> mpif90 -O0 -no-ipo -g -convert big_endian -fopenmp -r8 -DUSE_MPI -I/usr/apps.sp3/mpi/openmpi/1.6.5/i2013.1.046/include -I/usr/apps.sp3/mpi/openmpi/1.6.5/i2013.1.046/lib -I [NUSDAS13_PATH]/src -I [NETCDF_PATH]/include -DUSE_MPI -c mpi_comm.f90 -o mpi_comm.o
> mpif90 --version
ifort (IFORT) 14.0.2 20140120
Copyright (C) 1985-2014 Intel Corporation. All rights reserved.
Update
I just tried the same with the newest Intel compiler version:
> mpif90 --version
> ifort (IFORT) 17.0.1 20161005
The result is still wrong but different! This time I get all the characters zeroed.
Update 2
I've been trying to reproduce the issue with a minimal reproducer (see below), so far no luck, i.e. the error does not show up when running it this way. However, one thing is interesting: TotalView shows the first address of the array slightly differently:
I guess it must be a problem with the input c
, even though it shows up correctly in the debugger at the point where grpcast_set_c
is entered.
module foo
implicit none
type pointer_character
character, pointer :: c
end type
type(pointer_character), allocatable, save :: ptr_c(:)
integer(4), save :: idx_c
contains
subroutine parmread_bcast_a( mt, aout, chktag )
implicit none
integer(4), intent(in):: mt
character(*), intent(out):: aout
character(*), intent(in):: chktag
call filetool_cardread_a( mt, aout, chktag )
write(6,'("* ",a28," = ", a)') chktag, trim(aout)
call set_ptr_c(aout)
return
end subroutine parmread_bcast_a
subroutine filetool_cardread_a( imt, aout, chktag )
implicit none
integer(4), intent(in):: imt
character(*), intent(out):: aout
character(*), intent(in), optional:: chktag
character(len=256):: tag
character(len=256):: val
character(len=256):: buff
integer(4):: idxeql
integer(4) :: is_debug = 0
cardread: do
read( imt, "(a)" ) buff
if( buff(1:1) /= "*" ) then
idxeql = index(buff, "=")
tag = adjustl(buff(:idxeql-1))
if (is_debug == 1) then
write(6,*) 'buff', trim(buff)
endif
if ( present( chktag ) ) then
if ( trim(chktag) /= trim(tag) ) then
write(6,*)"WARNING !!! the record is for ", trim(tag), &
& ", while you specified ", trim(chktag)
cycle cardread
endif
endif
val = adjustl(buff(idxeql+1:))
aout = val
return
end if
end do cardread
end subroutine filetool_cardread_a
subroutine set_ptr_c(c)
implicit none
character(*), intent(in), target :: c
integer(4) :: cloc
do cloc = 1, len(c)
idx_c = idx_c + 1
if (idx_c > 10000) stop 9
ptr_c( idx_c )%c => c( cloc:cloc )
end do
end subroutine
end module
program main
use foo, only: ptr_c, idx_c, parmread_bcast_a
implicit none
character(len=256), save:: run_period
allocate(ptr_c( 10000 ))
idx_c = 0
open( 100, file='sample.conf', form='formatted')
call parmread_bcast_a(100, run_period, "run_period")
print *, "run period", run_period
print *, ptr_c( 1 )%c
print *, ptr_c( 2 )%c
print *, ptr_c( 3 )%c
print *, ptr_c( 11 )%c
end program
sample.conf
run_period = 00:00:00:16
Upvotes: 1
Views: 369
Reputation: 5705
As a workaround, separating the OpenMP code from this kind of datastructure initialization into multiple modules (even in the same file), works. In above case:
module foo1
implicit none
type pointer_character
character, pointer :: c
end type
type(pointer_character), allocatable :: ptr_c(:)
integer(4) :: idx_c
contains
subroutine grpbcast_set_c(c)
implicit none
character(*), intent(in), target :: c
integer(4) :: cloc
allocate(ptr_c( 256 ))
idx_c = 0
do cloc = 1, len(c)
idx_c = idx_c + 1
ptr_c( idx_c )%c => c( cloc:cloc )
end do
print *, "testprint-2", c
print *, "testprint-3.1", ptr_c( 1 )%c
print *, "testprint-3.2", ptr_c( 2 )%c
print *, "testprint-3.3", ptr_c( 3 )%c
print *, "testprint-3.4", ptr_c( 11 )%c
end subroutine
end module
module foo2
implicit none
contains
subroutine scatter_one_record()
implicit none
integer(4) :: k
!$OMP PARALLEL DO
do k = 1, 5
end do
!$OMP END PARALLEL DO
end subroutine
end module
program main
use foo1, only: grpbcast_set_c
implicit none
character(len=256), target:: run_period
run_period = "00:00:00:16"
call grpbcast_set_c(run_period)
end program
Upvotes: 0