It's gotten a lot of attention lately that signed integer overflow is officially undefined in C and C++. However, a given implementation may choose to define it; in C++, an implementation may set std::numeric_limits<signed T>::is_modulo to true to indicate that signed integer overflow is well-defined for that type, and wraps like unsigned integers do.
Visual C++ sets std::numeric_limits<signed int>::is_modulo to true. This has hardly been a reliable indicator, since GCC set this to true for years and has undefined signed overflow. I have never encountered a case in which Visual C++'s optimizer has done anything but give wraparound behavior to signed integers - until earlier this week.
I found a case in which the optimizer emitted x86-64 assembly code that acted improperly if the value of exactly INT_MAX was passed to a particular function. I can't tell whether it's a bug, because Visual C++ doesn't seem to state whether signed integer overflow is considered defined. So I'm wondering, is it supposed to be defined in Visual C++?
EDIT: I found this when reading about a nasty bug in Visual C++ 2013 Update 2 that wasn't in Update 1, where the following loop generates bad machine code if optimizations are enabled:
void func (int *b, int n)
{
for (int i = 0; i < n; i++)
b[i * (n + 1)] = 1;
}
That Update 2 bug results in the repeated line having its code generated as if it were b[i] = 1;, which is clearly wrong. It turned into rep stosd.
What was really interesting was that there was weirdness in the previous version, Update 1. It generated code that didn't properly handle the case that n exactly equaled INT_MAX. Specifically, if n were INT_MAX, the multiplication would act as if n were long long instead of int - in other words, the addition n + 1 would not cause the result to become INT_MIN as it should.
This was the assembly code in Update 1:
movsxd rax, edx ; RDX = 0x000000007FFFFFFF; RAX = 0x000000007FFFFFFF.
test edx, edx
jle short locret_76 ; Branch not taken, because EDX is nonnegative.
lea rdx, ds:4[rax*4] ; RDX = RAX * 4 + 4; RDX becomes 0x0000000200000000.
nop ; But it's wrong. RDX should now be 0xFFFFFFFE00000000.
loc_68:
mov dword ptr [rcx], 1
add rcx, rdx
dec rax
jnz short loc_68
locret_76:
retn
The issue is that I don't know whether this is a compiler bug - in GCC and Clang, this wouldn't be a compiler bug, because those compilers consider signed integer overflow/underflow to be undefined. Whether this is a bug in Visual C++ depends on whether Visual C++ considers signed integer overflow/underflow to be undefined.
Every other case I've seen besides this one has shown Visual C++ to consider signed overflow/underflow to be defined, hence the mystery.