После компиляции следующий фрагмент кода с помощью clang -O2 (или с онлайн-демонстрацией ):
#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])));
}
Я получаю следующий фрагмент сборки llvm в flip
:
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)
Может кто-нибудь объяснить, почему clang генерирует такой код?
Также я не могу понять, почему у llvm есть tail call
, если он может просто проверить, что следующий ret
будет использовать результат prev call
, а затем выполнить соответствующую оптимизацию или сгенерировать собственный эквивалент команды tail-call? 1021 *