Zero-Cost Well-Defined Signed Integer Overflow in C++
rentry.co
rentry.co
Perhaps you do, but others don't. This compiler behaviour isn't done just for the hell of it, it's done because it's an unavoidable consequence of optimisations that some people rely on that improve performance of valid code. At the same time, other people have almost-valid/invalid code (call it what you will) that behaves as intended when less-optimised, but breaks under these more aggressive optimisations. Whether to prioritise the handling of valid or invalid code is something people will never agree on; everybody will say to prioritise the handling of the type of code that they themselves wrote. I think compilers made the right call in having this as an option so that people can choose what works best for them.
Very little code in practice. Wrapping by default is typically the wrong thing to do. The only reason things like Java get away with it is that when you wrap on overflow and then use it to index into an array there’s an addition bounds check to save you. In C/C++ there are almost no codebases that should not trap, hard, on overflow.
We have entire special libraries for these and we still fail sometimes. Reminds me I have to reconcile a total price mismatch between a C# interface and a Java backend on monday...
But yeah, the normal way to handle an overflow should be an error. Most languages disagree, but I do think nearly every language is wrong here.
On AMD64 you can at least avoid check after each single instruction. So it becomes less expensive.
How?
At my job we have fuzz tests which complain about this class of bugs all the time. So far I haven't been able to prioritize fixing them since there's always bigger fish to fry (and the alignment bugs are more fun to fix anyway!).
In my opinion, using -wrapv to handle signed overflow is a great mistake.
The right compiler option is "-fsanitize=undefined -fsanitize-undefined-trap-on-error".
There is no need to implement any code to handle signed overflow, all decent compilers have appropriate options, which unfortunately are not the default, because too many people prefer speed over correctness.
Of these two, (1) is easily the worst, but where (1) is solved then (2) becomes the worse problem (naturally, being the only problem left).
For example there's a slow looking if statement in there:
if ((_is_neg() && _storage < usmin) ||
(!_is_neg() && _storage > usmax))
But in theory an optimizing compiler could remove it entirely since it can never be true (on C++20 or above).Whether the compiler does this in practice depends on a variety of factors, but in general they're fairly good at spotting "obvious" optimizations.