为什么这个尾调用递归fibonacci函数会破坏gcc -O2?

时间:2014-07-02 17:25:33

标签: c gcc recursion fibonacci tail-recursion

我实现了以下尾调用递归斐波那契函数来尝试gcc的尾调用优化(as mentioned here):

#include <stdint.h>
uint64_t fib_tc( uint8_t n) {
  uint64_t fib_helper(uint8_t n, uint64_t acc, uint64_t prev) {
    if(n == 0) 
      return acc;
    else
      return fib_helper( n-1, acc+prev, acc);
  }
  fib_helper(n,1,0);
}

最初,我没有指定-O级别(因此默认为1)。该功能与-O1一起按预期工作。后来,我尝试使用-O2进行编译,但这破坏了函数 - 它总是返回0.使用-O3时的结果相同。

我用-O2看了一下程序集输出(使用:gcc -O2 -std = gnu99 -S fib_tc.c),看看下面的程序集:

    .file   "fib_tc.c"
    .text
    .p2align 4,,15
    .type   fib_helper.1752, @function
fib_helper.1752:
.LFB1:
    .cfi_startproc
    testb   %dil, %dil
    movq    %rsi, %rax
    jne .L4
    rep ret
    .p2align 4,,10
    .p2align 3
.L4:
    leaq    (%rsi,%rdx), %rsi
    subl    $1, %edi
    movq    %rax, %rdx
    movzbl  %dil, %edi
    jmp fib_helper.1752
    .cfi_endproc
.LFE1:
    .size   fib_helper.1752, .-fib_helper.1752
    .p2align 4,,15
    .globl  fib_tc
    .type   fib_tc, @function
fib_tc:
.LFB0:
    .cfi_startproc
    testb   %dil, %dil
    jne .L11
    ret
    .p2align 4,,10
    .p2align 3
.L11:
    subl    $1, %edi
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
    movl    $1, %edx
    movzbl  %dil, %edi
    movl    $1, %esi
    call    fib_helper.1752
    addq    $8, %rsp
   .cfi_def_cfa_offset 8
    ret
    .cfi_endproc
.LFE0:
    .size   fib_tc, .-fib_tc
    .ident  "GCC: (GNU) 4.8.2 20131212 (Red Hat 4.8.2-7)"
    .section    .note.GNU-stack,"",@progbits

我注意到-O2似乎已经完成了TCO(注意上面的一行: jmp fib_helper.1752 )。但是,这是始终返回0的非工作版本。

然后我使用-O1生成程序集以检查差异(gcc -O1 -std = gnu99 -S fib_tc.c -o fib_tc1.S):

    .file   "fib_tc.c"
    .text
    .type   fib_helper.1752, @function
fib_helper.1752:
.LFB1:
    .cfi_startproc
    movq    %rsi, %rax
    testb   %dil, %dil
    je  .L6
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
    leaq    (%rsi,%rdx), %rax
    subl    $1, %edi
    movzbl  %dil, %edi
    movq    %rsi, %rdx
    movq    %rax, %rsi
    call    fib_helper.1752
    addq    $8, %rsp
    .cfi_def_cfa_offset 8
.L6:
    rep ret
    .cfi_endproc
.LFE1:
    .size   fib_helper.1752, .-fib_helper.1752
    .globl  fib_tc
    .type   fib_tc, @function
fib_tc:
.LFB0:
    .cfi_startproc
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
    movzbl  %dil, %edi
    movl    $0, %edx
    movl    $1, %esi
    call    fib_helper.1752
    addq    $8, %rsp
    .cfi_def_cfa_offset 8
   ret
   .cfi_endproc
.LFE0:
    .size   fib_tc, .-fib_tc
    .ident  "GCC: (GNU) 4.8.2 20131212 (Red Hat 4.8.2-7)"
   .section .note.GNU-stack,"",@progbits

这个版本(有效)似乎没有经过尾调用优化(基于上面对fib_helper的调用)。

如上所示,我在Fedora 20 Linux(64位)上使用gcc 4.8.2。

在我得出gcc与TCO相关的错误之前,我想检查以确保我在代码或分析中没有做错。

编辑:正如评论中指出的那样,嵌套函数可能存在问题,我也尝试使用如下的单独函数,并且总是返回0:

uint64_t fib_helper(uint8_t n, uint64_t acc, uint64_t prev) {
  if(n == 0) 
    return acc;
  else
    return fib_helper( n-1, acc+prev, acc);
}

uint64_t fib_tc( uint8_t n) {
  fib_helper(n,1,0);
}

EDIT2:正如下面的评论所指出的,我错过了回报(我猜我最近做了太多的OCaml :)应该是:

uint64_t fib_tc( uint8_t n) {
  uint64_t fib_helper(uint8_t n, uint64_t acc, uint64_t prev) {
    if(n == 0) 
      return acc;
    else
      return fib_helper( n-1, acc+prev, acc);
  }
  return fib_helper(n,1,0);
}

0 个答案:

没有答案