以下是我对事物的理解:
函数“f”在调用自身时是尾递归是它的最后一个动作。 通过形成循环而不是再次调用函数,可以显着优化尾递归;函数的参数就地更新,然后再次运行正文。这称为递归尾调用优化。
LLVM在使用fastcc,GHC或HiPE调用约定时实现递归尾调用优化。 http://llvm.org/docs/CodeGenerator.html#tail-call-optimization
我有一些问题: 让我们考虑一下这个愚蠢的例子:
int h(int x){
if (x <= 0)
return x;
else
h(x-1);
}
1)在他们的例子中,关键字“tail”在调用之前。在其他地方,我读到这个关键字是可选的。假设上面的函数被适当地转换为LLVM,最后几行需要
%x' = load *i32 %x
%m = tail call fastcc i32 @h(i32 %x')
ret %m
2)示例中inreg选项的含义是什么?
3)我不希望在整个地方执行尾调用优化,仅用于递归函数。有没有办法让LLVM只对递归函数进行优化(如果可用)?
答案 0 :(得分:2)
显然答案是肯定的。你必须改变h的定义才能看到这个(因为优化器太好了!它确定h是标识或返回0)。
考虑
int factorial (int x, int y){
if (x==0)
return y;
else
return factorial(x-1,y*x);
}
使用clang -S -emit-llvm编译,因此不执行任何优化。可以看出没有直接指定调用约定,这意味着默认调用约定足以支持尾递归优化(无论它是否支持尾调用都是一个不同的故事 - 知道它会很有趣,但我想这真是一个不同的问题。)
clang -S -emit-llvm发出的文件是main.s(假设factorial定义在main.c中)。如果你运行
opt -O3 main.s -S -o mainOpt.s
然后你可以看到尾部递归被消除了。有一个名为tailcallelim的优化可以打开为-O3。这很难分辨,因为帮助文件opt --help只表示-O3与gcc -O3类似。
关键是我们可以看到调用约定不需要为此指定。也许不需要fastcc,或者它可能是默认的?所以(1)部分回答;但是,我仍然不知道(2)或(3)。
答案 1 :(得分:2)
这里有两件不同的事情:
听起来你想要前者。