This is an archive of the discontinued LLVM Phabricator instance.

Devirtualize destructor of final class.
ClosedPublic

Authored by hjyamauchi on Jun 11 2019, 1:12 PM.

Diff Detail

Repository
rL LLVM

Event Timeline

hjyamauchi created this revision.Jun 11 2019, 1:12 PM
Herald added a project: Restricted Project. · View Herald TranscriptJun 11 2019, 1:12 PM
Herald added a subscriber: Prazek. · View Herald Transcript
davidxl edited reviewers, added: rsmith; removed: davidxl.Jun 11 2019, 1:22 PM
davidxl added a subscriber: davidxl.
rsmith added inline comments.Jun 11 2019, 1:28 PM
lib/CodeGen/CGExprCXX.cpp
1867–1868 ↗(On Diff #204143)

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

Using getDevirtualizedMethod.

hjyamauchi marked 2 inline comments as done.Jun 12 2019, 3:07 PM
hjyamauchi added inline comments.
lib/CodeGen/CGExprCXX.cpp
1867–1868 ↗(On Diff #204143)

Updated. Hopefully this does the right thing.

rsmith added inline comments.Jun 13 2019, 12:38 PM
lib/CodeGen/CGExprCXX.cpp
1871 ↗(On Diff #204365)

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.

hjyamauchi marked an inline comment as done.

Addressed comment.

hjyamauchi marked an inline comment as done.Jun 14 2019, 2:52 PM
hjyamauchi added inline comments.
lib/CodeGen/CGExprCXX.cpp
1871 ↗(On Diff #204365)

Should be good now.

rsmith added inline comments.Jun 14 2019, 3:00 PM
lib/CodeGen/CGExprCXX.cpp
1887 ↗(On Diff #204852)

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.

1867–1868 ↗(On Diff #204143)

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.

hjyamauchi marked 2 inline comments as done.Jun 17 2019, 3:21 PM
hjyamauchi added inline comments.
lib/CodeGen/CGExprCXX.cpp
1887 ↗(On Diff #204852)

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?

rsmith added inline comments.Jun 17 2019, 4:09 PM
lib/CodeGen/CGExprCXX.cpp
1887 ↗(On Diff #204852)

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

hjyamauchi marked 2 inline comments as done.Jun 19 2019, 9:44 AM
rsmith accepted this revision.Jun 20 2019, 1:59 PM

Thanks! Minor comment then feel free to commit.

lib/CodeGen/CGExprCXX.cpp
1877 ↗(On Diff #205381)

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.

This revision is now accepted and ready to land.Jun 20 2019, 1:59 PM
rsmith added inline comments.Jun 20 2019, 2:00 PM
test/CodeGenCXX/devirtualize-dtor-final.cpp
20 ↗(On Diff #205381)

Missing space after void here

hjyamauchi marked 2 inline comments as done.

Comments addressed.

This revision was automatically updated to reflect the committed changes.
Herald added a project: Restricted Project. · View Herald TranscriptJun 21 2019, 1:10 PM

You, @rdhindsa, should report here why you reverted this commit.

It is really weird to revert random commits without more information.

xbolva00 reopened this revision.Jul 4 2019, 2:09 PM
This revision is now accepted and ready to land.Jul 4 2019, 2:09 PM

This was reverted due to some internal test failure. But it turned out a false alarm. I'll work on recommitting it.

hjyamauchi closed this revision.Jul 11 2019, 8:56 AM