Issue
Here is an example:
namespace {
int a = 0;
}
int main() {
a = 1;
}
GCC and Clang optimized a = 1
out, but MSVC didn't.
Example of output from GCC:
main:
xor eax, eax
ret
Another example:
namespace {
int a = 0;
}
int main() {
a++;
}
None of compilers optimized out a++
: GCC, Clang, MSVC.
Example of output from GCC:
main:
add DWORD PTR _ZN12_GLOBAL__N_11aE[rip], 1
xor eax, eax
ret
Is there some reasoning, why compilers do optimize some global variables, but do not optimize others? It is okay to make an answer for a specific compiler.
Solution
For your first example, MSVC will also optimize out the global (and writing to it) if you use the /GL
flag. Unfortunately, when you do so, it doesn't produce assembly language output, so Godbolt will just tell you the output file is missing. To examine the code, you have to generate an actual executable, then disassemble it, to get:
0000000140001000: 33 C0 xor eax,eax
0000000140001002: C3 ret
...but even /GL
won't optimize out the increment for your second case:
0000000140001000: FF 05 AA 6B 01 00 inc dword ptr [0000000140017BB0h]
0000000140001006: 33 C0 xor eax,eax
0000000140001008: C3 ret
The "why" is hard to answer, simply because it comes down to the motivations of the people working on compiler optimizations. I'd guess, however, that they mostly respond to (at least perceived) needs of their customers. So, for gcc a lot comes down to things like whether a particular optimization will significantly help (or hurt) the performance of popular open source packages such as Linux. Likewise, for MSVC it's largely about popular packages like Windows and Office (and no I don't mean to imply that they ignore their outside customers either).
So in this case, you have code that has a minuscule effect once when the program starts. The big question then is whether that translates into bigger effects on other code that's common enough that they'd have some motivation to optimize it out. I'd guess that at least so far, they simply haven't see many cases where that arises. Given how often most people create and initialize a global variable, and ignore it from then on, my guess is they simply haven't see much reason to work on this particular case. It's optimized only to the extent that it's similar enough to be affected by optimizations they write primarily for other situations. For example, if you add another modification of a
, on this general order:
namespace {
int a = 0;
}
void foo()
{
for (int i=0;i<10;i++)
a++;
}
int main()
{
a++;
foo();
}
...it's probably no surprise that the compiler will turn the separate increment operations into a single assignment:
0000000140001000: C7 05 A6 6B 01 00 mov dword ptr [0000000140017BB0h],0Bh
0B 00 00 00
000000014000100A: 33 C0 xor eax,eax
000000014000100C: C3 ret
But this is just applying basic strength reduction, one of the standard optimizations that's been well known for decades, and most compilers have done just almost that long. I suppose you could make an argument that eliminating the global is just an application of dead code elimination, but being a global variable makes it kind of a special case, so even though they clearly do dead code elimination in general, it doesn't get applied (consistently) in this case.
Answered By - Jerry Coffin