所以,如果我乘以两个值: emacs -batch -eval'(print(* 1252463 -4400000000000))' 它将超过大多数负面的fixnum框架,并将在数学上返回错误的答案。指令级别之间的差异是什么? -O2标志,-O2 -fsanitize =未定义标志,-O2 -fwrapv标志?
答案 0 :(得分:2)
在emacs中?可能没什么。编译的函数可能如下所示:
int multiply(int x, int y) {
return x * y;
}
如果我们编译它并查看程序集(gcc -S multiply.c && cat multiply.s
),我们得到
multiply:
pushq %rbp
movq %rsp, %rbp
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
movl -4(%rbp), %eax
imull -8(%rbp), %eax
popq %rbp
ret
请参阅imull
说明?它正在经常繁殖。如果我们尝试gcc -O2 -S multiply.c
怎么办?
multiply:
movl %edi, %eax
imull %esi, %eax
ret
嗯,这肯定删除了一些代码,但它仍然在进行imull
,这是一个常规的乘法。
让我们尝试不使用imull
:
int multiply(int x) {
return x * 2;
}
使用gcc -O2 -S multiply.c
,我们得到
multiply:
leal (%rdi,%rdi), %eax
ret
而不是计算较慢的x * 2
,而是计算x + x
,因为加法比乘法更快。
我们可以让-fwrapv
生成不同的代码吗?是:
int multiply(int x) {
return x * 2 < 0;
}
使用gcc -O2 -S multiply.c
,我们得到
multiply:
movl %edi, %eax
shrl $31, %eax
ret
因此它被简化为x >> 31
,这与x < 0
相同。在数学中,如果是x * 2 < 0
,那么x < 0
。但在处理器的现实中,如果x * 2
溢出,它可能会变为负数,例如2,000,000,000 * 2 = -294967296
。
如果您强制gcc将gcc -O2 -fwrapv -S temp.c
考虑在内,我们就会
multiply:
leal (%rdi,%rdi), %eax
shrl $31, %eax
ret
所以它优化x * 2 < 0
到x + x < 0
。让-fwrapv
不是默认值可能看起来很奇怪,但是C是在标准乘法以这种可预测的方式溢出之前创建的。