You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

gfortran开启优化时intent(out)变量引发执行Bug的技术问询

Fortran代码优化异常与标准合规问题解析

问题背景

以下Fortran代码的预期输出值应接近2/3:

! File "buggy.f90".

program buggy

  implicit none
  integer, parameter :: n = 1000
  integer :: i, k
  logical :: c1, c2
  real, parameter :: q2 = 0.5
  real :: p2

  k = 0
  do i = 1, n
     c1 = .false.
     c2 = .false.
     do
!        write(*,*) c1
        if (c1 .or. c2) exit
        call proc(c1)
        if (c1) then
           k = k + 1
        else
           call random_number(p2)
           if (p2 >= q2) c2 = .true.
        end if
     end do
  end do
  write(*,*) "Result should be close to 2/3: ", real(k)/n

contains

  subroutine proc(c1)

    implicit none
    logical, intent(out) :: c1
!    logical, intent(inout) :: c1
    real, parameter :: q1 = 0.5
    real :: p1

    call random_number(p1)
    if (p1 >= q1) c1 = .true.
    
  end subroutine proc
  
end program buggy

对应的Makefile如下:

# Make file.

ifort = ifort13
gfortran = gfortran-11

all: buggy_ifort buggy_gfortran-O0 buggy_gfortran-O1 buggy_gfortran-O1_correct

buggy_ifort: buggy.f90
    @ echo "\nCompiling \"buggy_ifort\"."
    @ $(ifort) -warn declarations -check uninit -warn argument_checking -warn uninitialized -warn usage -implicitnone -warn uncalled -warn unused \
        -std95 -warn all -o buggy_ifort buggy.f90
    @ echo "\nDone."

buggy_gfortran-O0: buggy.f90
    @ echo "\nCompiling \"buggy_gfortran-O0\"."
    @ $(gfortran) -Wall -Wextra -pedantic -std=f95 -O0 -o buggy_gfortran-O0 buggy.f90
    @ echo "\nDone."

buggy_gfortran-O1: buggy.f90
    @ echo "\nCompiling \"buggy_gfortran-O1\"."
    @ $(gfortran) -Wall -Wextra -pedantic -std=f95 -O1 -o buggy_gfortran-O1 buggy.f90
    @ echo "\nDone."

buggy_gfortran-O1_correct: buggy.f90
    @ echo "\nCompiling \"buggy_gfortran-O1_correct\"."
    @ $(gfortran) -Wall -Wextra -pedantic -std=f95 -O1  \
        -o buggy_gfortran-O1_correct buggy.f90  \
        -fno-tree-ccp                       \
        -fno-tree-ch                        \
        -fno-tree-dominator-opts        \
        -fno-tree-fre               
    @ echo "\nDone."

# The output of buggy_gfortran-O1_correct is wrong if any of the -fno-tree-* above options is removed.

# The compilation of buggy_gfortran-O1_correct produces the following message:
# "   buggy.f90:18:12:
# 
#        18 |         if (c1) then
#           |            ^
#     Warning: ‘c1’ may be used uninitialized in this function [-Wmaybe-uninitialized]   ".

# This message disappears if -fno-tree-fre is removed.
# It is replaced by
# "   buggy.f90:16:19:
# 
#        16 |         if (c1 .or. c2) exit
#           |                   ^
#     Warning: ‘c1’ may be used uninitialized in this function [-Wmaybe-uninitialized]   "
# if -fno-tree-ch is removed.

# If -fno-tree-ch is replaced by -fno-inline-functions-called-once, or if these two options
# are present, the output is correct but all the warning messages disappear.

clean:
    @ rm buggy_ifort buggy_gfortran-O0 buggy_gfortran-O1 buggy_gfortran-O1_correct

问题现象

  • 使用ifort或gfortran -O0编译时,代码输出接近2/3的正确结果;
  • 使用gfortran -O1编译时,结果恒为1,且编译无任何警告;
  • 取消内循环开头write(*,*) c1的注释、将proc子程序的intent(out)改为intent(inout),或禁用-O1下的特定优化选项(如编译buggy_gfortran-O1_correct),代码可输出正确结果,但此时会出现c1可能未初始化的警告,且警告位置随优化选项变化而改变。

核心语义澄清

根据Fortran 95标准12.4.1.1节注12.17:INTENT(OUT)意味着调用过程后,参数的值完全由该过程的执行结果决定;若需要保留参数调用前的值,必须使用INTENT(INOUT)。用户最初的误解(认为intent(out)参数未被修改时会保留原值)不符合标准规范。

疑问解答

1. gfortran开启-O1及以上优化时结果错误的原因

gfortran在-O1及更高优化级别下,会严格依据Fortran标准对intent(out)的语义做激进优化:

  • 编译器认定intent(out)参数在子程序返回时必须被完全赋值,因此会假设proc一定会修改c1的值;
  • 基于这个假设,编译器会执行常量传播、死代码消除、函数内联等优化,错误改写代码逻辑:例如认为proc调用后c1必然为.true.(因为未赋值的intent(out)参数属于未定义行为,编译器可做任意假设),导致每次循环都执行k = k + 1,最终结果恒为1;
  • 禁用-fno-tree-ccp、-fno-tree-ch等特定优化选项时,编译器无法应用这些基于语义假设的激进优化,代码会按照用户错误的预期逻辑执行,从而输出正确结果。

2. 为何ifort和gfortran未发出预期警告

  • gfortran:默认-O1优化流程中,编译器基于intent(out)的语义假设,认为proc一定会给c1赋值,因此不会触发未初始化警告;只有禁用部分优化选项后,编译器才会检测到c1存在未被赋值的路径,进而发出警告;
  • ifort(13版本):旧版本编译器对intent(out)的语义检查和优化策略更宽松,没有严格执行标准中“intent(out)参数必须被完全覆盖”的要求,因此既不会触发警告,也不会做激进优化破坏原有逻辑,但这属于编译器的兼容行为,并不符合Fortran标准。

内容的提问来源于Stack Exchange,提问作者Michel Fioc

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 12:48:10