clang 1.1和1.0生成的尾调用(llvm 2.7和2.6)

时间:2010-05-24 08:16:07

标签: llvm clang

使用clang -O2(或使用online demo)编译下一段代码后:

#include <stdio.h>
#include <stdlib.h>

int flop(int x);
int flip(int x) {
  if (x == 0) return 1;
  return (x+1)*flop(x-1);
}
int flop(int x) {
  if (x == 0) return 1;
  return (x+0)*flip(x-1);
}

int main(int argc, char **argv) {
  printf("%d\n", flip(atoi(argv[1])));
}

我正在flip中获取llvm程序集的下一个片段:

bb1.i:                                            ; preds = %bb1
  %4 = add nsw i32 %x, -2                         ; <i32> [#uses=1]
  %5 = tail call i32 @flip(i32 %4) nounwind       ; <i32> [#uses=1]
  %6 = mul nsw i32 %5, %2                         ; <i32> [#uses=1]
  br label %flop.exit

我认为tail call意味着丢弃当前堆栈(即返回将返回到上一帧,因此下一条指令应为ret %5),但根据此代码,它将执行mul为了它。在原生程序集中,有一个简单的call没有尾部优化(即使有适当的标志为llc)

可以用sombody解释为什么clang生成这样的代码?

我也无法理解为什么llvm有tail call,如果它只是检查下一个ret是否会使用prev call的结果,然后进行适当的优化或生成本机等效的尾调指令?

1 个答案:

答案 0 :(得分:3)

查看'call' instruction中的LLVM Assembly Language Reference Manual。它说:

  

可选的“tail”标记表示被调用者函数不访问调用者中的任何allocas或varargs。请注意,即使在转发指令之前没有发生呼叫,也可能将呼叫标记为“尾部”。

可能是Clang中的一个LLVM优化传递分析了被调用者是否访问调用者中的任何allocas或varargs。如果没有,则传递将调用标记为尾调用,并让LLVM的另一部分找出如何处理“尾部”标记。也许这个函数现在不能成为一个真正的尾部调用,但是在进一步转换后它可能是。我猜这样做是为了使通行证的排序不那么重要。