Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

LLVM tail call optimization

Here is my understanding of things:

A function "f" is tail recursive when calling itself is its last action. Tail-recursion can be significantly optimized by forming a loop instead of calling the function again; the function's parameters are updated in place, and the body is ran again. This is called recursive tail call optimization.

LLVM implements recursive tail call optimization when using fastcc, GHC, or the HiPE calling convention. http://llvm.org/docs/CodeGenerator.html#tail-call-optimization

I have some questions: Let's consider the silly example:

int h(int x){
  if (x <= 0)
    return x;
  else
    h(x-1);
}

1) In their example, the keyword "tail" preceeds call. Elsewhere I read that this keyword is optional. Suppose the function above is translated to LLVM appropriately, do the last few lines need to be

%x' = load *i32 %x
%m = tail call fastcc i32 @h(i32 %x')
ret %m

2) What is the meaning of the inreg option in their example?

3) I would not want to perform tail call optimizations all over the place, only for recursive functions. Is there a way I can get LLVM to only perform the optimization (when available) for recursive functions?

like image 453
Jonathan Gallagher Avatar asked Sep 04 '13 00:09

Jonathan Gallagher


People also ask

What is tail call in LLVM?

Tail call optimization is a technique where the callee reuses the stack of the caller instead of adding a new stack frame to the call stack, hence saving stack space and the number of returns when dealing with mutually recursive functions.

What languages support tail call optimization?

This kind of function call is called a tail call, and languages like Haskell, Scala, and Scheme can avoid keeping around unnecessary stack frames in such calls. This is called tail call optimization (TCO) or tail call elimitation.

What is LLVM optimization?

DESCRIPTION. The opt command is the modular LLVM optimizer and analyzer. It takes LLVM source files as input, runs the specified optimizations or analyses on it, and then outputs the optimized file.

Does C++ support tail call optimization?

Tail call optimisation isn't in the C++ standard. Apparently, some compilers, including MS Visual Studio and GCC, do provide tail call optimisation under certain circumstances (when optimisations are enabled, obviously).


2 Answers

Apparently the answer is yes. You have to change the definition of h to see this (because the optimizer is too good! It figures out that h is either the identity or returns 0).

Consider

int factorial (int x, int y){
  if (x==0)
    return y;
  else
    return factorial(x-1,y*x);
}

Compiled with clang -S -emit-llvm, so that no optimization is performed. One sees that no calling conventions are directly specified, which means that the default calling convention is enough to support tail recursion optimization (whether or not it supports tail calling in general is a different story -- it would be interesting to know, but I guess that is really a different question).

The file emitted by clang -S -emit-llvm is main.s (assuming the factorial definition is in main.c). If you run

opt -O3 main.s -S -o mainOpt.s

then you can see that the tail recursion is eliminated. There is an optimization called tailcallelim which may be turned on as -O3. It's hard to tell because the help file, opt --help, says only that -O3 is similar to gcc -O3.

The point is that we can see that the calling convention does not need to specified for this. Maybe fastcc is not needed, or maybe it is default? So (1) is partially answered; however, I still do not know (2) or (3).

like image 200
Jonathan Gallagher Avatar answered Oct 01 '22 15:10

Jonathan Gallagher


There are two different things here:

  1. You can optimise self-recursive tail calls into a loop. LLVM provides an optimisation pass that does this. It does not require a specific calling convention.
  2. You can use a different calling convention to guarantee tail call optimisation of all calls in tail position (i.e. including calls to other functions). With LLVM, you need to specify the calling convention on the function, on the call instruction and mark the call as a tail call.

Sounds like you want the former.

like image 45
J D Avatar answered Oct 01 '22 15:10

J D