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


151

치다:

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또한 재생).


2
ideone.com/li3EzY 는 사실입니다. .NET 버전에 대한 자세한 정보를 추가, IDE는, 컴파일러
백업

1
여기도 마찬가지입니다. 그러나 프로젝트 설정을 검토 한 후 "빌드"탭에서 "32 비트 선호"확인란을 선택 취소하면 의도 한대로 작동한다는 것을 알았습니다. 그래서 그것은 WoW64 문제처럼 보입니다.
Dmitry Rotay

2
프레임 워크에서 버그를 지적한 것 같습니다.
Fabien PERRONNET

1
흥미롭게도, 깨진 코드를 실행 한 ildasm다음 ilasm"수정"합니다.
Jon Skeet

2
/debug=IMPL이 깨진 플래그 잎; /debug=OPT"고정"합니다.
Jon Skeet

답변:


163

.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에 연결하면 알아야 할 모든 것을 알 수 있습니다. 시간을 내고 싶지 않다면 알려주세요. 제가 처리하겠습니다.


33
그런 이상한 문제의 정확한 원인을 가진 훌륭하고 견고한 데이터, 항상 읽는 즐거움, +1.
Lasse V. Karlsen 2018

11
투표 할 수 있도록 connect.microsoft.com 기사에 대한 링크를 게시하십시오.
Hans Passant

byte대신에 사용하는 것도 좋습니다 sbyte. 실제 코드가 데이터베이스의 플래그가 여분의 공간을 차지하지 않도록하는 ORM과 함께 사용되는 경우 바람직 할 수 있습니다.
Voo

6
연결하는 대신 dotnet / coreclr에 버그 게시 하면 JIT 개발자에게 직접 전달됩니다.
Lucas Trzesniewski

8
저는 Microsoft JIT 팀의 개발자입니다. 버그를 재현했으며 내부적으로 문제를 열었습니다 (x86 JIT 배송은 아직 github에서 열리지 않았습니다). 이 문제가 수정되는시기와 관련하여 다음 번 주요 도구 릴리스에이 수정 프로그램이 포함될 것으로 예상합니다. 이 버그가 비즈니스에 영향을 미치고 이전에 수정이 필요한 경우 connect (connect.microsoft.com) 문제를 제기하여 영향을보고 빠른 수정을 위해 어떤 대안이 필요한지 확인하십시오.
Russell C. Hadley

8

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]거짓 이라고 주장하는 이유 입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.