JIT是否生成了错误的代码

本文关键字:错误 代码 是否 JIT | 更新日期: 2023-09-27 17:51:26

我一直在找你,有些代码不起作用。除了下面这行以外,一切看起来都很好。

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

在执行该行之前,传输为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连接站点的链接

JIT是否生成了错误的代码

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

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

你可以从你发布的拆解中看出,地址F7A0到F7A8的代码仍然是??的一部分??陈述F7AC的分支才是真正的分支,这就是下一个语句的起点。所以应该说F7AC是下一个语句的开始,而不是F7A0。

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

请放心,这只会在调试时出错,程序仍然可以正常运行。请记住这个怪癖,因为它只会出错吗??操作人员你可以看出它没有被太多使用:(尽管大多数程序员只调试过32位版本的程序,但默认的项目设置非常鼓励这样做

这个问题正在我们讨论的过程中得到解决,不要指望你的Connect报告会产生影响,微软很清楚这个错误。微软的抖动团队已经完全重写了x64抖动,它目前在CTP2中。我估计在它发布之前还有一年左右的时间。

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