1

VS2012 Update 1 によって生成されたコンソール プロジェクトを使用して、次のベクトル減算コードを試しました。グローバル最適化を無効にし、アセンブラー リストを有効にする以外のデフォルト オプションには実際には触れませんでした。

Windows 7 x64 SP1 上の x64 リリース構成でコンパイルされています。

#include <stdio.h>
#include <tchar.h>

#include <emmintrin.h>

typedef unsigned short ushort;
typedef unsigned int uint;

void print(__m128i i)
{
    auto& arr = i.m128i_u16;
    printf("[%d %d  %d  %d  %d  %d  %d  %d]\n", arr[0], arr[1], arr[2], arr[3], arr[4], arr[5], arr[6], arr[7]);
}

int _tmain(int argc, _TCHAR* argv[])
{
    const int lineSize = 912;
    ushort input[lineSize];
    ushort vals[lineSize];
//  printf("%X %X\n", input, vals); // note this one

    for (uint i=0; i<lineSize; i+=8)
    {
        __m128i vecinput = _mm_loadu_si128((__m128i*) &input[i]);
        __m128i vecvals = _mm_loadu_si128((__m128i*) &vals[i]);

        __m128i output = _mm_subs_epu16(vecinput, vecvals);
        print(output);
        printf("===\n");
    }

    return 0;
}

リリース モードで生成されたアセンブリ:

; 20   :    const int lineSize = 912;
; 21   :    ushort input[lineSize];
; 22   :    ushort vals[lineSize];

; without printf
; 23   : // printf("%X %X\n", input, vals);
; with printf
; 23   :    printf("%X %X\n", input, vals);

    lea r8, QWORD PTR vals$[rsp]
    lea rdx, QWORD PTR input$[rsp]
    lea rcx, OFFSET FLAT:??_C@_06NBKGFLKK@?$CFX?5?$CFX?6?$AA@
    call    QWORD PTR __imp_printf

; 24   : 
; 25   :    for (uint i=0; i<lineSize; i+=8)

    xor esi, esi
    lea ebp, QWORD PTR [rsi+114]
    npad    2
$LL3@wmain:

; 26   :    {
; 27   :        __m128i vecinput = _mm_loadu_si128((__m128i*) &input[i]);

    movdqu  xmm1, XMMWORD PTR input$[rsp+rsi]

; 28   :        __m128i vecvals = _mm_loadu_si128((__m128i*) &vals[i]);

; without printf
    movdqu  xmm0, xmm1
; with printf
    movdqu  xmm0, XMMWORD PTR vals$[rsp+rsi]

; 29   : 
; 30   :        __m128i output = _mm_subs_epu16(vecinput, vecvals);

; without printf
    psubusw xmm1, xmm1
; with printf
    psubusw xmm1, xmm0

; 15   :    printf("[%d %d  %d  %d  %d  %d  %d  %d]\n", arr[0], arr[1], arr[2], arr[3], arr[4], arr[5], arr[6], arr[7]);

    pextrw  ax, xmm1, 7
    movzx   edi, ax
    pextrw  ax, xmm1, 6
    movzx   ebx, ax
    pextrw  ax, xmm1, 5
    mov DWORD PTR [rsp+64], edi
    movzx   r11d, ax
    pextrw  ax, xmm1, 4
    mov DWORD PTR [rsp+56], ebx
    movzx   r10d, ax
    pextrw  ax, xmm1, 3
    mov DWORD PTR [rsp+48], r11d
    movzx   ecx, ax
    pextrw  ax, xmm1, 2
    mov DWORD PTR [rsp+40], r10d
    movzx   r9d, ax
    pextrw  ax, xmm1, 1
    mov DWORD PTR [rsp+32], ecx
    movzx   r8d, ax
    lea rcx, OFFSET FLAT:??_C@_0BL@ONEMJFJK@?$FL?$CFd?7?$CFd?7?$CFd?7?$CFd?7?$CFd?7?$CFd?7?$CFd?7?$CFd?$FN?6?$AA@
    movd    eax, xmm1
    movzx   edx, ax
    call    QWORD PTR __imp_printf

; 31   :        print(output);
; 32   :        printf("===\n");

    lea rcx, OFFSET FLAT:??_C@_04LEHBMKOA@?$DN?$DN?$DN?6?$AA@
    call    QWORD PTR __imp_printf
    lea rsi, QWORD PTR [rsi+16]
    dec rbp
    jne $LL3@wmain

; 33   :    }
; 34   : 
; 35   :    return 0;

    xor eax, eax

; 95   : }

    mov rcx, QWORD PTR __$ArrayPad$[rsp]
    xor rcx, rsp
    call    __security_check_cookie
    lea r11, QWORD PTR [rsp+1920]
    mov rbx, QWORD PTR [r11+16]
    mov rbp, QWORD PTR [r11+24]
    mov rsi, QWORD PTR [r11+32]
    mov rsp, r11
    pop rdi
    ret 0
wmain   ENDP

そのため、vals は入力と同じであるかのように誤って扱われ、結果は常に 0 になります。また、誤った最適化のために xmm0 が使用されなくなったにもかかわらず、まだ破棄されていないことも興味深い点です。printf のコメントを外すと、生成されたコードは正しいものになります。

質問は、私のコードに何か問題がありますか? 私には、オプティマイザーのバグのように見えます。

4

1 に答える 1

2

ushort input[lineSize]配列と を初期化することはないushort vals[lineSize]ため、オプティマイザーはたまたまそれらを同一のものとして扱います。これは、未定義の動作には問題ありません。

そこにprintf("%X %X\n", input, vals)呼び出しがある場合、配列のアドレスを外部関数に渡しているため、オプティマイザには、それらが指すメモリがその外部関数によって更新される可能性があると信じる理由があります。

于 2013-01-30T15:59:41.180 に答える