Почему код активно пытается предотвратить оптимизацию хвостового вызова?

Название вопроса может быть немного странным, но дело в том, что, насколько я знаю, ничего не говорит о оптимизации хвостового вызова вообще. Однако, просматривая проекты с открытым исходным кодом, я уже сталкивался с несколькими функциями, которые активно пытаются остановить компилятор от оптимизации хвостового вызова, например, реализация CFRunLoopRef, который полон таких хаков. Например:

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
}

Я хотел бы знать, почему это так важно, и есть ли какие-то случаи, когда я, как нормальный разработчик, должен держать это в виду тоже? Например. существуют ли общие проблемы с оптимизацией хвостового вызова?

Ответ 1

Это только предположение, но, возможно, чтобы избежать бесконечного цикла против взрыва с ошибкой.

Так как данный метод не помещает ничего в стек, казалось бы, для оптимизации рекурсии хвостового вызова можно создать код, который будет вводить бесконечный цикл, а не неоптимизированный код, который бы поместил обратный адрес на стек, который в конечном итоге переполнится в случае неправильного использования.

Единственная другая мысль, которую я имею, связана с сохранением вызовов в стеке для отладки и печати в стеке.

Ответ 2

Мое предположение заключается в том, что он должен гарантировать, что __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ находится в трассировке стека для целей отладки. Он имеет __attribute__((no inline)), который поддерживает эту идею.

Если вы заметили, что эта функция просто идет и отскакивает от другой функции, так что это форма батута, о которой я могу только думать, есть такое подробное имя, чтобы помочь отладки. Это было бы особенно полезно, учитывая, что функция вызывает указатель на функцию, который был зарегистрирован в другом месте, и поэтому у этой функции могут отсутствовать доступные отладочные символы.

Обратите внимание также на другие аналогично названные функции, которые делают подобные вещи - на самом деле это похоже на то, чтобы помочь увидеть, что произошло с обратной сетью. Имейте в виду, что это основной код Mac OS X и будет отображаться в отчетах о сбоях и отчетах о пробных примерах.

Ответ 3

Одна потенциальная причина заключается в том, чтобы упростить отладку и профилирование (с TCO, кадр родительского стека исчезает, что делает сложнее отслеживать трассировки стека).