]> granicus.if.org Git - clang/commitdiff
Generating available_externally vtables for outline virtual functions
authorPiotr Padlewski <prazek@google.com>
Fri, 24 Jul 2015 04:04:49 +0000 (04:04 +0000)
committerPiotr Padlewski <prazek@google.com>
Fri, 24 Jul 2015 04:04:49 +0000 (04:04 +0000)
Generating available_externally vtables for optimizations purposes.
Unfortunatelly ItaniumABI doesn't guarantee that we will be able to
refer to virtual inline method by name.
But when we don't have any inline virtual methods, and key function is
not defined in this TU, we can generate that there will be vtable and
mark it as available_externally.

This is patch will help devirtualize better.
Differential Revision: http://reviews.llvm.org/D11441

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@243090 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/VTableBuilder.h
lib/CodeGen/CGCXXABI.h
lib/CodeGen/CGVTables.cpp
lib/CodeGen/ItaniumCXXABI.cpp
lib/CodeGen/MicrosoftCXXABI.cpp
test/CodeGenCXX/vtable-available-externally.cpp
test/CodeGenCXX/vtable-linkage.cpp

index ebfbb8ad04abd56c87273c4d2d86ab4eb916c183..6d315749c5af65206945e8522e3b51a27a44247c 100644 (file)
@@ -205,8 +205,11 @@ public:
 
   typedef const VTableComponent *vtable_component_iterator;
   typedef const VTableThunkTy *vtable_thunk_iterator;
+  typedef llvm::iterator_range<vtable_component_iterator>
+      vtable_component_range;
 
   typedef llvm::DenseMap<BaseSubobject, uint64_t> AddressPointsMapTy;
+
 private:
   uint64_t NumVTableComponents;
   std::unique_ptr<VTableComponent[]> VTableComponents;
@@ -233,6 +236,11 @@ public:
     return NumVTableComponents;
   }
 
+  vtable_component_range vtable_components() const {
+    return vtable_component_range(vtable_component_begin(),
+                                  vtable_component_end());
+  }
+
   vtable_component_iterator vtable_component_begin() const {
     return VTableComponents.get();
   }
index 436b96a615efaef4b143568145b132353f961403..5ef409ecdeebc4250640fc8fc8269d87ec07ab2e 100644 (file)
@@ -218,6 +218,9 @@ public:
   virtual void emitThrow(CodeGenFunction &CGF, const CXXThrowExpr *E) = 0;
   virtual llvm::GlobalVariable *getThrowInfo(QualType T) { return nullptr; }
 
+  virtual bool canEmitAvailableExternallyVTable(
+      const CXXRecordDecl *RD) const = 0;
+
   virtual void emitBeginCatch(CodeGenFunction &CGF, const CXXCatchStmt *C) = 0;
 
   virtual llvm::CallInst *
index 7321dbfe3408f91a6141bcad57904b07fc5cc25f..c6d7e0632c7af7b25007cc102e5108ad1dbc9606 100644 (file)
@@ -679,6 +679,12 @@ CodeGenVTables::GenerateConstructionVTable(const CXXRecordDecl *RD,
   return VTable;
 }
 
+static bool shouldEmitAvailableExternallyVTable(const CodeGenModule &CGM,
+                                                const CXXRecordDecl *RD) {
+  return CGM.getCodeGenOpts().OptimizationLevel > 0 &&
+            CGM.getCXXABI().canEmitAvailableExternallyVTable(RD);
+}
+
 /// Compute the required linkage of the v-table for the given class.
 ///
 /// Note that we only call this at the end of the translation unit.
@@ -700,7 +706,12 @@ CodeGenModule::getVTableLinkage(const CXXRecordDecl *RD) {
     switch (keyFunction->getTemplateSpecializationKind()) {
       case TSK_Undeclared:
       case TSK_ExplicitSpecialization:
-        assert(def && "Should not have been asked to emit this");
+        assert((def || CodeGenOpts.OptimizationLevel > 0) &&
+               "Shouldn't query vtable linkage without key function or "
+               "optimizations");
+        if (!def && CodeGenOpts.OptimizationLevel > 0)
+          return llvm::GlobalVariable::AvailableExternallyLinkage;
+
         if (keyFunction->isInlined())
           return !Context.getLangOpts().AppleKext ?
                    llvm::GlobalVariable::LinkOnceODRLinkage :
@@ -742,16 +753,18 @@ CodeGenModule::getVTableLinkage(const CXXRecordDecl *RD) {
   }
 
   switch (RD->getTemplateSpecializationKind()) {
-  case TSK_Undeclared:
-  case TSK_ExplicitSpecialization:
-  case TSK_ImplicitInstantiation:
-    return DiscardableODRLinkage;
-
-  case TSK_ExplicitInstantiationDeclaration:
-    return llvm::GlobalVariable::ExternalLinkage;
-
-  case TSK_ExplicitInstantiationDefinition:
-    return NonDiscardableODRLinkage;
+    case TSK_Undeclared:
+    case TSK_ExplicitSpecialization:
+    case TSK_ImplicitInstantiation:
+      return DiscardableODRLinkage;
+
+    case TSK_ExplicitInstantiationDeclaration:
+      return shouldEmitAvailableExternallyVTable(*this, RD)
+                 ? llvm::GlobalVariable::AvailableExternallyLinkage
+                 : llvm::GlobalVariable::ExternalLinkage;
+
+    case TSK_ExplicitInstantiationDefinition:
+      return NonDiscardableODRLinkage;
   }
 
   llvm_unreachable("Invalid TemplateSpecializationKind!");
@@ -819,7 +832,12 @@ bool CodeGenVTables::isVTableExternal(const CXXRecordDecl *RD) {
 /// we define that v-table?
 static bool shouldEmitVTableAtEndOfTranslationUnit(CodeGenModule &CGM,
                                                    const CXXRecordDecl *RD) {
-  return !CGM.getVTables().isVTableExternal(RD);
+  // If vtable is internal then it has to be done
+  if (!CGM.getVTables().isVTableExternal(RD))
+    return true;
+
+  // If it's external then maybe we will need it as available_externally
+  return shouldEmitAvailableExternallyVTable(CGM, RD);
 }
 
 /// Given that at some point we emitted a reference to one or more
index 70af69b76457c5f6d2eb5d995820e515f70f85e4..eb85413a5de094739702d849ae2e5671e969f1df 100644 (file)
@@ -215,6 +215,8 @@ public:
 
   void emitVirtualInheritanceTables(const CXXRecordDecl *RD) override;
 
+  bool canEmitAvailableExternallyVTable(const CXXRecordDecl *RD) const override;
+
   void setThunkLinkage(llvm::Function *Thunk, bool ForVTable, GlobalDecl GD,
                        bool ReturnAdjustment) override {
     // Allow inlining of thunks by emitting them with available_externally
@@ -302,6 +304,24 @@ public:
   friend class ItaniumRTTIBuilder;
 
   void emitCXXStructor(const CXXMethodDecl *MD, StructorType Type) override;
+
+ private:
+  /// Checks if function has any virtual inline function.
+  bool hasAnyVirtualInlineFunction(const CXXRecordDecl *RD) const {
+    const auto &VtableLayout =
+        CGM.getItaniumVTableContext().getVTableLayout(RD);
+
+    for (const auto &VtableComponent : VtableLayout.vtable_components()) {
+      if (VtableComponent.getKind() !=
+          VTableComponent::Kind::CK_FunctionPointer)
+        continue;
+
+      const auto &Method = VtableComponent.getFunctionDecl();
+      if (Method->getCanonicalDecl()->isInlined())
+        return true;
+    }
+    return false;
+  }
 };
 
 class ARMCXXABI : public ItaniumCXXABI {
@@ -1481,6 +1501,19 @@ void ItaniumCXXABI::emitVirtualInheritanceTables(const CXXRecordDecl *RD) {
   VTables.EmitVTTDefinition(VTT, CGM.getVTableLinkage(RD), RD);
 }
 
+bool ItaniumCXXABI::canEmitAvailableExternallyVTable(
+    const CXXRecordDecl *RD) const {
+  // We don't emit available_externally vtables if we are in -fapple-kext mode
+  // because kext mode does not permit devirtualization.
+  if (CGM.getLangOpts().AppleKext)
+    return false;
+
+  // If we don't have any inline virtual functions,
+  // then we are safe to emit available_externally copy of vtable.
+  // FIXME we can still emit a copy of the vtable if we
+  // can emit definition of the inline functions.
+  return !hasAnyVirtualInlineFunction(RD);
+}
 static llvm::Value *performTypeAdjustment(CodeGenFunction &CGF,
                                           llvm::Value *Ptr,
                                           int64_t NonVirtualAdjustment,
index f864213566ade445ad7820dbb9eb32a9a86c7423..613d29a63cd3b21692590b9e99f64f786efaedd1 100644 (file)
@@ -106,6 +106,10 @@ public:
                                      QualType DestTy) override;
 
   bool EmitBadCastCall(CodeGenFunction &CGF) override;
+  bool canEmitAvailableExternallyVTable(
+      const CXXRecordDecl *RD) const override {
+    return false;
+  }
 
   llvm::Value *
   GetVirtualBaseClassOffset(CodeGenFunction &CGF, llvm::Value *This,
index e07d48463f4f80a6a3951aad4857d56f90f6f295..ab090936cbd27d5c4308f1eaeb99336b30d89080 100644 (file)
@@ -1,7 +1,12 @@
-// RUN: %clang_cc1 %s -I%S -triple=x86_64-apple-darwin10 -emit-llvm -o %t 
+// RUN: %clang_cc1 %s -I%S -triple=x86_64-apple-darwin10 -emit-llvm -o %t
+// RUN: %clang_cc1 %s -I%S -triple=x86_64-apple-darwin10 -O2 -disable-llvm-optzns -emit-llvm -o %t.opt
 // RUN: FileCheck --check-prefix=CHECK-TEST1 %s < %t
 // RUN: FileCheck --check-prefix=CHECK-TEST2 %s < %t
 // RUN: FileCheck --check-prefix=CHECK-TEST5 %s < %t
+// RUN: FileCheck --check-prefix=CHECK-TEST8 %s < %t.opt
+// RUN: FileCheck --check-prefix=CHECK-TEST9 %s < %t.opt
+// RUN: FileCheck --check-prefix=CHECK-TEST10 %s < %t.opt
+// RUN: FileCheck --check-prefix=CHECK-TEST11 %s < %t.opt
 
 #include <typeinfo>
 
@@ -152,3 +157,135 @@ struct c28 : virtual c11{
   void f6 ();
 };
 }
+
+namespace Test8 {
+// CHECK-TEST8: @_ZTVN5Test81YE = available_externally unnamed_addr constant
+// vtable for X is not generated because there are no stores here
+struct X {
+  X();
+  virtual void foo();
+};
+struct Y : X {
+  void foo();
+};
+
+void g(X* p) { p->foo(); }
+void f() {
+  Y y;
+  g(&y);
+  X x;
+  g(&x);
+}
+
+}  // Test8
+
+namespace Test9 {
+// all virtual functions are outline, so we can assume that it will
+// be generated in translation unit where foo is defined
+// CHECK-TEST9: @_ZTVN5Test91AE = available_externally unnamed_addr constant
+// CHECK-TEST9: @_ZTVN5Test91BE = available_externally unnamed_addr constant
+struct A {
+  virtual void foo();
+  virtual void bar();
+};
+void A::bar() {}
+
+struct B : A {
+  void foo();
+};
+
+void g() {
+  A a;
+  a.foo();
+  B b;
+  b.foo();
+}
+
+}  // Test9
+
+namespace Test10 {
+
+// because A's key function is defined here, vtable is generated in this TU
+// CHECK-TEST10: @_ZTVN6Test101AE = unnamed_addr constant
+struct A {
+  virtual void foo();
+  virtual void bar();
+};
+void A::foo() {}
+
+// Because key function is inline we will generate vtable as linkonce_odr
+// CHECK-TEST10: @_ZTVN6Test101DE = linkonce_odr unnamed_addr constant
+struct D : A {
+  void bar();
+};
+inline void D::bar() {}
+
+// because B has outline key function then we can refer to
+// CHECK-TEST10: @_ZTVN6Test101BE = available_externally unnamed_addr constant
+struct B : A {
+  void foo();
+  void bar();
+};
+
+// C's key function (car) is outline, but C has inline virtual function so we
+// can't guarantee that we will be able to refer to bar from name
+// so (at the moment) we can't emit vtable available_externally
+// CHECK-TEST10: @_ZTVN6Test101CE = external unnamed_addr constant
+struct C : A {
+  void bar() {}               // defined in body - not key function
+  virtual inline void gar();  // inline in body - not key function
+  virtual void car();
+};
+
+// no key function, vtable will be generated everywhere it will be used
+// CHECK-TEST10: @_ZTVN6Test101EE = linkonce_odr unnamed_addr constant
+struct E : A {};
+
+void g(A& a) {
+  a.foo();
+  a.bar();
+}
+
+void f() {
+  A a;
+  g(a);
+  B b;
+  g(b);
+  C c;
+  g(c);
+  D d;
+  g(d);
+  E e;
+  g(e);
+}
+
+}  // Test10
+
+namespace Test11 {
+struct D;
+// Can emit C's vtable available_externally.
+// CHECK-TEST11: @_ZTVN6Test111CE = available_externally unnamed_addr constant
+struct C {
+  virtual D& operator=(const D&);
+};
+
+// Cannot emit B's vtable available_externally, because we cannot create
+// a reference to the inline virtual B::operator= function.
+// CHECK-TEST11: @_ZTVN6Test111DE = external unnamed_addr constant
+struct D : C {
+  virtual void key();
+};
+D f();
+
+void g(D& a) {
+  C c;
+  c = a;
+  a.key();
+  a.key();
+}
+void g() {
+  D d;
+  d = f();
+  g(d);
+}
+}  // Test 11
index 5cbd3897a1ffd195a3af03f701acddef0679abc1..ff398ffa61dd1a2ba070217939de600ea994948a 100644 (file)
@@ -139,10 +139,11 @@ void use_F() {
 // CHECK-OPT-DAG: @_ZTV1FIiE = external unnamed_addr constant
 
 // E<int> is an explicit template instantiation declaration. It has a
-// key function that is not instantiated, so we should only reference
-// its vtable, not define it.
+// key function is not instantiated, so we know that vtable definition
+// will be generated in TU where key function will be defined
+// so we can mark it as available_externally (only with optimizations)
 // CHECK-DAG: @_ZTV1EIiE = external unnamed_addr constant
-// CHECK-OPT-DAG: @_ZTV1EIiE = external unnamed_addr constant
+// CHECK-OPT-DAG: @_ZTV1EIiE = available_externally unnamed_addr constant
 
 // The anonymous struct for e has no linkage, so the vtable should have
 // internal linkage.
@@ -196,8 +197,8 @@ void use_H() {
 // CHECK-DAG: @_ZTT1IIiE = external unnamed_addr constant
 // CHECK-NOT: @_ZTC1IIiE
 //
-// CHECK-OPT-DAG: @_ZTV1IIiE = external unnamed_addr constant
-// CHECK-OPT-DAG: @_ZTT1IIiE = external unnamed_addr constant
+// CHECK-OPT-DAG: @_ZTV1IIiE = available_externally unnamed_addr constant
+// CHECK-OPT-DAG: @_ZTT1IIiE = available_externally unnamed_addr constant
 struct VBase1 { virtual void f(); }; struct VBase2 : virtual VBase1 {};
 template<typename T>
 struct I : VBase2 {};