Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is Visual Studio 2013 optimizing correctly in the presence of /OPT:ICF?

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;
}
like image 275
Thomas Eding Avatar asked Mar 15 '15 03:03

Thomas Eding


2 Answers

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.

like image 200
Shafik Yaghmour Avatar answered Nov 20 '22 13:11

Shafik Yaghmour


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.)

like image 24
Potatoswatter Avatar answered Nov 20 '22 13:11

Potatoswatter