summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--clang/include/clang/AST/Expr.h5
-rw-r--r--clang/lib/AST/Expr.cpp28
-rw-r--r--clang/lib/CodeGen/CGClass.cpp52
-rw-r--r--clang/test/CodeGenCXX/devirtualize-virtual-function-calls.cpp63
4 files changed, 108 insertions, 40 deletions
diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h
index e61e9240a1b..64e363bc98d 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -828,6 +828,11 @@ public:
/// behavior if the object isn't dynamically of the derived type.
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
/// find the expression whose lifetime needs to be extended. Record
/// the LHSs of comma expressions and adjustments needed along the path.
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index 9cf9e86c4f4..53ab5cfd58b 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -35,9 +35,33 @@
#include <cstring>
using namespace clang;
-const CXXRecordDecl *Expr::getBestDynamicClassType() const {
- const Expr *E = ignoreParenBaseCasts();
+const Expr *Expr::getBestDynamicClassTypeExpr() const {
+ 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();
if (const PointerType *PTy = DerivedType->getAs<PointerType>())
DerivedType = PTy->getPointeeType();
diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp
index 99eb5d5c031..2f5855f89be 100644
--- a/clang/lib/CodeGen/CGClass.cpp
+++ b/clang/lib/CodeGen/CGClass.cpp
@@ -2842,31 +2842,6 @@ llvm::Value *CodeGenFunction::EmitVTableTypeCheckedLoad(
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
CodeGenFunction::CanDevirtualizeMemberFunctionCall(const Expr *Base,
const CXXMethodDecl *MD) {
@@ -2880,6 +2855,12 @@ CodeGenFunction::CanDevirtualizeMemberFunctionCall(const Expr *Base,
if (MD->hasAttr<FinalAttr>())
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
// 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>())
return true;
- Base = skipNoOpCastsAndParens(Base);
if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Base)) {
if (const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
// 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()))
return VD->getType()->isRecordType();
- // We can always devirtualize calls on temporary object expressions.
- if (isa<CXXConstructExpr>(Base))
- 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();
+ // Likewise for calls on an object accessed by a (non-reference) pointer to
+ // 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;
+ }
+ }
// We can't devirtualize the call.
return false;
diff --git a/clang/test/CodeGenCXX/devirtualize-virtual-function-calls.cpp b/clang/test/CodeGenCXX/devirtualize-virtual-function-calls.cpp
index 911ddeedd23..f2dc9789725 100644
--- a/clang/test/CodeGenCXX/devirtualize-virtual-function-calls.cpp
+++ b/clang/test/CodeGenCXX/devirtualize-virtual-function-calls.cpp
@@ -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 {
virtual void f();
virtual void f_const() const;
+ virtual void g();
A h();
};
@@ -37,6 +40,64 @@ void f(A a, A *ap, A& ar) {
(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 {
virtual void f();
~B();
OpenPOWER on IntegriCloud