我需要使用一些没有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
消失了,但会产生冗余的条件移动,递增和递减。
答案 0 :(得分:4)
几句话:
bsf
指令。查看代码,这并不奇怪,因为您返回mask
,这是源操作数,而不是目标操作数(gcc使用AT&amp; T语法!)。编译器足够聪明,可以解决这个问题并完全删除汇编程序代码(它没有做任何事情)。__builtin_ffsl
,它与内联汇编完全相同(但是,正确)。内联函数不比内联汇编程序便携,但编译器更容易优化。bsf cmov
序列(假设调用代码强制它实际发出指令),这表明编译器使用零标志就好了而没有额外的测试指令char
时,返回bool
并不是编译器的最佳提示,尽管大多数时候它可能会解决它。但是,当您真正只对“零或非零”感兴趣时,告诉编译器使用位扫描指令肯定是次优的。 if(x)
和if(!x)
对此非常有效。如果您将结果作为参考返回,则会有所不同,因此您可以在其他地方重复使用它,但实际上,您的代码只是编写if(x)
的一种非常复杂的方式。