Você encontrou um bug de geração de código no jitter do .NET 4 x86. É muito incomum, só falha quando o código não é otimizado. O código da máquina fica assim:
State a = s[0, 0];
013F04A9 push 0 ; index 2 = 0
013F04AB mov ecx,dword ptr [ebp-40h] ; s[] reference
013F04AE xor edx,edx ; index 1 = 0
013F04B0 call 013F0058 ; eax = s[0, 0]
013F04B5 mov dword ptr [ebp-4Ch],eax ; $temp1 = eax
013F04B8 movsx eax,byte ptr [ebp-4Ch] ; convert sbyte to int
013F04BC mov dword ptr [ebp-44h],eax ; a = s[0, 0]
Console.WriteLine(a == s[0, 0]); // False
013F04BF mov eax,dword ptr [ebp-44h] ; a
013F04C2 mov dword ptr [ebp-50h],eax ; $temp2 = a
013F04C5 push 0 ; index 2 = 0
013F04C7 mov ecx,dword ptr [ebp-40h] ; s[] reference
013F04CA xor edx,edx ; index 1 = 0
013F04CC call 013F0058 ; eax = s[0, 0]
013F04D1 mov dword ptr [ebp-54h],eax ; $temp3 = eax
; <=== Bug here!
013F04D4 mov eax,dword ptr [ebp-50h] ; a == s[0, 0]
013F04D7 cmp eax,dword ptr [ebp-54h]
013F04DA sete cl
013F04DD movzx ecx,cl
013F04E0 call 731C28F4
Um caso difícil com muitos temporários e duplicação de código, isso é normal para código não otimizado. A instrução em 013F04B8 é notável; é aí que ocorre a conversão necessária de sbyte para um número inteiro de 32 bits. A função auxiliar do getter da matriz retornou 0x0000000FF, igual a State.BUG, e precisa ser convertida em -1 (0xFFFFFFFF) antes que o valor possa ser comparado. A instrução MOVSX é uma instrução Sign eXtension.
A mesma coisa acontece novamente em 013F04CC, mas desta vez não há instruções MOVSX para fazer a mesma conversão. É aí que os chips caem, a instrução CMP compara 0xFFFFFFFF com 0x000000FF e isso é falso. Portanto, este é um erro de omissão, o gerador de código falhou ao emitir o MOVSX novamente para executar a mesma conversão de sbyte em int.
O que é particularmente incomum nesse bug é que ele funciona corretamente quando você ativa o otimizador, que agora sabe usar o MOVSX nos dois casos.
O motivo provável pelo qual esse bug ficou sem ser detectado por tanto tempo é o uso de sbyte como o tipo base do enum. Muito raro de se fazer. O uso de uma matriz multidimensional também é fundamental, a combinação é fatal.
Caso contrário, um bug bastante crítico, eu diria. É difícil adivinhar quão difundida pode ser, só tenho o jitter 4.6.1 x86 para testar. O jitter x64 e o jitter 3,5 x86 geram códigos muito diferentes e evitam esse bug. A solução temporária para continuar é remover sbyte como o tipo base de enum e deixá-lo como padrão, int , para que nenhuma extensão de sinal seja necessária.
Você pode registrar o bug em connect.microsoft.com, vincular a este Q + A deve ser suficiente para dizer a eles tudo o que eles precisam saber. Deixe-me saber se você não quiser reservar um tempo e eu cuido disso.