forked from OSchip/llvm-project
Improve obvious-most-derived-type devirtualization:
* if the base is produced by a series of derived-to-base conversions, check the expression inside them when looking for an expression with a known dynamic type * step past MaterializeTemporaryExprs when checking for a known dynamic type * when checking for a known dynamic type, treat all class prvalues as having a known dynamic type after skipping all relevant rvalue subobject adjustments * treat callees formed by pointer-to-member access for a non-reference member type like callees formed by member access. llvm-svn: 285954
This commit is contained in:
parent
f9fb2abb01
commit
018ac39f94
|
@ -828,6 +828,11 @@ public:
|
||||||
/// behavior if the object isn't dynamically of the derived type.
|
/// behavior if the object isn't dynamically of the derived type.
|
||||||
const CXXRecordDecl *getBestDynamicClassType() const;
|
const CXXRecordDecl *getBestDynamicClassType() const;
|
||||||
|
|
||||||
|
/// \brief Get the inner expression that determines the best dynamic class.
|
||||||
|
/// If this is a prvalue, we guarantee that it is of the most-derived type
|
||||||
|
/// for the object itself.
|
||||||
|
const Expr *getBestDynamicClassTypeExpr() const;
|
||||||
|
|
||||||
/// Walk outwards from an expression we want to bind a reference to and
|
/// Walk outwards from an expression we want to bind a reference to and
|
||||||
/// find the expression whose lifetime needs to be extended. Record
|
/// find the expression whose lifetime needs to be extended. Record
|
||||||
/// the LHSs of comma expressions and adjustments needed along the path.
|
/// the LHSs of comma expressions and adjustments needed along the path.
|
||||||
|
|
|
@ -35,9 +35,33 @@
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
using namespace clang;
|
using namespace clang;
|
||||||
|
|
||||||
const CXXRecordDecl *Expr::getBestDynamicClassType() const {
|
const Expr *Expr::getBestDynamicClassTypeExpr() const {
|
||||||
const Expr *E = ignoreParenBaseCasts();
|
const Expr *E = this;
|
||||||
|
while (true) {
|
||||||
|
E = E->ignoreParenBaseCasts();
|
||||||
|
|
||||||
|
// Follow the RHS of a comma operator.
|
||||||
|
if (auto *BO = dyn_cast<BinaryOperator>(E)) {
|
||||||
|
if (BO->getOpcode() == BO_Comma) {
|
||||||
|
E = BO->getRHS();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step into initializer for materialized temporaries.
|
||||||
|
if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(E)) {
|
||||||
|
E = MTE->GetTemporaryExpr();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return E;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CXXRecordDecl *Expr::getBestDynamicClassType() const {
|
||||||
|
const Expr *E = getBestDynamicClassTypeExpr();
|
||||||
QualType DerivedType = E->getType();
|
QualType DerivedType = E->getType();
|
||||||
if (const PointerType *PTy = DerivedType->getAs<PointerType>())
|
if (const PointerType *PTy = DerivedType->getAs<PointerType>())
|
||||||
DerivedType = PTy->getPointeeType();
|
DerivedType = PTy->getPointeeType();
|
||||||
|
|
|
@ -2842,31 +2842,6 @@ llvm::Value *CodeGenFunction::EmitVTableTypeCheckedLoad(
|
||||||
cast<llvm::PointerType>(VTable->getType())->getElementType());
|
cast<llvm::PointerType>(VTable->getType())->getElementType());
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Ideally Expr::IgnoreParenNoopCasts should do this, but it doesn't do
|
|
||||||
// quite what we want.
|
|
||||||
static const Expr *skipNoOpCastsAndParens(const Expr *E) {
|
|
||||||
while (true) {
|
|
||||||
if (const ParenExpr *PE = dyn_cast<ParenExpr>(E)) {
|
|
||||||
E = PE->getSubExpr();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (const CastExpr *CE = dyn_cast<CastExpr>(E)) {
|
|
||||||
if (CE->getCastKind() == CK_NoOp) {
|
|
||||||
E = CE->getSubExpr();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (const UnaryOperator *UO = dyn_cast<UnaryOperator>(E)) {
|
|
||||||
if (UO->getOpcode() == UO_Extension) {
|
|
||||||
E = UO->getSubExpr();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return E;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
CodeGenFunction::CanDevirtualizeMemberFunctionCall(const Expr *Base,
|
CodeGenFunction::CanDevirtualizeMemberFunctionCall(const Expr *Base,
|
||||||
const CXXMethodDecl *MD) {
|
const CXXMethodDecl *MD) {
|
||||||
|
@ -2880,6 +2855,12 @@ CodeGenFunction::CanDevirtualizeMemberFunctionCall(const Expr *Base,
|
||||||
if (MD->hasAttr<FinalAttr>())
|
if (MD->hasAttr<FinalAttr>())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
// If the base expression (after skipping derived-to-base conversions) is a
|
||||||
|
// class prvalue, then we can devirtualize.
|
||||||
|
Base = Base->getBestDynamicClassTypeExpr();
|
||||||
|
if (Base->isRValue() && Base->getType()->isRecordType())
|
||||||
|
return true;
|
||||||
|
|
||||||
// If the most derived class is marked final, we know that no subclass can
|
// If the most derived class is marked final, we know that no subclass can
|
||||||
// override this member function and so we can devirtualize it. For example:
|
// override this member function and so we can devirtualize it. For example:
|
||||||
//
|
//
|
||||||
|
@ -2907,7 +2888,6 @@ CodeGenFunction::CanDevirtualizeMemberFunctionCall(const Expr *Base,
|
||||||
if (MD->getParent()->hasAttr<FinalAttr>())
|
if (MD->getParent()->hasAttr<FinalAttr>())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
Base = skipNoOpCastsAndParens(Base);
|
|
||||||
if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Base)) {
|
if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Base)) {
|
||||||
if (const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
|
if (const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
|
||||||
// This is a record decl. We know the type and can devirtualize it.
|
// This is a record decl. We know the type and can devirtualize it.
|
||||||
|
@ -2924,17 +2904,15 @@ CodeGenFunction::CanDevirtualizeMemberFunctionCall(const Expr *Base,
|
||||||
if (const ValueDecl *VD = dyn_cast<ValueDecl>(ME->getMemberDecl()))
|
if (const ValueDecl *VD = dyn_cast<ValueDecl>(ME->getMemberDecl()))
|
||||||
return VD->getType()->isRecordType();
|
return VD->getType()->isRecordType();
|
||||||
|
|
||||||
// We can always devirtualize calls on temporary object expressions.
|
// Likewise for calls on an object accessed by a (non-reference) pointer to
|
||||||
if (isa<CXXConstructExpr>(Base))
|
// member access.
|
||||||
|
if (auto *BO = dyn_cast<BinaryOperator>(Base)) {
|
||||||
|
if (BO->isPtrMemOp()) {
|
||||||
|
auto *MPT = BO->getRHS()->getType()->castAs<MemberPointerType>();
|
||||||
|
if (MPT->getPointeeType()->isRecordType())
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
// And calls on bound temporaries.
|
}
|
||||||
if (isa<CXXBindTemporaryExpr>(Base))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Check if this is a call expr that returns a record type.
|
|
||||||
if (const CallExpr *CE = dyn_cast<CallExpr>(Base))
|
|
||||||
return CE->getCallReturnType(getContext())->isRecordType();
|
|
||||||
|
|
||||||
// We can't devirtualize the call.
|
// We can't devirtualize the call.
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
// RUN: %clang_cc1 %s -triple armv7-none-eabi -emit-llvm -o - | FileCheck %s
|
// RUN: %clang_cc1 -std=c++98 %s -triple armv7-none-eabi -emit-llvm -o - | FileCheck %s
|
||||||
|
// RUN: %clang_cc1 -std=c++11 %s -triple armv7-none-eabi -emit-llvm -o - | FileCheck %s
|
||||||
|
// RUN: %clang_cc1 -std=c++1z %s -triple armv7-none-eabi -emit-llvm -o - | FileCheck %s
|
||||||
|
|
||||||
struct A {
|
struct A {
|
||||||
virtual void f();
|
virtual void f();
|
||||||
virtual void f_const() const;
|
virtual void f_const() const;
|
||||||
|
virtual void g();
|
||||||
|
|
||||||
A h();
|
A h();
|
||||||
};
|
};
|
||||||
|
@ -37,6 +40,64 @@ void f(A a, A *ap, A& ar) {
|
||||||
(a).f();
|
(a).f();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct D : A { virtual void g(); };
|
||||||
|
struct XD { D d; };
|
||||||
|
|
||||||
|
D gd();
|
||||||
|
|
||||||
|
void fd(D d, XD xd, D *p) {
|
||||||
|
// CHECK: call void @_ZN1A1fEv(%struct.A*
|
||||||
|
d.f();
|
||||||
|
|
||||||
|
// CHECK: call void @_ZN1D1gEv(%struct.D*
|
||||||
|
d.g();
|
||||||
|
|
||||||
|
// CHECK: call void @_ZN1A1fEv
|
||||||
|
D().f();
|
||||||
|
|
||||||
|
// CHECK: call void @_ZN1D1gEv
|
||||||
|
D().g();
|
||||||
|
|
||||||
|
// CHECK: call void @_ZN1A1fEv
|
||||||
|
gd().f();
|
||||||
|
|
||||||
|
// CHECK: call void @_ZNK1A7f_constEv
|
||||||
|
d.f_const();
|
||||||
|
|
||||||
|
// CHECK: call void @_ZN1A1fEv
|
||||||
|
(d).f();
|
||||||
|
|
||||||
|
// CHECK: call void @_ZN1A1fEv
|
||||||
|
(true, d).f();
|
||||||
|
|
||||||
|
// CHECK: call void @_ZN1D1gEv
|
||||||
|
(true, d).g();
|
||||||
|
|
||||||
|
// CHECK: call void @_ZN1A1fEv
|
||||||
|
xd.d.f();
|
||||||
|
|
||||||
|
// CHECK: call void @_ZN1A1fEv
|
||||||
|
XD().d.f();
|
||||||
|
|
||||||
|
// CHECK: call void @_ZN1A1fEv
|
||||||
|
D XD::*mp;
|
||||||
|
(xd.*mp).f();
|
||||||
|
|
||||||
|
// CHECK: call void @_ZN1D1gEv
|
||||||
|
(xd.*mp).g();
|
||||||
|
|
||||||
|
// Can't devirtualize this; we have no guarantee that p points to a D here,
|
||||||
|
// due to the "single object is considered to be an array of one element"
|
||||||
|
// rule.
|
||||||
|
// CHECK: call void %
|
||||||
|
p[0].f();
|
||||||
|
|
||||||
|
// FIXME: We can devirtualize this, by C++1z [expr.add]/6 (if the array
|
||||||
|
// element type and the pointee type are not similar, behavior is undefined).
|
||||||
|
// CHECK: call void %
|
||||||
|
p[1].f();
|
||||||
|
}
|
||||||
|
|
||||||
struct B {
|
struct B {
|
||||||
virtual void f();
|
virtual void f();
|
||||||
~B();
|
~B();
|
||||||
|
|
Loading…
Reference in New Issue