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连接站点的链接
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中得到了修复。