2017-03-14 6 views
4

SIMD 컬러 lerp 함수로 작업하면서 이상한 행동을했고 최소한의 프로그램으로 정리했습니다. 이 예제의 SIMD 코드는 더 이상 lerp를 수행하지 않지만 32 비트 색상에서 XMM 레지스터로 압축을 풀고 다시 32 비트로 압축 해제합니다.MSVC++ 2015 - 내 프로그램에서 SSE 컴파일러 버그 또는 버그/정의되지 않은 동작이 발생합니까?

릴리스 x64 모드에서 MSVC++ 2015 (업데이트 3)에서 다음 코드는 올바른 결과를 산출하지는 않지만 디버그 x64 또는 릴리스/디버그 x86에서는 올바르게 작동합니다.

#include <stdint.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include "emmintrin.h" 

struct Color4 
{ 
    uint8_t red; 
    uint8_t green; 
    uint8_t blue; 
    uint8_t alpha; 

    Color4(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha = 255) 
     : red(red), green(green), blue(blue), alpha(alpha) {} 

    explicit Color4(uint32_t rgba) 
    { 
     red = (uint8_t)(rgba & 0xFF); 
     green = (uint8_t)((rgba >> 8)&0xFF); 
     blue = (uint8_t)((rgba >> 16) & 0xFF); 
     alpha = (uint8_t)((rgba >> 24) & 0xFF); 
    } 
}; 

Color4 PackUnpack(Color4 col) 
{ 
    uint32_t tmp; 

    memcpy(&tmp, &col, sizeof(tmp)); 

    __m128 aFloat = _mm_cvtepi32_ps(
     _mm_unpacklo_epi16(
      _mm_unpacklo_epi8(
       _mm_set1_epi32(tmp), 
       _mm_setzero_si128() 
      ), 
      _mm_setzero_si128() 
     ) 
    ); 

    __m128i ret = _mm_packus_epi16(
     _mm_packs_epi32(
      _mm_cvtps_epi32(aFloat), 
      _mm_setzero_si128() 
     ), 
     _mm_setzero_si128() 
    ); 

    return Color4((uint32_t)_mm_cvtsi128_si32(ret)); 
} 

int main() 
{ 
#ifdef _DEBUG 
    printf("DEBUG\n"); 
#else 
    printf("RELEASE\n"); 
#endif 

    Color4 c = PackUnpack(Color4(32, 64, 128, 255)); 

    // Debug x64 or Debug/Release x86: Prints "32 64 128 255" 
    // Release x64: Prints "255 0 0 0" 
    printf("%d %d %d %d\n", c.red, c.green, c.blue, c.alpha); 

    return 0; 
} 

릴리스 x64의 출력은 다음과 같습니다 :이 유일한 그렇지 않으면 빈에서 Win32 C++ 콘솔 응용 프로그램 프로젝트의 코드

RELEASE 
255 0 0 0 

디버그 x64 및 모든 86의 출력 :

DEBUG 
32 64 128 255 

XMM 레지스터에로드 할 상수 값을 사전 계산하여 _mm_set1_epi32 (앞의 movdqa 지침 참조) 건너 뛰기가 엉망인 것 같습니다.

main: 
00007FF674391070 sub   rsp,38h 
00007FF674391074 lea   rcx,[string "RELEASE\n" (07FF674392200h)] 
00007FF67439107B call  printf (07FF674391010h) 
00007FF674391080 movdqa  xmm0,xmmword ptr [[email protected] (07FF674392220h)] 
00007FF674391088 lea   rcx,[string "%d %d %d %d\n" (07FF674392210h)] 
00007FF67439108F xorps  xmm2,xmm2 
00007FF674391092 mov   dword ptr [rsp+40h],0FF804020h 
00007FF67439109A punpcklbw xmm0,xmm2 
00007FF67439109E punpcklwd xmm0,xmm2 
00007FF6743910A2 cvtdq2ps xmm0,xmm0 
00007FF6743910A5 cvtps2dq xmm1,xmm0 
00007FF6743910A9 packssdw xmm1,xmm2 
00007FF6743910AD packuswb xmm1,xmm2 
00007FF6743910B1 movd  r10d,xmm1 
00007FF6743910B6 mov   edx,r10d 
00007FF6743910B9 mov   r8d,r10d 
00007FF6743910BC shr   edx,10h 
00007FF6743910BF mov   eax,r10d 
00007FF6743910C2 shr   r8d,8 
00007FF6743910C6 movzx  r9d,dl 
00007FF6743910CA shr   eax,18h 
00007FF6743910CD movzx  edx,r10b 
00007FF6743910D1 movzx  r8d,r8b 
00007FF6743910D5 mov   dword ptr [rsp+20h],eax 
00007FF6743910D9 call  printf (07FF674391010h) 
00007FF6743910DE xor   eax,eax 
00007FF6743910E0 add   rsp,38h 
00007FF6743910E4 ret 

우분투 14.04 64에 g++ 4.8.4와 함께이 시도하고 그것은 또는 해제 -O3와 함께 잘 작동합니다.

제 질문은 입니다 이것은 컴파일러 버그, 정의되지 않은/구현을 사용하여 정의 된 동작 또는 내 평범한 버그가있는 코드입니까?

은 (코드는 표준 아니기 때문에 여전히 ... 나는 방어 적이기로 대체 Color4, 밖으로 uint32_t 값을 얻기 위해 노동 조합을 통해 더 주사위 형 말장난을 사용하지하는 데 사용됩니다.)

+0

도움이 될 수 있습니다 그 : https://support.microsoft.com/en-us/help/3207317/visual-co ptimizer-fixes-for-visual-studio-2015-update-3 –

+0

예기치 않은 동작은 VS 2017에서 동일합니다. – Timbo

+0

컴파일러 버그처럼 보입니다. 'memcpy' 대신'tmp = color.red + 256 * (col.blue + 256 * (col.green + 256 * col.alpha)));를 사용하면 어떻게됩니까? – 1201ProgramAlarm

답변

0

이 때문이다 컴파일러 버그. 해결 방법은 memcpy 또는 형식 말장난 대신에

tmp = color.red + 256 * (col.blue + 256 * (col.green + 256 * col.alpha))); 

을 사용하는 것입니다.

+0

감사합니다. - 저는 실제 컴파일러 버그를 쳤다는 것에 약간의 충격을 받았습니다. 그래서 누군가를 다르게 증명할 수 있으려면이 코드를 조금 더 열어 둘 예정입니다. 그렇지만 수락 할 수 없다면 Accepted로 표시 할 것입니다! :) (그리고 MS와 파일.) –

+0

이미이 문제에 대해 제기 된 버그가 있습니까? 아니면이 문제가 임시적인 컴파일러 버그입니까? – MDV

+0

제가 제출했는지 모르겠지만 생성 된 코드가 잘못되어 버그가 될 수 있습니다. – 1201ProgramAlarm

2
실제로

하지 대답하지만, 내가 코멘트에 너무 많은 텍스트를 넣어 좋아하지 않기 때문에,이 작은 코드 내가 함께 문제를 재현 할 수 :

#include <stdint.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include "emmintrin.h" 

int main() 
{ 
    uint8_t src[4] = { 32, 64, 128, 255 }; 

    uint32_t tmp = 0; 
    memcpy(&tmp, &src, sizeof(tmp));  

    auto a = _mm_set1_epi32(tmp); 

    printf("tmp = 0x%08x\n", tmp); 
    printf("a.m128i_i32[0] = 0x%08x\n", a.m128i_i32[0]); 

    return 0; 
} 

예상 출력 :

릴리스 64와
tmp = 0xff804020 
a.m128i_i32[0] = 0xff804020 

출력 :

tmp = 0xff804020 
a.m128i_i32[0] = 0x000000ff 
+0

함께 사용해 주셔서 감사합니다. –

+0

좀 더 자세히 살펴보면 SSE 레지스터에있는 값이 실제로 배열의 4 번째 바이트임을 알 수 있습니다. 재미있는 :-) – Timbo