[prev in list] [next in list] [prev in thread] [next in thread] 

List:       cfe-commits
Subject:    Re: [PATCH] D11441: Generating available_externally vtables for classes without inline virtual funct
From:       Piotr Padlewski <prazek () google ! com>
Date:       2015-07-23 1:58:23
Message-ID: d0d6b15c64df0437fc59373ab7b3c0e5 () localhost ! localdomain
[Download RAW message or body]

Prazek updated this revision to Diff 30440.
Prazek marked an inline comment as done.

http://reviews.llvm.org/D11441

Files:
  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


["D11441.30440.patch" (text/x-patch)]

Index: test/CodeGenCXX/vtable-linkage.cpp
===================================================================
--- test/CodeGenCXX/vtable-linkage.cpp
+++ test/CodeGenCXX/vtable-linkage.cpp
@@ -139,10 +139,11 @@
 // 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 @@
 // 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 {};
Index: test/CodeGenCXX/vtable-available-externally.cpp
===================================================================
--- test/CodeGenCXX/vtable-available-externally.cpp
+++ test/CodeGenCXX/vtable-available-externally.cpp
@@ -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 @@
   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: lib/CodeGen/MicrosoftCXXABI.cpp
===================================================================
--- lib/CodeGen/MicrosoftCXXABI.cpp
+++ lib/CodeGen/MicrosoftCXXABI.cpp
@@ -106,6 +106,10 @@
                                      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: lib/CodeGen/ItaniumCXXABI.cpp
===================================================================
--- lib/CodeGen/ItaniumCXXABI.cpp
+++ lib/CodeGen/ItaniumCXXABI.cpp
@@ -215,6 +215,8 @@
 
   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 @@
   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 @@
   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.
+  // FIXME we can still emit a copy of the vtable if we
+  // can emit definition of the inline functions.
+  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.
+  return !hasAnyVirtualInlineFunction(RD);
+}
 static llvm::Value *performTypeAdjustment(CodeGenFunction &CGF,
                                           llvm::Value *Ptr,
                                           int64_t NonVirtualAdjustment,
Index: lib/CodeGen/CGVTables.cpp
===================================================================
--- lib/CodeGen/CGVTables.cpp
+++ lib/CodeGen/CGVTables.cpp
@@ -679,6 +679,12 @@
   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 @@
     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 @@
   }
 
   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 @@
 /// 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: lib/CodeGen/CGCXXABI.h
===================================================================
--- lib/CodeGen/CGCXXABI.h
+++ lib/CodeGen/CGCXXABI.h
@@ -218,6 +218,9 @@
   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: include/clang/AST/VTableBuilder.h
===================================================================
--- include/clang/AST/VTableBuilder.h
+++ include/clang/AST/VTableBuilder.h
@@ -205,8 +205,11 @@
 
   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 @@
     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();
   }


_______________________________________________
cfe-commits mailing list
cfe-commits@cs.uiuc.edu
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic