使g ++使用SHLD/SHRD指令

Vin*_*ent 6 c++ optimization assembly gcc bit-shift

请考虑以下代码:

#include <limits>
#include <cstdint>

using T = uint32_t; // or uint64_t

T shift(T x, T y, T n)
{
    return (x >> n) | (y << (std::numeric_limits<T>::digits - n));
}
Run Code Online (Sandbox Code Playgroud)

根据godbolt,clang 3.8.1 为-O1,-O2,-O3生成以下汇编代码:

shift(unsigned int, unsigned int, unsigned int):
        movb    %dl, %cl
        shrdl   %cl, %esi, %edi
        movl    %edi, %eax
        retq
Run Code Online (Sandbox Code Playgroud)

而gcc 6.2(即使有-mtune=haswell)生成:

shift(unsigned int, unsigned int, unsigned int):
    movl    $32, %ecx
    subl    %edx, %ecx
    sall    %cl, %esi
    movl    %edx, %ecx
    shrl    %cl, %edi
    movl    %esi, %eax
    orl     %edi, %eax
    ret
Run Code Online (Sandbox Code Playgroud)

这似乎远没有那么优化,因为SHRD在英特尔Sandybridge和后来非常快.无论如何重写函数以便编译器(尤其是gcc)进行优化并支持使用SHLD/SHRD汇编指令?

或者是否有任何gcc -mtune或其他选项可以鼓励gcc更好地调整现代Intel CPU?

有了-march=haswell它,它会发出BMI2 shlx/shrx,但仍然没有shrd.

Joh*_*ica 6

不,我看不到让gcc使用该SHRD指令的方法.
您可以通过更改-mtune-march选项来操纵gcc生成的输出.

或者是否有任何gcc -mtune或其他选项可以鼓励gcc更好地调整现代Intel CPU?

是的,你可以让gcc生成BMI2代码:

例如:X86-64 GCC6.2 -O3 -march=znver1 //AMD Zen
生成:( Haswell时间).

    code            critical path latency     reciprocal throughput
    ---------------------------------------------------------------
    mov     eax, 32          *                     0.25
    sub     eax, edx         1                     0.25        
    shlx    eax, esi, eax    1                     0.5
    shrx    esi, edi, edx    *                     0.5
    or      eax, esi         1                     0.25
    ret
    TOTAL:                   3                     1.75
Run Code Online (Sandbox Code Playgroud)

与clang 3.8.1相比:

    mov    cl, dl            1                     0.25
    shrd   edi, esi, cl      4                     2
    mov    eax, edi          *                     0.25 
    ret
    TOTAL                    5                     2.25
Run Code Online (Sandbox Code Playgroud)

鉴于依赖链在这里:SHRDHaswell的速度较慢,与Sandybridge相关,在Skylake上较慢.序列
的倒数吞吐量更快shrx.

所以这取决于,在后BMI处理器gcc产生更好的代码,前BMI clang胜利.
SHRD在不同的处理器上有不同的时序,我可以看出为什么gcc并不过分喜欢它.
即使使用-Os(优化大小)gcc仍然没有选择SHRD.

*)不是时序的一部分,因为要么不在关键路径上,要么变成零延迟寄存器重命名.