Является ли JIT генерирующим неправильный код?

Я искал вам код, который не работал. Все выглядит отлично, за исключением следующей строки.

Transport = Transport?? MockITransportUtil.GetMock(true);

Перед выполнением этой строки Transport имеет значение null. Я вижу, что GetMock выполняется, и что он возвращает ненулевой объект. После этой строки Transport по-прежнему остается null;

Я посмотрел на ИЛ, который был сгенерирован, и это выглядит хорошо для меня.

 IL_0002:  ldarg.0
  IL_0003:  ldfld      class [Moq]Moq.Mock`1<class [CommLibNet]CommLibNET.ITransport> Curex.Services.Common.UnitTests.Messaging.TestIGuaranteedSubscriptionBase::Transport
  IL_0008:  dup
  IL_0009:  brtrue.s   IL_0012
  IL_000b:  pop
  IL_000c:  ldc.i4.1
  IL_000d:  call       class [Moq]Moq.Mock`1<class [CommLibNet]CommLibNET.ITransport> Curex.Services.Common.UnitTests.Mocking.MockITransportUtil::GetMock(bool)
  IL_0012:  stfld      class [Moq]Moq.Mock`1<class [CommLibNet]CommLibNET.ITransport> Curex.Services.Common.UnitTests.Messaging.TestIGuaranteedSubscriptionBase::Transport

Мы видим, что функция get вызвана, и stfld должен принять возвращаемое значение и установить поле.

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

            Transport = Transport?? MockITransportUtil.GetMock(true);
000007FE9236F776  mov         rax,qword ptr [rbp+0B0h]  
000007FE9236F77D  mov         rax,qword ptr [rax+20h]  
000007FE9236F781  mov         qword ptr [rbp+20h],rax  
000007FE9236F785  mov         rcx,qword ptr [rbp+20h]  
000007FE9236F789  mov         rax,qword ptr [rbp+0B0h]  
000007FE9236F790  mov         qword ptr [rbp+28h],rax  
000007FE9236F794  test        rcx,rcx  
000007FE9236F797  jne         000007FE9236F7AC  
000007FE9236F799  mov         cl,1  
000007FE9236F79B  call        000007FE92290608  

            //var x = ReferenceEquals(null, Transport) ? MockITransportUtil.GetMock(true) : Transport;
            ListerFactory = ListerFactory ?? MockIListenerUtil.GetMockSetupWithAction((a) => invokingAction = a);
000007FE9236F7A0  mov         qword ptr [rbp+30h],rax  
000007FE9236F7A4  mov         rax,qword ptr [rbp+30h]  
000007FE9236F7A8  mov         qword ptr [rbp+20h],rax  
000007FE9236F7AC  mov         rcx,qword ptr [rbp+28h]  

если я использую оператор if или оператор?: оператор работает отлично.

Visual Studio 2013

ИЗМЕНИТЬ

Я создал минимальное воспроизведение psudo.

class simple
{
    public A MyA = null;
    public B MyB = null;

    public void SetUp()
    {
        MyA = MyA ?? new A();
        MyB = new B();// Put breakpoint here
    }
}

Если вы установите контрольную точку в указанной строке и посмотрите на значение MyA в отладчике, она будет по-прежнему равна null (только при построении в x64). если вы выполните следующую строку, оно установит значение. Я не смог воспроизвести оценку, которая вообще не происходит. Его очень ясно в разборке, выполнение для следующей строки началось до назначения.

Изменить 2

Вот ссылка на сайт ms connect

Ответы

Ответ 1

Я получил обновление от MS, что это действительно настоящая проблема и исправлено в предстоящей версии x64 jiter.

Ответ 2

    MyB = new B();// Put breakpoint here

Проблема - это точка останова, а не генерация кода. X64 jitter flubs this, он генерирует неточную информацию для отладки. Он неправильно выводит информацию о номере для инструкции, используя код, который все еще является частью предыдущего оператора.

Вы можете сказать, что из разборки, которую вы опубликовали, код по адресам F7A0-F7A8 все еще является частью выражение. Ветвь к F7AC является реальной, когда начинается следующий оператор. Поэтому он должен был сказать, что F7AC был началом следующего утверждения, а не F7A0.

Последствия этой ошибки в том, что отладчик никогда не может остановиться в точке останова. Вы можете это увидеть сами, изменив свой репродуцирующий код и напишите public A MyA = new A();. И если он остановится, то назначение еще не выполнено. Таким образом, вы по-прежнему видите переменную с предыдущим значением, null в вашем случае. Один шаг разрешает его, хотя это зависит от того, как выглядит следующий оператор.

Будьте уверены, что это происходит неправильно при отладке, программа по-прежнему работает правильно. Просто держите эту причуду в виду, афайк это только пойдет не так для??? оператор. Вы можете сказать, что он не используется много:) Хотя большинство программистов только отлаживают 32-битную версию своей программы, настройки проекта по умолчанию в значительной степени поощряют ее.

Проблема решается по мере того, как мы говорим, не ожидайте, что ваш отчет Connect будет иметь влияние, Microsoft хорошо знает об этой ошибке. Команда джиттера в Microsoft полностью перезаписала джиттер x64, в настоящее время она находится в CTP2. Я бы оценил еще год или около того, прежде чем он будет выпущен.