我正在使用 GCC 4.8.1 编译 C 代码,我需要检测 x86/64 架构的减法中是否发生下溢。两者都是未签名的。我知道在汇编中很容易,但我想知道我是否可以在 C 代码中完成它并让 GCC 以某种方式对其进行优化,因为我找不到它。这是一个非常常用的功能(或低级,是这个词吗?)所以我需要它高效,但 GCC 似乎太笨了,无法识别这个简单的操作?我尝试了很多方法在 C 中给它提示,但它总是使用两个寄存器,而不仅仅是一个 sub 和一个条件跳转。老实说,看到如此愚蠢的代码编写了很多次(函数被调用了很多次),我感到很恼火。
我在 C 语言中的最佳方法似乎如下:
if((a-=b)+b < b) {
// underflow here
}
基本上,从 a 中减去 b,如果结果下溢检测到它并进行一些条件处理(这与 a 的值无关,例如,它会带来错误等)。
GCC 似乎太笨了,无法将上述内容简化为子跳转和条件跳转,相信我,我尝试了很多方法在 C 代码中执行此操作,并尝试了很多命令行选项(当然包括 -O3 和 -Os)。GCC 所做的事情是这样的(英特尔语法汇编):
mov rax, rcx ; 'a' is in rcx
sub rcx, rdx ; 'b' is in rdx
cmp rax, rdx ; useless comparison since sub already sets flags
jc underflow
不用说上面是愚蠢的,当它所需要的只是:
sub rcx, rdx
jc underflow
这太烦人了,因为 GCC 确实理解 sub 以这种方式修改标志,因为如果我将其类型转换为“int”,它将生成与上面完全相同的内容,除了它使用带有符号跳转的“js”,而不是进位,这不会如果无符号值差异足够高以设置高位,则工作。尽管如此,它表明它知道影响这些标志的子指令。
现在,也许我应该放弃尝试让 GCC 正确优化它并使用我没有问题的内联汇编来完成它。不幸的是,这需要“asm goto”,因为我需要有条件的 JUMP,而 asm goto 的输出效率不是很高,因为它是易变的。
我尝试了一些东西,但我不知道使用它是否“安全”。asm goto 由于某种原因不能有输出。我不想让它将所有寄存器刷新到内存,这会扼杀我这样做的全部意义,这是效率。但是,如果我使用空的 asm 语句,并且在它之前和之后将输出设置为“a”变量,那会起作用吗?它安全吗?这是我的宏:
#define subchk(a,b,g) { typeof(a) _a=a; \
asm("":"+rm"(_a)::"cc"); \
asm goto("sub %1,%0;jc %l2"::"r,m,r"(_a),"r,r,m"(b):"cc":g); \
asm("":"+rm"(_a)::"cc"); }
并像这样使用它:
subchk(a,b,underflow)
// normal code with no underflow
// ...
underflow:
// underflow occured here
这有点难看,但效果很好。在我的测试场景中,它只编译 FINE 而没有易失性开销(将寄存器刷新到内存)而不会产生任何不好的东西,而且它似乎工作正常,但这只是一个有限的测试,我不可能在任何地方测试这个我使用这个函数/macro 正如我所说的,它被使用了很多,所以我想知道是否有人知识渊博,上述构造是否有不安全之处?
特别是,如果发生下溢,则不需要“a”的值,因此考虑到这一点,我的内联 asm 宏是否会发生任何副作用或不安全的事情?如果不是,我会毫无问题地使用它,直到他们优化编译器,所以我猜后可以将其替换回来。
请不要将其变成关于过早优化或其他问题的辩论,请继续关注问题的主题,我完全了解这一点,所以谢谢。