Take advantage of the final keyword to devirtualize destructor calls.
Details
Diff Detail
- Repository
- rC Clang
- Build Status
Buildable 33426 Build 33425: arc lint + arc unit
Event Timeline
lib/CodeGen/CGExprCXX.cpp | ||
---|---|---|
1867–1868 | Can we use getDevirtualizedMethod here instead? That catches a few other cases that just checking for final will miss, and disables this for cases where it will currently do the wrong thing (eg, -fapple-kext). See EmitCXXMemberOrOperatorMemberCallExpr for where we do this for other virtual calls. As a particularly terrible example: struct A { virtual ~A() final = 0; }; void evil(A *p) { delete p; } is (amazingly) valid, and must not be devirtualized because there is no requirement that the destructor of A have a definition in this program. (It would, however, be correct to optimize out the entire delete expression in this case, on the basis that p must always be nullptr whenever it's reached. But that doesn't really seem worthwhile.) |
lib/CodeGen/CGExprCXX.cpp | ||
---|---|---|
1867–1868 | Updated. Hopefully this does the right thing. |
lib/CodeGen/CGExprCXX.cpp | ||
---|---|---|
1871 | Dtor could be the destructor for a type derived from ElementPtr. We'll either need to somehow emit a cast to the correct derived type here, or just abort the devirtualization and emit a virtual call in that case. (The latter is what EmitCXXMemberOrOperatorMemberCallExpr does -- see its checks on getCXXRecord(Base) and getCXXRecord(Inner).) Eg, for: struct SomethingElse { virtual void f(); }; struct A { virtual ~A(); }; struct B : SomethingElse, A { ~B() final; }; void f(B *p) { delete (A*)p; } ... Ptr will be a pointer to an A subobject of a B object, but getDevirtualizedMethod will still be able to work out that B::~B is the right thing to call here (by looking through the cast and finding that the pointer must actually point to a B object whose destructor is final). We need to either convert Ptr back from an A* to a B* or just not devirtualize this call. |
lib/CodeGen/CGExprCXX.cpp | ||
---|---|---|
1871 | Should be good now. |
lib/CodeGen/CGExprCXX.cpp | ||
---|---|---|
1867–1868 | Please can you add that test to your testcases too (eg, with a CHECK-NOT to ensure we don't reference the destructor of A)? It's subtle enough that I could imagine us getting that wrong in the future. | |
1887 | In this case we'll emit the inner expression (including its side-effects) twice. The simplest thing to do would be to just drop this else if case for now and add a FIXME. |
lib/CodeGen/CGExprCXX.cpp | ||
---|---|---|
1887 | I'd go with that. I assume there isn't a simple way to adjust the this pointer of a base class given a derived class? Sort of like CodeGenFunction::GetAddressOfDerivedClass? |
lib/CodeGen/CGExprCXX.cpp | ||
---|---|---|
1887 | I don't think we have anything quite like that. This won't be possible in the general case: there could be more than one base class of the current type within the derived class, so you'd need to take into account the path that the base expression took to reach the inner expression with the known dynamic type. (We could opportunistically do this in the common case where the derived class has only one base class of the base type, and it's a non-virtual base, but I don't think we have any existing code to do that either.) |
Thanks! Minor comment then feel free to commit.
lib/CodeGen/CGExprCXX.cpp | ||
---|---|---|
1877 | There's no guarantee that you get the same declaration of the class in both cases; use declaresSameEntity here instead of == to compare whether you have the same class. |
test/CodeGenCXX/devirtualize-dtor-final.cpp | ||
---|---|---|
21 | Missing space after void here |
You, @rdhindsa, should report here why you reverted this commit.
It is really weird to revert random commits without more information.
This was reverted due to some internal test failure. But it turned out a false alarm. I'll work on recommitting it.
Can we use getDevirtualizedMethod here instead? That catches a few other cases that just checking for final will miss, and disables this for cases where it will currently do the wrong thing (eg, -fapple-kext). See EmitCXXMemberOrOperatorMemberCallExpr for where we do this for other virtual calls.
As a particularly terrible example:
is (amazingly) valid, and must not be devirtualized because there is no requirement that the destructor of A have a definition in this program. (It would, however, be correct to optimize out the entire delete expression in this case, on the basis that p must always be nullptr whenever it's reached. But that doesn't really seem worthwhile.)