r285954 - Improve obvious-most-derived-type devirtualization:

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Thu Nov 3 11:55:19 PDT 2016


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();




More information about the cfe-commits mailing list