为什么这个SSE2程序(整数)生成movaps(float)?

Mar*_*tin 3 x86 assembly gcc sse simd

以下循环将整数矩阵转置为另一个整数矩阵.当我有趣地编译时,它生成movaps指令以将结果存储到输出矩阵中.为什么gcc这样?

数据:

int __attribute__(( aligned(16))) t[N][M]  
  , __attribute__(( aligned(16))) c_tra[N][M];
Run Code Online (Sandbox Code Playgroud)

循环:

for( i=0; i<N; i+=4){
    for(j=0; j<M; j+=4){

        row0 = _mm_load_si128((__m128i *)&t[i][j]);
        row1 = _mm_load_si128((__m128i *)&t[i+1][j]);
        row2 = _mm_load_si128((__m128i *)&t[i+2][j]);
        row3 = _mm_load_si128((__m128i *)&t[i+3][j]);

        __t0 = _mm_unpacklo_epi32(row0, row1);
        __t1 = _mm_unpacklo_epi32(row2, row3);
        __t2 = _mm_unpackhi_epi32(row0, row1);
        __t3 = _mm_unpackhi_epi32(row2, row3);

        /* values back into I[0-3] */
        row0 = _mm_unpacklo_epi64(__t0, __t1);
        row1 = _mm_unpackhi_epi64(__t0, __t1);
        row2 = _mm_unpacklo_epi64(__t2, __t3);
        row3 = _mm_unpackhi_epi64(__t2, __t3);

        _mm_store_si128((__m128i *)&c_tra[j][i], row0);
        _mm_store_si128((__m128i *)&c_tra[j+1][i], row1);
        _mm_store_si128((__m128i *)&c_tra[j+2][i], row2);
        _mm_store_si128((__m128i *)&c_tra[j+3][i], row3);



    }
}
Run Code Online (Sandbox Code Playgroud)

汇编生成的代码:

.L39:
    lea rcx, [rsi+rdx]
    movdqa  xmm1, XMMWORD PTR [rdx]
    add rdx, 16
    add rax, 2048
    movdqa  xmm6, XMMWORD PTR [rcx+rdi]
    movdqa  xmm3, xmm1
    movdqa  xmm2, XMMWORD PTR [rcx+r9]
    punpckldq   xmm3, xmm6
    movdqa  xmm5, XMMWORD PTR [rcx+r10]
    movdqa  xmm4, xmm2
    punpckhdq   xmm1, xmm6
    punpckldq   xmm4, xmm5
    punpckhdq   xmm2, xmm5
    movdqa  xmm5, xmm3
    punpckhqdq  xmm3, xmm4
    punpcklqdq  xmm5, xmm4
    movdqa  xmm4, xmm1
    punpckhqdq  xmm1, xmm2
    punpcklqdq  xmm4, xmm2
    movaps  XMMWORD PTR [rax-2048], xmm5
    movaps  XMMWORD PTR [rax-1536], xmm3
    movaps  XMMWORD PTR [rax-1024], xmm4
    movaps  XMMWORD PTR [rax-512], xmm1
    cmp r11, rdx
    jne .L39
Run Code Online (Sandbox Code Playgroud)

gcc -Wall -msse4.2 -masm="intel" -O2 -c -S skylake linuxmint

-mavx2-march=naticve生成VEX编码:vmovaps.

Ant*_*nty 7

功能上这些说明是相同的.我不喜欢复制+粘贴其他人的陈述,因为很少有链接解释它:

MOVDQA和MOVAPS x86指令之间的区别?

https://software.intel.com/en-us/forums/intel-isa-extensions/topic/279587

http://masm32.com/board/index.php?topic=1138.0

https://www.gamedev.net/blog/615/entry-2250281-demystifying-sse-move-instructions/

精简版:

因此,在大多数情况下,您应该尝试使用与要在这些寄存器上使用的操作相对应的移动指令.但是,还有一个复杂的问题.内存中的加载和存储在与整数和浮点单元分开的端口上执行; 因此,无论您附加到移动的数据类型如何,从存储器加载到寄存器或从寄存器存储到存储器的指令都将经历相同的延迟.因此,在这种情况下,无论您使用什么数据,movaps,movapd和movdqa都将具有相同的延迟.由于movaps(和movups)以二进制形式编码,其字节少于其他两个,因此无论数据类型如何,将其用于所有reg-mem移动都是有意义的.

所以这是GCC优化.

  • 这实际上是 Intel 和 AMD 推荐的代码生成实践。事实上,对于现代 CPU,英特尔建议您始终使用“movups”,因为对齐和未对齐的负载具有相同的性能——对齐写入更重要。参见 [英特尔](http://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-optimization-manual.html) 和 [AMD](http: //developer.amd.com/resources/developer-guides-manuals/) 软件优化指南。 (2认同)