This enables better optimization, I don't if it is legal c++11 though.
Details
- Reviewers
chandlerc rsmith hfinkel - Commits
- rG7cb1b304f8f6: Emit static constexpr member as available_externally definition
rGf23847604b2d: Emit static constexpr member as available_externally definition
rC312512: Emit static constexpr member as available_externally definition
rC311857: Emit static constexpr member as available_externally definition
rL312512: Emit static constexpr member as available_externally definition
rL311857: Emit static constexpr member as available_externally definition
Diff Detail
- Build Status
Buildable 8081 Build 8081: arc lint + arc unit
Event Timeline
Does this apply to all constexpr global variables? It could potentially fix https://bugs.llvm.org/show_bug.cgi?id=31860 .
clang/lib/CodeGen/CodeGenModule.cpp | ||
---|---|---|
2375 | Does getAnyInitializer ever return a null pointer here when D is a c++11 constexpr? |
We've had problems in the past with speculative emission of values like this resulting in us producing invalid symbol references. (Two cases I remember: an available_externally symbol references a symbol that should be emitted as linkonce_odr but which is not emitted in this TU, and an available_externally symbol references a symbol with hidden visibility that is actually defined in a different DSO. In both cases, if the value of the available_externally symbol is actually used, you end up with a program with an invalid symbol reference.)
I don't immediately see any ways that this change would be susceptible to such a problem, so perhaps our best bet is to try it and see if it actually breaks anything.
clang/lib/CodeGen/CodeGenModule.cpp | ||
---|---|---|
2373 | Applying this for only constexpr variables seems unnecessarily conservative; it seems we could equally do this for any variable that is const and has no mutable fields (though we'd need to check for EmitConstantInit failing in the general case). | |
2375 | Only in error cases, which shouldn't get this far. | |
clang/test/CodeGenCXX/cxx11-extern-constexpr.cpp | ||
2 | Please also test what happens in C++1z mode, particularly as static constexpr data members are implicitly inline there. | |
16 | I would imagine that we skip promotion of declaration to definition in this case if we already have a definition. To that end, please add a testcase like this: struct Bar { static constexpr int BAZ = 42; }; auto *use = &Bar::BAZ; const int Bar::BAZ; ... to make sure that we convert the definition of Bar::BAZ from available_externally to a strong definition (I think we should end up with weak_odr here). |
clang/lib/CodeGen/CodeGenModule.cpp | ||
---|---|---|
2373 | OK I will improve, and include a test with a struct with a mutable field. | |
clang/test/CodeGenCXX/cxx11-extern-constexpr.cpp | ||
2 | This is already covered by `clang/test/CodeGenCXX/cxx1z-inline-variables.cpp ` (I actually broke this test during development because I didn't know about inline variable). But I can also add coverage for it here if you'd like. | |
16 | weak_odr in C++17 because it is an inline variable, but I expect a strong definition in c++11. |
clang/test/CodeGenCXX/cxx11-extern-constexpr.cpp | ||
---|---|---|
16 | Well, weak_odr is a kind of strong definition :) Ah, I'd intended to change from emitting this as a strong definition to emitting it as weak_odr even in C++11 (for better forward-compatibility with C++17), but it looks like I never did so. So yes, we'd expect an ExternalLinkage global here (for now) in C++11. |
clang/lib/CodeGen/CodeGenModule.cpp | ||
---|---|---|
2293 | This comment change suggests that when ExcludeCtor is true, a trivial constructor is considered. That's the opposite of what this change does -- namely, a trivial constructor is not considered regardless of the value of ExcludeCtor. | |
2302 | This change looks wrong: you don't know that the default constructor would be used to initialize the object in question, so whether the default constructor is trivial seems like it should have no bearing on the result. I think you can change the code below that uses GV->isConstant() to call isTypeConstant instead, and remove the need for any changes here. | |
2433 | Typo "it's" -> "its" | |
2435 | Instead of GV->isConstant(), it would make more sense to use isTypeConstant(D->getType(), true) here. | |
2437–2445 | This duplicates much of the work done by isTypeConstant. | |
2448–2451 | In order for this transformation to be correct, you need to know that the variable has static initialization, which means that it needs to formally have a constant initializer. You can use D->isInitKnownICE() to check this. |
I'd like to also see a testcase for the situation where we trigger the emission of a declaration with an available_externally definition and then find we need to promote it to a "full" definition:
struct A { static const int Foo = 123; }; int *p = &A::Foo; // emit available_externally const int A::Foo; // convert to full definition
clang/lib/CodeGen/CodeGenModule.cpp | ||
---|---|---|
2437 | Do we need to special-case this? Such declarations are definitions. | |
2448–2451 | You'd need to use an initializer that we can constant-fold, such as: struct A { static const int n; }; bool check() { assert(A::n == 0 && "already initialized!"); return true; } const int A::n = (check() || true) ? 1 : 2; |
I'd like to also see a testcase for the situation where we trigger the emission of a declaration with an available_externally definition and then find we need to promote it to a "full" definition:
Added!
clang/lib/CodeGen/CodeGenModule.cpp | ||
---|---|---|
2437 | We don't! Simplified after rebasing and adapting after John's changes. | |
2448–2451 | But this wouldn't be an "available_externally". We need the initializer in the class definition. I tried multiple variant but couldn't get one that compile: struct B { static bool check() { if (B::n == 0) return false; return true; } // error: in-class initializer for static data member is not a constant expression static const int n = (B::check() || true) ? 1 : 2; }; struct B { static constexpr bool check() { // "error: constexpr function never produces a constant expression" return (B::n == 0 || true) ? false : true; } //"note: initializer of 'n' is not a constant expression" static constexpr int n = (B::check() || true) ? 1 : 2; }; |
This comment change suggests that when ExcludeCtor is true, a trivial constructor is considered. That's the opposite of what this change does -- namely, a trivial constructor is not considered regardless of the value of ExcludeCtor.