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


название вопроса может быть немного странным, но дело в том, что, насколько я знаю, нет ничего, что говорит против оптимизации хвостового вызова вообще. Однако, просматривая проекты с открытым исходным кодом, я уже столкнулся с несколькими функциями, которые активно пытаются остановить компилятор от выполнения оптимизации хвостового вызова, например реализация 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
}

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

3 78

3 ответа:

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

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

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

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

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

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

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