[C#] 다차원 배열의 열거 값이 자체와 다른 이유는 무엇입니까?

치다:

using System;

public class Test
{
    enum State : sbyte { OK = 0, BUG = -1 }

    static void Main(string[] args)
    {
        var s = new State[1, 1];
        s[0, 0] = State.BUG;
        State a = s[0, 0];
        Console.WriteLine(a == s[0, 0]); // False
    }
}

이것을 어떻게 설명 할 수 있습니까? x86 JIT에서 실행될 때 Visual Studio 2015의 디버그 빌드에서 발생합니다. x64 JIT에서 릴리스 빌드 또는 실행은 예상대로 True를 인쇄합니다.

명령 행에서 재현하려면 다음을 수행하십시오.

csc Test.cs /platform:x86 /debug

( /debug:pdbonly, /debug:portable/debug:full또한 재생).



답변

.NET 4 x86 지터에서 코드 생성 버그를 발견했습니다. 매우 드문 경우이며 코드가 최적화되지 않은 경우에만 실패합니다. 머신 코드는 다음과 같습니다.

        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  

많은 임시 및 코드 복제가 포함 된 플로팅 문제입니다. 이는 최적화되지 않은 코드의 경우에 일반적입니다. 013F04B8의 명령은 주목할 만합니다. 즉, sbyte에서 32 비트 정수로 필요한 변환이 발생합니다. 배열 getter 도우미 함수는 State.BUG와 동일한 0x0000000FF를 반환했으며 값을 비교하기 전에 -1 (0xFFFFFFFF)로 변환해야합니다. MOVSX 명령어는 Sign eXtension 명령어입니다.

013F04CC에서도 같은 일이 다시 발생하지만 이번에 는 동일한 변환을 수행하는 MOVSX 명령 이 없습니다 . 그것이 칩이 떨어지는 곳이며, CMP 명령은 0xFFFFFFFF와 0x000000FF를 비교합니다. 따라서 이것은 누락 오류이며 코드 생성기는 MOVSX를 다시 방출하여 동일한 sbyte에서 int로 변환하는 데 실패했습니다.

이 버그에서 특히 특이한 점은 옵티 마이저를 활성화 할 때 올바르게 작동한다는 것입니다. 이제 두 경우 모두 MOVSX를 사용한다는 것을 알고 있습니다.

이 버그가 오랫동안 발견되지 않은 이유는 sbyte를 열거 형의 기본 유형으로 사용하기 때문입니다. 매우 드문 일입니다. 다차원 배열을 사용하는 것도 도움이되며 조합은 치명적입니다.

그렇지 않으면 매우 중요한 버그입니다. 추측하기 어려울 정도로 테스트 할 4.6.1 x86 지터 만 있습니다. x64와 3.5 x86 지터는 매우 다른 코드를 생성하고이 버그를 피합니다. 계속하는 임시 해결 방법은 sbyte를 열거 형 기본 유형으로 제거하고 기본값을 int 로 설정하는 것이므로 부호 확장이 필요하지 않습니다.

connect.microsoft.com에서 버그를 제기 할 수 있습니다.이 Q + A에 연결하면 알아야 할 모든 것을 알 수 있습니다. 시간을 내고 싶지 않다면 알려주세요. 제가 처리하겠습니다.


답변

OP의 선언을 고려해 봅시다.

enum State : sbyte { OK = 0, BUG = -1 }

버그 BUG가 음수 (-128에서 -1로)이고 State가 부호있는 바이트 의 열거 인 경우에만 버그가 발생하기 때문에 어딘가에 캐스트 문제가 있다고 가정하기 시작했습니다.

이것을 실행하면 :

Console.WriteLine((sbyte)s[0, 0]);
Console.WriteLine((sbyte)State.BUG);
Console.WriteLine(s[0, 0]);
unchecked
{
    Console.WriteLine((byte) State.BUG);
}

출력됩니다 :

255

-1

곤충

255

내가 (현재) 무시하기 때문에 s[0, 0]평가하기 전에 바이트로 캐스트되므로 그것이 a == s[0,0]거짓 이라고 주장하는 이유 입니다.


답변