Why would code actively try to prevent tail-call optimization?

C++COptimizationCompiler OptimizationTail Call-Optimization

C++ Problem Overview


The title of the question might be a bit strange, but the thing is that, as far as I know, there is nothing that speaks against tail call optimization at all. However, while browsing open source projects, I already came across a few functions that actively try to stop the compiler from doing a tail call optimization, for example the implementation of CFRunLoopRef which is full of such hacks. For example:

static void __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__() __attribute__((noinline));
static void __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(CFRunLoopObserverCallBack func, CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) {
    if (func) {
        func(observer, activity, info);
    }
    getpid(); // thwart tail-call optimization
}

I would love to know why this is seemingly so important, and are there any cases were I as a normal developer should keep this is mind too? Eg. are there common pitfalls with tail call optimization?

C++ Solutions


Solution 1 - C++

My guess here is that it's to ensure that __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ is in the stack trace for debugging purposes. It has __attribute__((no inline)) which backs up this idea.

If you notice, that function just goes and bounces to another function anyway, so it's a form of trampoline which I can only think is there with such a verbose name to aid debugging. This would be especially helpful given that the function is calling a function pointer that has been registered from elsewhere and therefore that function may not have debugging symbols accessible.

Notice also the other similarly named functions which do similar things - it really looks like it's there to aid in seeing what has happened from a backtrace. Keep in mind that this is core Mac OS X code and will show up in crash reports and process sample reports too.

Solution 2 - C++

This is only a guess, but maybe to avoid an infinite loop vs bombing out with a stack overflow error.

Since the method in question doesn't put anything on the stack it would seem possible for the tail-call recursion optimization to produce code that would enter an infinite loop as opposed to the non-optimized code which would put the return address on the stack which would eventually overflow in the event of misuse.

The only other thought I have is related to preserving the calls on the stack for debugging and stacktrace printing.

Solution 3 - C++

One potential reason is to make debugging and profiling easier (with TCO, the parent stack frame disappears, which makes stack traces harder to make sense of.)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJustSidView Question on Stackoverflow
Solution 1 - C++mattjgallowayView Answer on Stackoverflow
Solution 2 - C++Andrew WhiteView Answer on Stackoverflow
Solution 3 - C++NPEView Answer on Stackoverflow