JIT是否生成了错误的代码

时间:2014-03-21 18:09:01

标签: c# jit

我一直在找你,有些代码无效。除了下面一行,一切看起来都很好。

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

在执行该行之前,Transport为null。我看到GetMock已执行,并且它返回一个非null对象。在该行之后,运输仍为空;

我看着生成的IL,它对我来说很好看。

 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

我们看到函数被调用,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连接网站的link

2 个答案:

答案 0 :(得分:10)

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

问题是断点,而不是代码生成。 x64抖动会导致这种情况发生,它会产生不准确的调试信息。它使用仍然是前一个语句一部分的代码地址错误地发出语句的行号信息。

你可以从你发布的反汇编中看出,地址F7A0到F7A8的代码仍然是??声明。 F7AC的分支是真实的,这是下一个语句开始的地方。所以应该说F7AC是下一个语句的开头,而不是F7A0。

此错误的后果是调试器可能永远不会在断点处停止。您可以通过更改您的repro代码并编写public A MyA = new A();来自己看到这一点。如果它 停止,则分配尚未执行。因此,在您的情况下,您仍然会看到具有先前值的变量 null 。一步就解决了它,尽管它取决于下一个语句的样子。

请放心,调试时只会出错,程序仍能正常运行。只要记住这个怪癖,afaik它只会出错?运营商。你可以告诉它不会得到太多使用:)虽然大多数程序员只调试他们程序的32位版本,默认的项目设置非常鼓励它。

正如我们所说,问题正在得到解决,不要指望您的Connect报告会产生影响,Microsoft非常清楚这个错误。微软的抖动团队有rewritten the x64 jitter completely,目前在CTP2。我估计它会被释放一年左右。

答案 1 :(得分:1)

我从MS得到了一个更新,这确实是一个真正的问题,并已在即将发布的x64 jiter中得到修复。