Tail-Call Optimization in C: A Recent Arrival

Tail-call optimization in C is relatively recent

A LWN reader reflects on the history of tail-call optimization in C, noting that it wasn't always available. In 1994, compilers didn't optimize tail calls due to calling convention constraints. Mark Probst's 2001 GCC implementation had limitations, like no indirect calls. Recent tests show GCC and Clang now handle tail calls well, enabling techniques like the 100,000 code snippets used in Copy-and-Patch Compilation, a leap from Gforth's 2,000. The author congratulates Python for adopting it first.

Actually tail calls in C have not been around forever.
  1. drdexebtjl

    Unless the language can guarantee TCO, I don’t feel comfortable writing tail recursive code and being at the compiler’s/interpreter’s mercy.

    I think the framing of TCO as an optimization has been very unfortunate.

  2. torginus

    What practical patterns are enabled by TCO in C? My impression is that every tail call can written as a loop much more naturally. Tail calls are important in functional languages where you don't have mutable loop variables.

    And imo they are an ugly hack even there - one of the few core constructs where its readily apparent you're not programming an abstract machine but a real, and limited computer. For example the most natural way to write factorial:

    let rec factorial n = if n <= 1 then 1 else n * factorial (n - 1)

    is not tail recursive, and will overflow if the compiler fails to optimize.

  3. kenjin4096

    I think Anton is replying to me in that LWN article IIRC. I personally didn't know C only had tail calls that late and learnt something new there!

    On the other hand, I am pretty new to the compiler space myself, and I count early 2000s as a pretty long time ago, though again it is not that far back considering how long other language implementations had tail calls like in ML or variants since 1980-90s.

  4. amavect

    I recently played around with what I call "manual tail-call optimization": transform a tail call to a goto to the beginning of the function. Check it out: https://godbolt.org/z/3fY1v1oeW

    int factorial_loop_iterative(int n, int a){

    while(n > 0){

    a = a * n;

    n = n - 1;

    }

    return a;

    }

    int factorial_loop_recursive(int n, int a){

    if(n > 0){

    return factorial_loop_recursive(n - 1, a * n);

    }else{

    return a;

    }

    }

    int factorial_loop_manual(int n, int a){

    tailcall:

    if(n > 0){

    a = a * n;

    n = n - 1;

    goto tailcall;

    }else{

    return a;

    }

    }

    int (*factorial_loop)(int n, int a) = factorial_loop_manual;

    int factorial(int n){

    return factorial_loop(n, 0);

    }

    I recommend against, of course! Incorrectly sequencing the manual version results in bugs (swap the assignment for n and a), which the recursive version doesn't need to care about.

  5. mmsc

    and TCO was added then removed from js! https://stackoverflow.com/a/54721813

    This leads to fun stack-overflow bugs too in a lot of js code (one solution is to flatten: https://joshua.hu/javascript-infinite-tail-call-recursion-st...)

  6. throwaway81523

    GCC has had TCO since the 1980s I'm pretty sure. Since then it's been extended to work in more contexts.

  7. nyeah

    >That quote is the article, and it's a little surprising that it's buried so far into the content

    Is it really surprising in 2026? Today's online writing style is not primarily designed to communicate. It's designed to keep the reader 'engaged' for as long as possible. The reader's time is a resource to be extracted.

    I'm absolutely not poking this author individually. It's the writing style of the net.

  8. swiftcoder

    > In 2001 Mark Probst implemented tail-call optimization in GCC

    MSVC didn't add tail-call optimisation until sometime in the 2010s, IIRC.

    I distinctly remember sending a tail-recursive C++ program to someone who developed on Windows, and it crashing, in the late mid-to-late 2000s.

More from this day

2026-08-10