Ответ 1
Следуя ссылкам, которые вы уже предоставили, это часть, которая мне кажется, очень близко отвечает на ваш вопрос.
<Р → CLR и хвосты звонят
Когда вы работаете с языками, управляемыми CLR, в игре есть два вида компиляторов. Там компилятор, который идет от вашего исходного кода до IL (разработчики С# знают это как csc.exe), а затем компилятор, который идет от IL к собственному коду (компиляторы JIT 32/64 бит, которые вызывают во время выполнения или время NGEN). Исходные компиляторы source- > IL и IL- > понимают оптимизацию хвостового вызова. Но собственный компилятор IL- > , который я буду называть JIT, имеет последнее слово в будет ли в конечном итоге использоваться оптимизация вызова хвоста. Компилятор source- > IL может помочь генерировать ИЛ, что способствует созданию хвостовых вызовов, включая использование "хвоста". IL prefix (подробнее об этом позже). Таким образом, компилятор source- > IL может структурировать IL, который он генерирует, чтобы убедить JIT в создании хвостового вызова. Но JIT всегда имеет возможность делать все, что захочет.
Когда JIT делает хвостовые звонки?
Я спросил Фей Чена и Гранта Ричинса, соседей по коридору от меня, которые, случается, работают над JIT, при каких условиях различные JIT будут использовать оптимизацию хвостового вызова. Полный ответ довольно подробный. Краткое краткое изложение состоит в том, что JITs пытаются использовать оптимизацию хвостового вызова всякий раз, когда могут, но есть много причин, по которым оптимизация хвостовых вызовов не может быть использована. Некоторые причины, по которым вызов хвоста не является опцией:
- Caller не возвращается сразу после вызова (duh: -))
- Аргументы стека между вызывающим и вызываемым несовместимы таким образом, что потребовалось бы перемещение вещей вокруг кадра вызывающего абонента до того, как вызываемый мог выполнить
- Caller и callle возвращают разные типы.
- Вместо этого мы встраиваем вызов (вставка лучше, чем вызов хвоста, и открывает двери для многих других оптимизаций)
- Безопасность мешает
- Отладчик/профилировщик отключил оптимизацию JIT
Самая интересная часть в контексте вашего вопроса, которая делает это очень ясным, на мой взгляд, среди многих сценариев, - это пример безопасности, упомянутой выше...
Безопасность в .NET во многих случаях зависит от точности стека... во время выполнения. Вот почему, как указано выше, бремя разделяется обоими источниками на компилятор CIL и (исполняемый файл) CIL-to-native JIT-компиляторы, причем последнее слово принадлежит последнему.