Optimization ackermann的gcc转换过程

Optimization ackermann的gcc转换过程,optimization,gcc,Optimization,Gcc,gcc(我在Mac和Linux上使用-O3标志尝试了4.7.2)将ackermann函数优化为一个具有大本地堆栈的调用。下面是Ackermann代码示例: int ack(int m,int n){ if(m == 0) return n+1; if(n == 0) return ack(m-1,1); return ack(m-1,ack(m,n-1)); } 反汇编时,对ack函数只有一个递归调用,而不是两个调用(我无法分析正在发生的事情-ack现在由gcc转换为一个具有8个参

gcc
(我在Mac和Linux上使用
-O3
标志尝试了
4.7.2
)将ackermann函数优化为一个具有大本地堆栈的调用。下面是Ackermann代码示例:

int ack(int m,int n){
  if(m == 0) return n+1;
  if(n == 0) return ack(m-1,1);
  return ack(m-1,ack(m,n-1));
}

反汇编时,对
ack
函数只有一个递归调用,而不是两个调用(我无法分析正在发生的事情-
ack
现在由gcc转换为一个具有8个参数的函数,本地堆栈为49 int和9 char)。我试图查找gcc对单个调用优化Ackermann函数所做的转换,但没有发现任何有趣的东西。我将非常欣赏关于gcc执行什么主要转换来将深度递归Ackermann转换为单个递归调用的指针。LLVM gcc(我在mac上尝试了v4.2)还没有将其简化为单个递归调用,并且使用
-O3
标志时速度慢了4倍。这个优化看起来很有趣。

第一步是尾部调用消除。GCC在大多数优化级别上都这样做。本质上,所有处于尾部位置的函数调用都转换为goto,如下所示:

int ack(int m, int n) {
begin:
  if (m == 0) return n + 1;
  if (n == 0) { m -= 1; n = 1; goto begin; }
  n = ack(m, n-1); m -= 1; goto begin;
}

现在只剩下一个递归调用,GCC(仅在-O3级别)将其内联几次迭代。结果是你看到了一个巨大的怪物。

你还应该看到这个系统和带有-O1和-O2的gcc版本的速度下降了多少。手册页列出了每一步所做的大量(如果不是全部的话)优化,因此,如果是其中一步之间的优化,您可以缩小范围。用转到函数开头来替换对函数本身的调用并不难(没有检查是否发生了这种情况)在终端递归的情况下。@Marglisse,Ackermann是深度递归函数的特例。我知道其他递归函数(如Fibonacci)的尾部递归技术。将Fibonacci转换为尾部递归函数非常简单,但据我所知,不是Ackermann。这就是为什么还有一个调用而不是0。。。在最后一次调用ack之后,您不需要返回调用函数,因此可以用跳转替换调用。这似乎是兄弟调用优化。在say-O1中打开just fooptimize sibling calls标志,复制此优化,然后关闭它,将其关闭。如果有人对如何在Ackermann函数的上下文中完成此转换有更多的指示,请随时回答。