3 ms·
The compiler has to assume that I+10 won’t overflow by virtue of I never being big enough. So, it’ll emit all of the code and UB won’t come into play. It’s mor
by pizlonator 3y ago
The compiler has to assume that I+10 won’t overflow by virtue of I never being big enough. So, it’ll emit all of the code and UB won’t come into play.
It’s more like this. If you say A[I] where I is 32 bit signed and you’re on a 64 bit target, then this lowers to:
- sign extend I to get a 64-bit value
- multiply it by the size of A’s element type
- add that to A
- then do the access
The last three steps will be just one instruction in the common case on arm and x86. The first step will require a separate instruction on x86.
The compiler can kill the sign extend if it’s sure that the integer value cannot be negative. That’s hard to prove. But you can almost prove it if you see code like:
for (int i = 0; something; ++i)
It looks like i starts out as zero and only grows! So it has to be positive! So if you say A[i] then no sign extend needed!
But wait, what if ++i overflows?
With signed int UB, the compiler can just assume it won’t overflow. And then it can prove that i is nonnegative. And then it can kill the sign extend on those CPUs where it’s not free, like x86.
I’m a compiler writer. I know how valuable this optimization is. Namely, it’s the tiniest of benefits on some program/CPU combos. Modern languages like Java or Swift just give this well defined semantics and call it a day because this isn’t a good hill to die on. Fucking up the language isn’t worth 0.3% on some stupid benchmark, period.
- jzwinck 3y agoI agree with this, and I will take it a step further: people who want the compiler to know the ranges of their variables to enable better optimizations should use the various "assume" attributes/builtins which have existed for a long time. The compiler can do way more if it knows your loop count is max 5 than if it knows it is max 2 billion.
- rdtsc 3y agoThank you for detailed explanation. > Modern languages like Java or Swift just give this well defined semantics and call it a day because this isn’t a good hill to die on. Fucking up the language isn’t worth 0.3% on some stupid benchmark, period. I saw some projects just opt to use -fwrapv as a gcc option. clang I think has the same one too now. The docs for gcc mentions [1] "This flag enables some optimizations and disables others" [1] https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html
- pizlonator 3y agoYeah. It would be great if either of these things happened: - signed overflow was defined to wrap like -fwrapv does. - overflow of any kind is guaranteed to trap. The latter would be better for sanity and security but I’ll take what I can get.