Reputation: 9
So, if I multiply two values: emacs -batch -eval '(print (* 1252463 -4400000000000))' It will exceed most-negative-fixnum frame and will return mathematically wrong answer. What will be the difference in instruction level between -O2 flag, -O2 -fsanitize=undefined flag, and -O2 -fwrapv flag?
Upvotes: 0
Views: 270
Reputation: 2685
In emacs? Probably nothing. The function that is compiled probably looks like this:
int multiply(int x, int y) {
return x * y;
}
If we compile that and look at the assembly (gcc -S multiply.c && cat multiply.s
), we get
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
See the imull
instruction? It's doing a regular multiply. What if we try gcc -O2 -S multiply.c
?
multiply:
movl %edi, %eax
imull %esi, %eax
ret
Well that's certainly removed some code, but it's still doing imull
, a regular multiplication.
Let's try to get it to not use imull
:
int multiply(int x) {
return x * 2;
}
With gcc -O2 -S multiply.c
, we get
multiply:
leal (%rdi,%rdi), %eax
ret
Instead of computing the slower x * 2
, it instead computed x + x
, because addition is faster than multiplication.
Can we get -fwrapv
to do produce different code? Yes:
int multiply(int x) {
return x * 2 < 0;
}
With gcc -O2 -S multiply.c
, we get
multiply:
movl %edi, %eax
shrl $31, %eax
ret
So it was simplified into x >> 31
, which is the same thing as x < 0
. In math, if x * 2 < 0
then x < 0
. But in the reality of processors, if x * 2
overflows it may become negative, for example 2,000,000,000 * 2 = -294967296
.
If you force gcc to take this into account with gcc -O2 -fwrapv -S temp.c
, we get
multiply:
leal (%rdi,%rdi), %eax
shrl $31, %eax
ret
So it optimized x * 2 < 0
to x + x < 0
. It might seem strange to have -fwrapv
not be the default, but C was created before it was standard for multiplication to overflow in this predictable manner.
Upvotes: 2