릴리스 모드에서 코드 동작이 예상과 다릅니다


131

다음 코드는 디버그 모드와 릴리스 모드 (Visual Studio 2008 사용)에서 다른 결과를 생성합니다.

int _tmain(int argc, _TCHAR* argv[])
{

    for( int i = 0; i < 17; i++ ) 
    { 
        int result = i * 16;

        if( result > 255 )
        {
            result = 255;
        }

        printf("i:%2d, result = %3d\n", i, result) ; 
    } 

    return 0;
}

예상대로 디버그 모드의 출력 :

i: 0, result =   0
i: 1, result =  16
(...)
i:14, result = 224
i:15, result = 240
i:16, result = 255

i : 15 결과가 올바르지 않은 릴리스 모드의 출력 :

i: 0, result =   0
i: 1, result =  16
(...)
i:14, result = 224
i:15, result = 255
i:16, result = 255

릴리스 모드에서 Visual Studio에서 "최적화-> 최적화하지 않음"을 선택하면 출력 결과가 정확합니다. 그러나 최적화 프로세스가 잘못된 결과를 초래할 수있는 이유를 알고 싶습니다.


최신 정보:

Mohit JainBy가 제안한대로 다음과 같이 인쇄합니다.

printf("i:%2d, result = %3d, i*16=%d\n", i, result, i*16) ;

릴리즈 모드 출력이 정확합니다 :

i: 0, result =   0, i*16=0
i: 1, result =  16, i*16=16
(...)
i:14, result = 224, i*16=224
i:15, result = 240, i*16=240
i:16, result = 255, i*16=256

15
그것은 컴파일러 버그 (그리고 상당히 중요한 버그)처럼 보입니다.
WhozCraig

1
@WhozCraig i * 16게시물 의 출력 만 업데이트하면 결과가 정확합니다.
Lorris Lin

4
@ juanchopanza : MS 및 버그 수정에 대한 나의 경험에서 VS에 대한 정보를 얻은 후에는 버그를 수정했지만 이전 버전의 VS에는 해당 수정 사항을 적용하지 않으므로 어떤 이유로 든 이전 버전을 사용해야하는 경우 VS를 사용하면 최신 버전으로 업그레이드 할 때까지 이러한 버그가 발생합니다.
Kaiserludi

2
FWIW 이것은 곧 출시 될 Visual Studio 2015에서 잘 작동합니다.
ismail

답변:


115

이것은 적어도 역사적 관점에서 흥미 롭습니다. VC 2008 (15.00.30729.01) VC 2010 (16.00.40219.01) (32 비트 x86 또는 64 비트 x64 대상) 의 문제를 재현 할 수 있습니다 . VC 2012 (17.00.61030)부터 시작하려고 시도한 컴파일러에서는 문제가 발생하지 않습니다.

내가 컴파일하는 데 사용한 명령 : cl /Ox vc15-bug.cpp /FAsc

VC 2008 (및 2010)은 다소 오래되었고 수정 사항이 몇 년 동안 있었으므로 새로운 컴파일러를 사용하는 것을 제외하고 Microsoft의 조치를 기대할 수 있다고 생각하지 않습니다.

문제는 255실제 i * 16표현식 결과가 아닌 루프 카운트를 기반으로 값을 강제 적용할지 여부를 결정하는 테스트 입니다. 그리고 컴파일러는 단순히 값을로 시작해야 할 때 카운트를 잘못 얻습니다 255. 왜 그런 일이 일어 났는지 전혀 모르겠습니다. 그것은 내가 보는 효과 일뿐입니다.

; 6    :    for( int i = 0; i < 17; i++ ) 

  00001 33 f6        xor     esi, esi
$LL4@main:
  00003 8b c6        mov     eax, esi
  00005 c1 e0 04     shl     eax, 4

; 7    :    { 
; 8    :        int result = i * 16;
; 9    : 
; 10   :        if( result > 255 )

  // the value `esi` is compared with in the following line should be 15!
  00008 83 fe 0e     cmp     esi, 14            ; 0000000eH
  0000b 7e 05        jle     SHORT $LN1@main

; 11   :        {
; 12   :            result = 255;

  0000d b8 ff 00 00 00   mov     eax, 255       ; 000000ffH
$LN1@main:

; 13   :        }

업데이트 : VC 2008 이전에 설치 한 모든 버전의 VC에는 VC6을 제외하고 동일한 버그가 있습니다. 프로그램을 컴파일하면 VC6 컴파일러가 충돌합니다.

vc15-bug.cpp(10) : fatal error C1001: INTERNAL COMPILER ERROR

따라서 이것은 MSVC에서 10 년 이상 한 형태로 지속 된 버그입니다!


만약 x86 어셈블리 타이밍의 메모리가 옳다면 eax보다는 esi와 비교 한 이유는 comp eax이고, 255는 eax가 쓰여졌을 때 파이프 라인을 멈추게 할 것입니다.
Loren Pechtel

3
내 추측 (변환) : 결과> 255, 결과 / 16>
255/16

매우 흥미로운! 또한 비교를에서 result > 255로 변경하면 result >= 255올바르게 동작합니다. VS2010의 변화가 cmp esi, 14cmp esi, 16(그리고 jlejl).
opello

16

보고 된 사실이 정확하다고 가정하면 컴파일러 버그 일 수 있습니다. 최신 버전의 컴파일러를 확인하십시오. 버그가 여전히 존재하면 버그 보고서를 제출하십시오.

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