使用emacs lisp时,如果该值超过最大负数

时间:2015-10-27 21:23:55

标签: emacs elisp x86-64

所以,如果我乘以两个值: emacs -batch -eval'(print(* 1252463 -4400000000000))' 它将超过大多数负面的fixnum框架,并将在数学上返回错误的答案。指令级别之间的差异是什么? -O2标志,-O2 -fsanitize =未定义标志,-O2 -fwrapv标志?

1 个答案:

答案 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 < 0x + x < 0。让-fwrapv不是默认值可能看起来很奇怪,但是C是在标准乘法以这种可预测的方式溢出之前创建的。