Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

ifx 2023.2.0 wrong optimization

Frank_R_1
Beginner
853 Views

Dear Support,

 

We use Intel oneAPI 2023.2.0 on Windows/Linux and have found a compiler bug.

Please see the attached zip file for a reproducer. (includes the source code, compiler options, and disassembly outputs)

ifx does wrong optimization with -O3 but correct optimization with -O1 option.

ifort does it correct regardless of -O3 or -O1

Also our big pool of legacy fortran77 code with a lot of regressiontest run bit identical on both compilers ifort and ifx in debug mode.

It would be nice if this bug can be fixed so that we can transit from ifort to ifx in the future.

 

Thanks in advance and best regards

Frank

0 Kudos
1 Solution
TobiasK
Moderator
673 Views

@Frank_R_1


I discussed this issue with the team, and we won't 'fix' it since it is undefined behavior.

The work around of declaring the array with size two might also break in the future, however, the recommendation is to stick with that for now.


If the program flow is always like this:


integer*8 add,off

integer array(1)

#c routine for memory allocation

call allocation(add,off,array)

#work with array

call func(array(1+off))

#c routine to free memory

free(add)

so only performance relevant operations are done inside func, you can add asynchronous without performance impact since inside func you don't need to add the attribute.

Another way would be do call a dummy function dummy(intarr) between each assignment, or just simply put all the assignments inside another function, but again those workarounds require the optimizer not seeing the declaration of intarr(1).



View solution in original post

0 Kudos
8 Replies
TobiasK
Moderator
744 Views

Hi Frank,


unfortunately, I don't think this is valid code.


Even the Fortran66 standard says:

Page 17.:

No array element name may contain a subscript that, during execution of the executable program, assumes a value less than one or larger than the maximum length specified in the array declarator.

https://archive.org/details/ansi-x-3.9-1966-fortran-66/page/16/mode/2up


You can add volatile or asynchronous to the intarr declaration which prevent optimizations on intarr.


Best

Tobias






0 Kudos
Frank_R_1
Beginner
730 Views

Hi,

 

Thank you for your reply. We use classic Intel Fortran compiler ifort over 20 years now and our code base is a huge amount of Fortran77 files. On Linux and Windows we used the following trick to dynamically allocate memory.

 

integer*8 add,off

integer array(1)

#c routine for memory allocation

call allocation(add,off,array)

#work with array

call func(array(1+off))

#c routine to free memory

free(add)

 

We never had a problem with this construction concerning ifort on Windows and Linux.

Also ifx does this right (as ifort) in many locations in our code, but sometimes it optimizes things away with O3 like in the example I gave.

Even with intarr(2) it does work with ifx!

Why is ifx not consistent with ifort?

How can we safely use volatile on these arrays but also get optimization performance benefits?

 

Best regards

Frank

 

0 Kudos
jimdempseyatthecove
Honored Contributor III
733 Views

Newer Versions of Fortran support array descriptors which permit arrays defined with negative indices. This does require the descriptor be visible.

!  NegIndex.f90 

module mod_foo
    interface
        subroutine fooA(fooarg)
            real :: fooarg(:,:)
        end subroutine fooA
        subroutine fooB(fooarg)
            real :: fooarg(:,:)
        end subroutine fooB
    end interface
end module mod_foo
    
program NegIndex
    use mod_foo
    implicit none
    real :: array(-10:10, 3)
    print *, lbound(array, 1), ubound(array, 1), size(array, 1)
    call fooA(array)
    call fooB(array)
end program NegIndex

subroutine fooA(arg)
    use mod_foo
    real :: arg(:,:)
    print *, lbound(arg, 1), ubound(arg,1), size(arg, 1)
    end subroutine fooA
    
subroutine fooB(arg)
    use mod_foo
    real :: arg(-10:,:)
    print *, lbound(arg, 1), ubound(arg,1), size(arg, 1)
end subroutine fooB

         -10          10          21
           1          21          21
         -10          10          21

Jim Dempsey

 

0 Kudos
TobiasK
Moderator
717 Views

@jimdempseyatthecove


your code is completely different from the code Frank uses.

You declare an array and always stay inside the declaration.

Frank's code does some C magic on the hood and changes the declaration, for the compiler it looks like out of bound access to a array of length 1 and the compiler simply removes all but the first and last assignment to this length 1 array. gfortran does the same, and I could bet NAGfortran also does the same but I have not tested it yet.


@Frank_R_1

I will discuss with the developers and see if there is anything we can do.


0 Kudos
jimdempseyatthecove
Honored Contributor III
710 Views

Then Frank needs to specify the index ranges for himself.

module mod_foo
    interface
        subroutine fooA(fooarg)
            real :: fooarg(:,:)
        end subroutine fooA
        subroutine fooB(fooarg)
            real :: fooarg(:,:)
        end subroutine fooB
    end interface
end module mod_foo
    
program NegIndex
    use mod_foo
    implicit none
    real :: array(-10:10, 3)
    print *, lbound(array, 1), ubound(array, 1), size(array, 1)
    call fooA(array)
    call fooB(array)
    call fooNoInterface(array, -3, size(array, 1) - 3 - 1, size(array,2))
end program NegIndex

subroutine fooA(arg)
    use mod_foo
    real :: arg(:,:)
    print *, lbound(arg, 1), ubound(arg,1), size(arg, 1)
    end subroutine fooA
    
subroutine fooB(arg)
    use mod_foo
    real :: arg(-10:,:)
    print *, lbound(arg, 1), ubound(arg,1), size(arg, 1)
    end subroutine fooB
    
subroutine fooNoInterface(arg, lb1, ub1, sz2)
    integer :: lb1, ub1, sz2
    real :: arg(lb1:ub1, 0:sz2-1)
    print *, lbound(arg, 1), ubound(arg,1), size(arg, 1)
end subroutine fooNoInterface

         -10          10          21
           1          21          21
         -10          10          21
          -3          17          21

Jim Dempsey

0 Kudos
TobiasK
Moderator
674 Views

@Frank_R_1


I discussed this issue with the team, and we won't 'fix' it since it is undefined behavior.

The work around of declaring the array with size two might also break in the future, however, the recommendation is to stick with that for now.


If the program flow is always like this:


integer*8 add,off

integer array(1)

#c routine for memory allocation

call allocation(add,off,array)

#work with array

call func(array(1+off))

#c routine to free memory

free(add)

so only performance relevant operations are done inside func, you can add asynchronous without performance impact since inside func you don't need to add the attribute.

Another way would be do call a dummy function dummy(intarr) between each assignment, or just simply put all the assignments inside another function, but again those workarounds require the optimizer not seeing the declaration of intarr(1).



0 Kudos
Frank_R_1
Beginner
610 Views

Hi Tobias,

 

Thank you for your detailed answer. I think when we transit to ifx we will take the "volatile" path:

 

integer intarr(1)

volatile intarr

 

We have hundreds of code locations but I think this will fix the problem for us.

The only thing which bothers me, is that ifort and ifx do not behave the same.

 

Best regards

Frank

0 Kudos
TobiasK
Moderator
595 Views

Hi Frank,


glad that volatile resolves your problem.


IFX is a different compiler than IFORT, so there is no guarantee that both behave the same for an undefined behavior.


Best

Tobias


0 Kudos
Reply