如何在gcc和VS中强制使用cmov

时间:2017-06-26 06:43:23

标签: c++ gcc assembly visual-studio-2015 inline-assembly

我有这个简单的二元搜索成员函数,其中lastIndexnIterxi是类成员:

uint32 scalar(float z) const
{
    uint32 lo = 0;
    uint32 hi = lastIndex;
    uint32 n = nIter;
    while (n--) {
        int mid = (hi + lo) >> 1;
        // defining this if-else assignment as below cause VS2015
        // to generate two cmov instructions instead of a branch
        if( z < xi[mid] ) 
            hi = mid;
        if ( !(z < xi[mid]) )
            lo = mid;
    }
    return lo;
}

gcc和VS 2015都将内循环转换为代码流分支:

000000013F0AA778  movss       xmm0,dword ptr [r9+rax*4]  
000000013F0AA77E  comiss      xmm0,xmm1  
000000013F0AA781  jbe         Tester::run+28h (013F0AA788h) 
000000013F0AA783  mov         r8d,ecx  
000000013F0AA786  jmp         Tester::run+2Ah (013F0AA78Ah)  
000000013F0AA788  mov         edx,ecx  
000000013F0AA78A  mov         ecx,r8d

有没有办法在不编写汇编内联的情况下说服他们使用1 comiss条指令和2 cmov条指令?

如果没有,有人可以建议如何为此编写gcc汇编程序模板吗?

请注意,我知道二进制搜索算法存在变化,编译器很容易生成无分支代码,但这不是问题所在。

由于

1 个答案:

答案 0 :(得分:4)

正如评论中所说,虽然最近(> 4.4)版本的gcc似乎已经像你说的那样优化它,但是没有简单的方法可以强迫你提出要求。 编辑 :有趣的是,gcc 6 series seems to use a branch与使用两个cmov的{​​{3}}和gcc 5系列不同。

通常__builtin_expect可能无法推动gcc使用cmov,因为cmov通常很难预测比较结果,虽然__builtin_expect告诉编译器可能的结果是什么 - 所以你只是把它推向错误的方向。

但是,如果您发现此优化非常重要,那么您的编译器版本通常会出错,并且出于某些原因您无法帮助它,相关的gcc程序集模板应该是这样的:

    __asm__ (
        "comiss %[xi_mid],%[z]\n"
        "cmovb %[mid],%[hi]\n"
        "cmovae %[mid],%[lo]\n"
        : [hi] "+r"(hi), [lo] "+r"(lo)
        : [mid] "rm"(mid), [xi_mid] "xm"(xi[mid]), [z] "x"(z)
        : "cc"
    );

使用的约束是:

  • hilo进入&#34;写&#34;变量列表,+r约束为cmov只能将寄存器用作目标操作数,我们有条件地只覆盖其中一个(我们不能使用= ,因为它意味着值总是被覆盖,所以编译器可以自由地给我们一个不同于当前目标寄存器的目标寄存器,并在我们的asm块后用它来引用该变量;;
  • mid位于&#34;读取&#34; list,rm as cmov可以将寄存器或内存操作数作为输入值;
  • xi[mid]z位于&#34;读取&#34;列表;
    • z具有特殊x约束,表示&#34;任何SSE寄存器&#34; (ucomiss第一个操作数需要);
    • xi[mid]xm,因为第二个ucomiss操作数允许内存运算符;考虑到zxi[mid]之间的选择,我选择最后一个作为直接从内存中获取的候选者,因为z已经在寄存器中(由于系统V)调用约定 - 并且将在迭代之间进行缓存)并且xi[mid]仅用于此比较;
  • cc(FLAGS寄存器)位于&#34; clobber&#34; list - 我们确实破坏了旗帜而没有别的。