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

Мы используем рекурсию для поиска факторов и получаем исключение StackOverflow. Мы прочитали, что компилятор С# на компьютерах x64 выполняет оптимизацию хвостовых вызовов:

JIT определенно делает tailcals при запуске оптимизированного кода, а не отладки.

Запуск dotnet --configuration release заходит так далеко в нашу программу:

...                      
7214 is a factor of 1234567890
7606 is a factor of 1234567890
10821 is a factor of 1234567890
11409 is a factor of 1234567890                

Process is terminated due to StackOverflowException.

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

class Program
{
    static void Main(string[] args)
    {
        const long firstCandidate = 1;
        WriteAllFactors(1234567890, firstCandidate);
    }

    private static void WriteAllFactors(long number, long candidate)
    {
        if (number % candidate == 0)
        {
            System.Console.WriteLine($"{candidate} is a factor of {number}");
        }

        candidate = candidate + 1;
        if(candidate > number / 2)
        {
            return;
        }

        WriteAllFactors(number, candidate);
    }
}

Ответ 1

VSadov дает явную причину этого в своем ответе:

Обычно JIT испускает хвостовые вызовы, когда находит это прибыльным.

Кроме того, он продолжает утверждать:

Это часть, которая не выражается в С#. В отличие от inline, может быть принудительно через атрибуты, tailcalling в настоящее время не может быть принудительно. Если нужно написать код, похожий на EmitMethodCall, он не может использовать С#.

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