Search code examples
c#optimizationcil

Can C# 'is' operator suffer under release mode optimization on .NET 4?


Below is a simple test fixture. It succeeds in Debug builds and fails in Release builds (VS2010, .NET4 solution, x64):

[TestFixture]
public sealed class Test
{
    [Test]
    public void TestChecker()
    {
        var checker = new Checker();
        Assert.That(checker.IsDateTime(DateTime.Now), Is.True);
    }
}

public class Checker
{
    public bool IsDateTime(object o)
    {
        return o is DateTime;
    }
}

It seems code optimization wreaks some havoc; if I disable it on the Release build, it works as well. That was rather puzzling to me. Below, I've used ILDASM to disassemble the 2 versions of the build:

Debug IL:

.method public hidebysig instance bool IsDateTime(object o) cil managed
{
  // Code size       15 (0xf)
  .maxstack  2
  .locals init (bool V_0)
  IL_0000:  nop
  IL_0001:  ldarg.1
  IL_0002:  isinst     [mscorlib]System.DateTime
  IL_0007:  ldnull
  IL_0008:  cgt.un
  IL_000a:  stloc.0
  IL_000b:  br.s       IL_000d
  IL_000d:  ldloc.0
  IL_000e:  ret
} // end of method Validator::IsValid

Release IL:

.method public hidebysig instance bool IsDateTime(object o) cil managed
{
  // Code size       10 (0xa)
  .maxstack  8
  IL_0000:  ldarg.1
  IL_0001:  isinst     [mscorlib]System.DateTime
  IL_0006:  ldnull
  IL_0007:  cgt.un
  IL_0009:  ret
} // end of method Validator::IsValid

It seems a store and load is optimized away. Targeting earlier versions of the .NET framework made the problem go away, but that may just be a fluke. I found this behaviour somewhat unnerving, can anybody explain why the compiler would think it safe to do an optimization that produces different observable behaviour?


Solution

  • This bug already came up in this SO question by Jacob Stanley. Jacob has already reported the bug, and Microsoft has confirmed that it is indeed a bug in the CLR JIT. Microsoft had this to say:

    This bug will be fixed in a future version of the runtime. I'm afraid it's too early to tell if that will be in a service pack or the next major release.

    Thank you again for reporting the issue.

    You should be able to work around the bug by adding the following attribute to TestChecker():

    [MethodImpl(MethodImplOptions.NoInlining)]