I got an update from MS that this was indeed a real problem and has been fixed in the upcoming release of the x64 jiter.
Is the JIT generating the wrong code
Question
I have been looking in to you some code wasn't working. Everything looks fine except for the following line.
Transport = Transport?? MockITransportUtil.GetMock(true);
Before that line is executed Transport is null. I see the GetMock executed and that it returns a non null object. After that line Transport is still null;
I looked at the IL that was generated an it looks fine to me.
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
We see the function get called and stfld should take the return value and set the field.
So I then looked at the assembly I see the call get made but it looks like the return in RAX gets blown away by the next call and is lost.
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 I use an if statement or a ?: operator everyting works fine.
Visual Studio 2013
EDIT
I have create a psudo minimal reproduction.
class simple
{
public A MyA = null;
public B MyB = null;
public void SetUp()
{
MyA = MyA ?? new A();
MyB = new B();// Put breakpoint here
}
}
If you set a breakpoint on the indicated line and look at the value of MyA in the debugger it will still be null(only if building in x64). if you execute the next line it will set the value. I have not been able to reproduce the assessment not happening at all. Its very clear in the disassembly the execution for the next line has begun before the assignment takes place.
Edit 2
Here is a link to the ms connect site
Solution 2
OTHER TIPS
MyB = new B();// Put breakpoint here
The problem is the breakpoint, not the code generation. The x64 jitter flubs this, it generates inaccurate debugging info. It emits line number info for the statement incorrectly, using a code address that's still part of the previous statement.
You can tell from the disassembly you posted, the code at addresses F7A0 through F7A8 are still part of the ?? statement. The branch to F7AC is the real one, that's where the next statement starts. So it should have said that F7AC was the start of the next statement, not F7A0.
The consequences of this bug is that the debugger may never stop at the breakpoint. You can see this for yourself by altering your repro code and write public A MyA = new A();
And that if it does stop then the assignment isn't executed yet. So you still see the variable having the previous value, null in your case. A single step resolves it, albeit that it depends on what the next statement looks like.
Rest assured that this only goes wrong when you debug, the program still operates correctly. Just keep this quirk in mind, afaik it only goes wrong for the ?? operator. You can tell it doesn't get used much :) Albeit that most programmers only ever debug the 32-bit version of their program, the default project settings heavily encourage it.
The problem is being addressed as we speak, don't expect your Connect report to have an affect, Microsoft is well aware of this bug. The jitter team at Microsoft has rewritten the x64 jitter completely, it is currently in CTP2. I'd estimate another year or so before it is released.