I have a C++ program that creates a two-element array of structs, then outputs the a field from one of the structs based on user input.
When user the inputs 1 to the program, it should print 0 since the a field of the second struct has a value of 0. However, after enabling optimizations and compiling with the win32 release of MSVC, it prints out 281474976710656 (2^48) instead.
Reproduction environment:
The bug disappears if I turn off the optimization, change the release to debug, or switch to GCC or Clang.
#include <iostream>
struct MyData
{
int64_t a;
int64_t b;
};
int main()
{
MyData dataList[2]
{
{1000, -1 }, /* change -1 to another number. say 3, the result will be -3 * 2^48 */
{ 0, 0 },
};
int i;
std::cin >> i;
i = i % 2;
int64_t c = -dataList[i].a; /* if we just do : std::cout << -dataList[i].a. the result will be correct...*/
std::cout << c << std::endl;
system("pause");
return 0;
}
I believe this may be a compiler bug. Why is it occurring, and how can I avoid it?
I agree this is a compiler bug. The emitted code is clearly wrong.
At line 1217 of the assembly, with the result of i % 2 in eax, we have:
add eax, eax
mov ecx, DWORD PTR _dataList$[ebp+eax*8]
neg ecx
mov eax, DWORD PTR _dataList$[ebp+eax+4]
adc eax, 0
neg eax
push eax
push ecx
mov ecx, OFFSET std::basic_ostream<char,std::char_traits<char> > std::cout ; std::cout
call std::basic_ostream<char,std::char_traits<char> > & std::basic_ostream<char,std::char_traits<char> >::operator<<(__int64) ; std::basic_ostream<char,std::char_traits<char> >::operator<<
The instruction mov eax, DWORD PTR _dataList$[ebp+eax+4] to load the high half of datalist[i].a is wrong and should be _dataList$[ebp+eax*8+4]. (Note that eax was already multiplied by 2 with add eax, eax, so multiplying by 8 is just what we need to account for the 16-byte size of struct MyData; the load of the low half did it correctly.)
So the load is bytes 6-9 of dataList, rather than 20-23 as it should be. That's the highest two bytes of dataList[0].a (which are 0) and the lowest two bytes of dataList[0].b which are 0xffff. Effectively, the value we end up working with is 0xffff000000000000, which is -2^48, and is then negated into 2^48.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With