在GCC内联汇编中检索ZF

时间:2011-08-01 08:10:47

标签: gcc assembly x86

我需要使用一些没有GCC内在函数的x86指令,例如BSF和BSR。 使用GCC内联汇编,我可以编写类似下面的内容

__INTRIN_INLINE unsigned char bsf64(unsigned long* const index, const uint64_t mask)
{
__asm__("bsf %[mask], %[index]" : [index] "=r" (*index) : [mask] "mr" (mask));
return mask ? 1 : 0;
}

if (bsf64(x, y)) { /* use x */ }这样的代码由GCC翻译为

0x000000010001bf04 <bsf64+0>:   bsf    %rax,%rdx
0x000000010001bf08 <bsf64+4>:   test   %rax,%rax
0x000000010001bf0b <bsf64+7>:   jne    0x10001bf44 <...>

但是如果mask为零,则BSF已经设置了ZF标志,因此test之后的bsf是多余的。

是否可以检索ZF标志并返回它,而不是返回mask ? 1 : 0,这使得GCC无法生成test

编辑:使if示例更清晰

编辑:为响应Damon,__builtin_ffsl生成的代码效果更差。如果我使用以下代码

    int b = __builtin_ffsl(mask);
    if (b) {
        *index = b - 1;
        return true;
    } else {
        return false;
    }

GCC生成此程序集

   0x000000000044736d <+1101>:  bsf    %r14,%r14
   0x0000000000447371 <+1105>:  cmove  %r12,%r14
   0x0000000000447375 <+1109>:  add    $0x1,%r14d
   0x0000000000447379 <+1113>:  je     0x4471c0 <...>
   0x000000000044737f <+1119>:  lea    -0x1(%r14),%ecx

因此test消失了,但会产生冗余的条件移动,递增和递减。

1 个答案:

答案 0 :(得分:4)

几句话:

  • 这是一种“反优化”。您正在尝试对编译器已经支持的内容进行微优化。
  • 在我的gcc版本开启所有优化开关的情况下,您的代码根本不会生成bsf指令。查看代码,这并不奇怪,因为您返回mask,这是操作数,而不是目标操作数(gcc使用AT&amp; T语法!)。编译器足够聪明,可以解决这个问题并完全删除汇编程序代码(它没有做任何事情)。
  • 有一个内在函数__builtin_ffsl,它与内联汇编完全相同(但是,正确)。内联函数不比内联汇编程序便携,但编译器更容易优化。
  • 使用内部函数会在我的编译器上产生bsf cmov序列(假设调用代码强制它实际发出指令),这表明编译器使用零标志就好了而没有额外的测试指令
  • 当你需要char时,返回bool并不是编译器的最佳提示,尽管大多数时候它可能会解决它。但是,当您真正只对“零或非零”感兴趣时,告诉编译器使用位扫描指令肯定是次优的。 if(x)if(!x)对此非常有效。如果您将结果作为参考返回,则会有所不同,因此您可以在其他地方重复使用它,但实际上,您的代码只是编写if(x)的一种非常复杂的方式。