Strange initialization behavior for array constructor implied by gfortran

Say I have 3 double precision arrays,

real*8, dimension(n) :: x, y, z 

which are initialized as

 x = 1. y = (/ (1., i=1,n) /) z = (/ (1. +0*i, i=1,n) /) 

They must initialize all elements of all arrays to 1 . In ifort (16.0.0 20150815), this works as intended for any n within the range of declared precision. That is, if we initialize n as

 integer*4, parameter :: n 

then, until n < 2147483647 , initialization works as for all declarations.

In gfortran (4.8.5 20150623 Red Hat 4.8.5-16), initialization fails with error y (understanding array with constant argument) until n>65535 , independent of its accuracy. AFAIK, 65535 - maximum a unsigned short int , aka unsigned int*2 , which is within the range of integer*4 .

The following is the MWE:

 program test implicit none integer*4, parameter :: n = 65536 integer*4, parameter :: m = 65535 real*8, dimension(n) :: x, y, z real*8, dimension(m) :: a, b, c integer*4 :: i print *, huge(n) x = 1. y = (/ (1., i=1,n) /) z = (/ (1.+0*i, i=1,n) /) print *, x(n), y(n), z(n) a = 1. b = (/ (1., i=1,m) /) c = (/ (1.+0*i, i=1,m) /) print *, a(m), c(m), c(m) end program test 

Compiling with gfortran test.f90 -o gfortran_test ( gfortran test.f90 -o gfortran_test ) outputs:

  2147483647 1.0000000000000000 0.0000000000000000 1.0000000000000000 1.0000000000000000 1.0000000000000000 1.0000000000000000 

Compiling with ifort ( ifort test.f90 -o ifort_test ) outputs:

 2147483647 1.00000000000000 1.00000000000000 1.00000000000000 1.00000000000000 1.00000000000000 1.00000000000000 

What gives?

+5
source share
1 answer

There is really a big difference in how the compiler views array constructors. For n<=65535 there is an actual array from [1., 1., 1., ...] stored in the object file (or in some intermediate representations).

For a larger array, the compiler generates a loop:

  (*(real(kind=8)[65536] * restrict) atmp.0.data)[offset.1] = 1.0e+0; offset.1 = offset.1 + 1; { integer(kind=8) S.2; S.2 = 0; while (1) { if (S.2 > 65535) goto L.1; y[S.2] = (*(real(kind=8)[65536] * restrict) atmp.0.data)[S.2]; S.2 = S.2 + 1; } L.1:; } 

it seems to me that at first it sets only one element of a temporary array, and then copies the temporary array (mostly undefined) to y . And this is wrong. Valgrind also reports the use of uninitialized memory.

For the real default we have

  while (1) { if (shadow_loopvar.2 > 65536) goto L.1; (*(real(kind=4)[65536] * restrict) atmp.0.data)[offset.1] = 1.0e+0; offset.1 = offset.1 + 1; shadow_loopvar.2 = shadow_loopvar.2 + 1; } L.1:; { integer(kind=8) S.3; S.3 = 0; while (1) { if (S.3 > 65535) goto L.2; y[S.3] = (*(real(kind=4)[65536] * restrict) atmp.0.data)[S.3]; S.3 = S.3 + 1; } L.2:; } 

Now we have two loops: one sets the entire temporary array, and the second copies it to y , and everything is fine.

Conclusion: compiler error.

The issue was fixed by the GCC developers who read this question. The error is tracked by https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84931

They also determined that the problem was with type conversion. The constructor has a default precision limit of 1. and with a single precision array there is no type conversion, but for a double precision array there is some type conversion. This caused a difference in the two cases.

+4
source

Source: https://habr.com/ru/post/1275928/


All Articles