gfortran是否支持尾部呼叫消除?

时间:2016-09-05 12:20:12

标签: fortran gfortran tail-recursion

我做了这个小程序来测试,如果gfortran做尾调用消除:

program tailrec
implicit none

print *, tailrecsum(5, 0)

contains

recursive function tailrecsum (x, running_total) result (ret_val)
    integer, intent(in) :: x
    integer, intent(in) :: running_total
    integer             :: ret_val

    if (x == 0) then
        ret_val = running_total
        return
    end if
    ret_val = tailrecsum (x-1, running_total + x)
end function tailrecsum

end program

要检查,我使用-S选项编译它,以查看说明。这里是tailrecsum函数的行:

tailrecsum.3429:
.LFB1:
    .cfi_startproc
    movl    (%rdi), %eax
    testl   %eax, %eax
    jne .L2
    movl    (%rsi), %eax
    ret
    .p2align 4,,10
    .p2align 3
.L2:
    subq    $24, %rsp
    .cfi_def_cfa_offset 32
    leal    -1(%rax), %edx
    addl    (%rsi), %eax
    leaq    8(%rsp), %rdi
    leaq    12(%rsp), %rsi
    movl    %edx, 8(%rsp)
    movl    %eax, 12(%rsp)
    call    tailrecsum.3429
    addq    $24, %rsp
    .cfi_def_cfa_offset 8
    ret
    .cfi_endproc

最后,我看到call tailrecsum.3429,因此,认为没有尾部呼叫消除。当我使用-O2-O3-foptimize-sibling-calls时也是如此。 那么,gfortran不支持这个还是我的代码问题?

1 个答案:

答案 0 :(得分:2)

确实支持它。避免许多非常微妙的陷阱会损害尾部调用优化是非常棘手的。

考虑这个版本:

> gfortran -O2 -frecursive tailrec.f90 
> ./a.out 
   125000000250000000

使用5000000次迭代,它会在没有TCO的情况下明显地破坏堆栈,但它不会:

-fdump-tree-optimized

您可以使用 <bb 6>: _25 = _5 + -3; D.1931 = _25; _27 = _18 + _20; D.1930 = _27; ret_val_28 = tailrecsum (&D.1931, &D.1930); D.1930 ={v} {CLOBBER}; D.1931 ={v} {CLOBBER}; <bb 7>: # _29 = PHI <_20(5), ret_val_28(6)> <bb 8>: # _22 = PHI <_11(4), _29(7)> <bb 9>: # _1 = PHI <ret_val_7(3), _22(8)> return _1; } 更轻松地检查编译器的功能。老实说,我甚至懒得试图理解你的装配输出。 X86程序集对我来说太神秘了,我的简单大脑只能处理某些RISC。

您可以看到在原始版本中调用下一次迭代后仍有很多事情发生:

D.193x

我不是GIMPLE的专家,但是PHI操作肯定与放置在调用的堆栈上的临时表达式相关联。

business_day,commodity,total,delivery,total_lots . . 20160831,CTC,,201710,10 20160831,CTC,,201711,10 20160831,CTC,,201712,10 20160831,CTC,Total,,385 20160831,HTC,,201701,30 20160831,HTC,,201702,30 . . 操作然后根据if语句中实际采用的分支(https://gcc.gnu.org/onlinedocs/gccint/SSA.html)找到实际返回的返回值版本。

正如我所说,将代码简化为正确的形式有时很难将gfortran执行尾调用优化。