Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.NET JIT potential error?

Tags:

c#

jit

The following code gives different output when running the release inside Visual Studio, and running the release outside Visual Studio. I'm using Visual Studio 2008 and targeting .NET 3.5. I've also tried .NET 3.5 SP1.

When running outside Visual Studio, the JIT should kick in. Either (a) there's something subtle going on with C# that I'm missing or (b) the JIT is actually in error. I'm doubtful that the JIT can go wrong, but I'm running out of other possiblities...

Output when running inside Visual Studio:

    0 0,     0 1,     1 0,     1 1, 

Output when running release outside of Visual Studio:

    0 2,     0 2,     1 2,     1 2, 

What is the reason?

using System; using System.Collections.Generic; using System.Linq; using System.Text;  namespace Test {     struct IntVec     {         public int x;         public int y;     }      interface IDoSomething     {         void Do(IntVec o);     }      class DoSomething : IDoSomething     {         public void Do(IntVec o)         {             Console.WriteLine(o.x.ToString() + " " + o.y.ToString()+",");         }     }      class Program     {         static void Test(IDoSomething oDoesSomething)         {             IntVec oVec = new IntVec();             for (oVec.x = 0; oVec.x < 2; oVec.x++)             {                 for (oVec.y = 0; oVec.y < 2; oVec.y++)                 {                     oDoesSomething.Do(oVec);                 }             }         }          static void Main(string[] args)         {             Test(new DoSomething());             Console.ReadLine();         }     } } 
like image 306
Philip Welch Avatar asked Jan 13 '10 13:01

Philip Welch


1 Answers

It is a JIT optimizer bug. It is unrolling the inner loop but not updating the oVec.y value properly:

      for (oVec.x = 0; oVec.x < 2; oVec.x++) { 0000000a  xor         esi,esi                         ; oVec.x = 0         for (oVec.y = 0; oVec.y < 2; oVec.y++) { 0000000c  mov         edi,2                           ; oVec.y = 2, WRONG!           oDoesSomething.Do(oVec); 00000011  push        edi   00000012  push        esi   00000013  mov         ecx,ebx  00000015  call        dword ptr ds:[00170210h]        ; first unrolled call 0000001b  push        edi                             ; WRONG! does not increment oVec.y 0000001c  push        esi   0000001d  mov         ecx,ebx  0000001f  call        dword ptr ds:[00170210h]        ; second unrolled call       for (oVec.x = 0; oVec.x < 2; oVec.x++) { 00000025  inc         esi   00000026  cmp         esi,2  00000029  jl          0000000C  

The bug disappears when you let oVec.y increment to 4, that's too many calls to unroll.

One workaround is this:

  for (int x = 0; x < 2; x++) {     for (int y = 0; y < 2; y++) {       oDoesSomething.Do(new IntVec(x, y));     }   } 

UPDATE: re-checked in August 2012, this bug was fixed in the version 4.0.30319 jitter. But is still present in the v2.0.50727 jitter. It seems unlikely they'll fix this in the old version after this long.

like image 50
Hans Passant Avatar answered Sep 20 '22 15:09

Hans Passant