Surprising results with aliasing

While I post this on 1 april, it is not a joke. I was inspired by the recent discussion on the “memory-safeness” of Fortran: what are the consequences of aliasing? So, I tried a small program with two different compilers and various optimisation flags. The results are quite varied, though in most cases I can understand them. There is one case where I cannot. The outcome of the attached program is this:

              Input:    1    2    3    4    5    6    7    8    9   10
Correct call FORTRAN    0    2    4    6    8   10   12   14   16   18
         FORTRAN 77:    0    2    4    6    8   16   12   14   16   32
      Assumed shape:    0    2    4    6    8   16   12   14   16   32
   DO-loops, intent:    0    2    4    6    8   16   12   14   16   32
    Array operation:    0    2    4    6    8   16   12   14   16   32
Whole array, intent:    0    2    4    6    8   16   12   14   16   32
 Problem: overlap!
            Checked:    0    2    4    6    8   16   12   14   16   32

The second line is the expected outcome, all following lines show the consequences of aliasing. Look at the result for input 6 and 10 - I do not get why they are not the double value. This happens with Intel Fortran oneAPI’s ifort (default options) and gfortran with -O3. If I use ifx, I get:

              Input:    1    2    3    4    5    6    7    8    9   10
Correct call FORTRAN    0    2    4    6    8   10   12   14   16   18
         FORTRAN 77:    0    2    4    6    8   16   12   14   16   32
      Assumed shape:    0    2    4    8   16   32   64  128  256  512
   DO-loops, intent:    0    2    4    8   16   32   64  128  256  512
    Array operation:    0    2    4    8   16   32   64  128  256  512
Whole array, intent:    0    2    4    8   16   32   64  128  256  512
 Problem: overlap!
            Checked:    0    2    4    8   16   32   64  128  256  512

Similar for gfortran without options. The resulting geometric series is easy to understand (see the source code), but the deviations in the FORTRAN 77 row are not.

I have attached the program’s source code.

alias_prog.f90 (1.4 KB)
aliasing.f90 (2.2 KB)

(Oh, for extra fun: uncommenting the first assignment to b(1) gives even more variations)

1 Like

Isn’t this the result of caching? The actual operations are something like

  integer :: a(10) , cache(4)
  a = [1,2,3,4,5,6,7,8,9,10]
  cache = a(1:4)               !   cache = [1,2,3,4]
  a(2:5) = 2*cache             !   a= [1,2,4,6,8,6,7,8,9,10]
  cache=a(5:8)                 !   cache = [8,6,7,8]  
  a(6:9) = 2*cache             !   a= [1,2,4,6,8,16,12,14,16,10]
  cache=a(9:10)                !   cache = [16,10]
  a(10) 2*cache(1)             !   a = (1,2,4,6,8,16,12,14,16,32]
  a(1) = 0                     !   a = (0,2,4,6,8,16,12,14,16,32]

So this is a case where Fortran is getting the “wrong” result because it is allowed to optimise on the assumption that arguments are not aliased.

Nice analysis, yes, caching like this came to mind, but I did not try to figure out how it would work.

Aside: if you uncomment the first assignment b(1) = 0, then some compier/option combinations give all zeroes and some do not. Apparently, the compiler then recognises that one of them is superfluous (the first) and the geometric series reappears.