Author: rsmith Date: Thu Nov 3 13:55:18 2016 New Revision: 285954 URL: http://llvm.org/viewvc/llvm-project?rev=285954&view=rev Log: 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. Modified: cfe/trunk/include/clang/AST/Expr.h cfe/trunk/lib/AST/Expr.cpp cfe/trunk/lib/CodeGen/CGClass.cpp cfe/trunk/test/CodeGenCXX/devirtualize-virtual-function-calls.cpp Modified: cfe/trunk/include/clang/AST/Expr.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Expr.h?rev=285954&r1=285953&r2=285954&view=diff ============================================================================== --- cfe/trunk/include/clang/AST/Expr.h (original) +++ cfe/trunk/include/clang/AST/Expr.h Thu Nov 3 13:55:18 2016 @@ -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. Modified: cfe/trunk/lib/AST/Expr.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Expr.cpp?rev=285954&r1=285953&r2=285954&view=diff ============================================================================== --- cfe/trunk/lib/AST/Expr.cpp (original) +++ cfe/trunk/lib/AST/Expr.cpp Thu Nov 3 13:55:18 2016 @@ -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(); Modified: cfe/trunk/lib/CodeGen/CGClass.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGClass.cpp?rev=285954&r1=285953&r2=285954&view=diff ============================================================================== --- cfe/trunk/lib/CodeGen/CGClass.cpp (original) +++ cfe/trunk/lib/CodeGen/CGClass.cpp Thu Nov 3 13:55:18 2016 @@ -2842,31 +2842,6 @@ llvm::Value *CodeGenFunction::EmitVTable 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::CanDevirtualizeMemberFu 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::CanDevirtualizeMemberFu 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::CanDevirtualizeMemberFu 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; Modified: cfe/trunk/test/CodeGenCXX/devirtualize-virtual-function-calls.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/devirtualize-virtual-function-calls.cpp?rev=285954&r1=285953&r2=285954&view=diff ============================================================================== --- cfe/trunk/test/CodeGenCXX/devirtualize-virtual-function-calls.cpp (original) +++ cfe/trunk/test/CodeGenCXX/devirtualize-virtual-function-calls.cpp Thu Nov 3 13:55:18 2016 @@ -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(); _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits