https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85975
Bug ID: 85975
Summary: Incorrect size for spread array
Product: gcc
Version: 8.1.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: libfortran
Assignee: unassigned at gcc dot gnu.org
Reporter: stephan.kramer at imperial dot ac.uk
Target Milestone: ---
With the following program
program foo
implicit none
call bar(2, 3, 5, 7)
contains
subroutine bar(k, l, m, n)
integer, intent(in) :: k, l, m, n
real, dimension(k) :: a
real, dimension(k,l):: b
real, dimension(k,l,m):: c
real, dimension(k,l,m,n):: d
print *, size(spread(A, 1, 1))
print *, size(spread(b, 1, 1))
print *, size(spread(c, 1, 1))
print *, size(spread(d, 1, 1))
end subroutine
end program
I get the following result:
$ gfortran test2.f90
skramer@gyre:~/tst/gfortran8$ ./a.out
2
6
0
210
skramer@gyre:~/tst/gfortran8$ ./a.out
0
6
754395868
210
i.e. the results for the A and c arrrays are incorrect (and vary between
subsequent reruns).
After some digging in the libfortran source I think this is due to a bug in the
GFC_DTYPE_COPY_SETRANK macro introduced in
https://gcc.gnu.org/ml/gcc-patches/2018-01/msg00369.html, where line 421 of
libgfortran/libgfortran.h changed from
(a)->dtype = (((b)->dtype & ~GFC_DTYPE_RANK_MASK) | n ); \
to
(a)->dtype.rank = ((b)->dtype.rank | n ); \
The thing is that the "| n" makes sense in the previous case where the relevant
bits that store the previous rank have been zeroed out first. Now that rank is
a separate field, I think it should just be:
(a)->dtype.rank = n
Note that in the example above the even ranked arrays (B and D) do get the
correct result. This is because in those cases it happens to be that
new_rank==(old_rank | new_rank) (3 == 2 | 3 and 5 == 4 | 5). In the case of
arrays A and C however, we get 1 | 2 == 3 and 3 | 4 == 7, so that the rank of
the resulting spread array is too big, and the size calculations uses spurious
uninitialised array extents.
I have found, what I think are, related issues, when passing the result of
spread of a rank-3 array into a subroutine, where in the "internal pack"
routine would sometimes try to allocate a temporary that is way too big and
cause an out of memory error (similar to
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85816). Unfortunately this one
seems a little harder to reproduce in a small program.