I expect the following program to return 0 all of the time. However with Visual Studio 2013 (Update 4), the program exits 1 in release builds. I'm not sure if this is a bug or if the compiler's optimizer is correct and is relying on some edge behavior. If the CONST macro is turned off, the release exe returns 0. If the optimizer is indeed correct, could I get the reason why it is allowed to emit the code it does?
#if 1
# define CONST const
#else
# define CONST
#endif
class TypeId {
public:
bool operator== (TypeId const & other) const
{
return id == other.id;
}
private:
TypeId (void const * id)
: id(id)
{}
public:
template <typename T>
static TypeId Get ()
{
static char CONST uniqueMemLoc = 0;
return TypeId(&uniqueMemLoc);
}
private:
void const * id;
};
int main(int, char **)
{
typedef int A;
typedef unsigned int B;
if (TypeId::Get<A>() == TypeId::Get<B>()) {
return 1;
}
return 0;
}
This does not seem like a valid optimization according to the draft C++11 standard section 14.8
[temp.fct.spec] says (emphasis mine going forward):
Each function template specialization instantiated from a template has its own copy of any static variable. [ Example:
template<class T> void f(T* p) { static T s; }; void g(int a, char* b) { f(&a); // calls f<int>(int*) f(&b); // calls f<char*>(char**) }
Here f(int*) has a static variable s of type int and f<char*>(char**) has a static variable s of type char*. —end example ]
Since your taking the address of the variable folding them effects observable behavior which would violate the as-if rule.
T.C. points out that /opt:noicf
prevents the non-conforming behavior.
Matt McNabb points out that the /OPT (Optimizations) documentation contains the following note:
Because /OPT:ICF can cause the same address to be assigned to different functions or read-only data members (const variables compiled by using /Gy), it can break a program that depends on unique addresses for functions or read-only data members. For more information, see /Gy (Enable Function-Level Linking).
Which suggests this could be intentional non-conforming behavior. Ben Voigt says in a comment now moved to chat that this indeed means the optimizations can be non-conforming but this points is debatable.
User usr linked to an MS blog post: Introducing ‘/Gw’ Compiler Switch and it says:
Please note, the ICF optimization will only be applied for identical COMDATs where their address is not taken, and they are read only. If a data is not address taken, then breaking address uniqueness by ICF won't lead to any observable difference, thus it is valid and conformant to the standard.
and a later comment says:
Even though it's on it's own completely standards complaint, when combined with /Gy potentially breaking behavior can result.
From what I can tell in order for /Gy
to effect const variables __declspec(selectany) has to be used but it could be clearer in the documentation.
At minimum we can see that /Gw
should not introduce non-conforming behavior but /Gy
in combination with /Gw
may.
No, this optimization does not conform to the C++ standard. The declaration of uniqueMemLoc
defines a unique object for each instance of the template, and each object has its own address.
(If you had used a string literal, it would be a different story. The optimization would be valid in that case.)
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