Hello!
Some time ago a reported a problem with global variables which are passed as argument to functions, and later, in some calls it was updated. See more here, https://community.intel.com/t5/Intel-Fortran-Compiler/common-variable-passed-as-argument-of-function-is-overoptimized/m-p/1573128
I have tried to implement a static check into LLVM Flang to verify our source code, and for testing of my implementation, I have created the following example:
==> alloc.f90 <==
integer function alloc(n)
use common_mod
implicit none
integer n
alloc = imem + n
imem = imem + n
print_char => print_B
end function alloc
==> common.f90 <==
module print_mod
implicit none
abstract interface
subroutine print_char_t()
end subroutine print_char_t
end interface
end module print_mod
module common_mod
use print_mod, only: print_char_t
implicit none
private
integer, pointer :: imem => null()
procedure(print_char_t), pointer :: print_char
public :: imem, print_char, print_A, print_B
contains
subroutine print_A()
print *, 'A'
end subroutine print_A
subroutine print_B()
print *, 'B'
end subroutine print_B
end module common_mod
==> main.f90 <==
program main
use common_mod
implicit none
integer, target :: real_mem
interface
subroutine test(i, f)
use print_mod
integer, pointer :: i
procedure(print_char_t), pointer :: f
end subroutine test
end interface
real_mem=0
imem=>real_mem
print_char=>print_A
call print_char()
call test(imem, print_char)
call print_char()
end program main
==> test.f90 <==
subroutine test(imem2, func)
use common_mod, only: imem
use print_mod
implicit none
integer, pointer :: imem2
procedure(print_char_t), pointer, intent(inout) :: func
integer :: res
integer, external :: alloc
print *, imem2, imem
call func()
res = alloc(8)
print *, imem2, imem
call func()
end subroutine test
However, it does not work well with ifx. With optimizations it gives a quite strange answer:
$ ifx common.f90 alloc.f90 test.f90 main.f90 -O3 && ./a.out
A
0 0
A
8 8
A
B
That, pointer to function was a constant in procedure test.
Without, it is OK:
$ ifx common.f90 alloc.f90 test.f90 main.f90 -O0 && ./a.out
A
0 0
A
8 8
B
B
gfortran -O0/-O3, flang-new (Clang 20.0Git) -O0/-O3, ifort -O0/-O3 generates the same output as ifx -O0.
So, it looks like a bug in ifx. And it would be wonderful to have the same (consistent) behaviour between ifx/gfortran/flang-new not only with pointer attribute, but also without it.
連結已複製
I combined the sources to one source to help with the analysis.
cat common.f90 alloc.f90 test.f90 main.f90 >> repro.f90
Indeed, ifx is behaving differently. Using opt-bisect-limit I've isolated the difference at opt phase
EarlyCSEPass
There is a LLVM bug report on this function but it seems completely unrelated to your test case.
EarlyCSEPass In my compiler is optimization 421. 420 naturally abides.
If you've not used opt-bisect-limit here is how to do it - binary search on the optimization numbers
ifx -O3 -mllvm -opt-bisect-limit=420 repro.f90 && ./a.out
this works, 420 friendly ( I can't make this stuff up)
and this does not
ifx -O3 -mllvm -opt-bisect-limit=421 repro.f90 && ./a.out
I have what I need to dig in deeper. This issue is also present in the 2025.0 branch and the main branch "2025.1".
I will now see if I can reduce this test case down some more.
I wrote it up, bug ID is CMPLRLLVM-62428
I was wrong on the optimization phase. It's actually further up the list.
O3 is
BISECT: running pass (86) InstCombinePass on test_
O2 also points to InstCombinePass on test_
BISECT: running pass (78) InstCombinePass on test_
Optimizer bug it appears, although it is possible we are lowering test() with a missing attribute from our front end. I'm letting the optimizer team take a look first.
Here's what I reduced this to:
module print_mod
implicit none
abstract interface
subroutine print_char_t()
end subroutine print_char_t
end interface
end module print_mod
module common_mod
use print_mod, only: print_char_t
implicit none
private
procedure(print_char_t), pointer :: print_char
public :: print_char, print_A, print_B
contains
subroutine print_A()
print *, 'A'
end subroutine print_A
subroutine print_B()
print *, 'B'
end subroutine print_B
end module common_mod
integer function alloc()
use common_mod
implicit none
alloc = 42
print_char => print_B
end function alloc
subroutine test( func)
use print_mod
implicit none
procedure(print_char_t), pointer, intent(inout) :: func
integer :: res
integer, external :: alloc
print*, "calling print A"
print*, " then call alloc() sets func ptr to print B. should see A followed by B "
call func()
!...rwg commenting out both of the following print
!...rwg statements will give wrong results for func ptr
!...rwg uncomment either print will set func ptr to B as it should be
!print*, "calling alloc, setting func ptr to print B"
res = alloc()
!print*, "done with alloc, call func to print B"
call func()
end subroutine test
program main
use common_mod
implicit none
interface
subroutine test(f)
use common_mod
use print_mod
procedure(print_char_t), pointer, intent(inout) :: f
end subroutine test
end interface
print_char=>print_A
print*, "calling test"
call test(print_char)
print*, "done with test, should be print B"
call print_char()
end program main
This is one of those tricky bugs. Notice my comments around the call to alloc() at line 46. There is a print before and a print after the call, commented out. If you uncomment one or the other RPINT the code works at O1-O3!
Not in the optimizer, it's in our Fortran frontend code. We know the problem, working on a fix.
I am curious, is this blocking you from moving your application to ifx? If so, what is the name of your project or application, assuming you can share that information.
Yep, it is blocking. The project is MRCC developed in BME. Project page is mrcc.hu
I have replaced all places where this bug happens, however, I do not know the status of my patches... If you interested, I can add you into GitHub repo. It is my own development ahead of master branch.
If somebody push Prof. Mihaly Kallay to use git, I will be extremely happy
