]> granicus.if.org Git - clang/commitdiff
When emitting the destructor for a class with a vtable, if we can determine
authorAnders Carlsson <andersca@mac.com>
Sat, 14 May 2011 23:26:09 +0000 (23:26 +0000)
committerAnders Carlsson <andersca@mac.com>
Sat, 14 May 2011 23:26:09 +0000 (23:26 +0000)
that the destructor body is trivial and that all member variables also have either
trivial destructors or trivial destructor bodies, we don't need to initialize the
vtable pointers since no virtual member functions will be called on the destructor.

Fixes PR9181.

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

include/clang/AST/Decl.h
lib/AST/Decl.cpp
lib/CodeGen/CGCXX.cpp
lib/CodeGen/CGClass.cpp
test/CodeGenCXX/mangle-subst-std.cpp
test/CodeGenCXX/skip-vtable-pointer-initialization.cpp [new file with mode: 0644]

index 193c3286f810137c4907fd7cac66620a9f6a7e2b..6ca14ce8749645f0664b8106a22d40bcde91f3a9 100644 (file)
@@ -1515,6 +1515,10 @@ public:
     return hasBody(Definition);
   }
 
+  /// hasTrivialBody - Returns whether the function has a trivial body that does
+  /// not require any specific codegen.
+  bool hasTrivialBody() const;
+
   /// isDefined - Returns true if the function is defined at all, including
   /// a deleted definition. Except for the behavior when the function is
   /// deleted, behaves like hasBody.
index b57c6ab7d76df854bcf88225e5295aa377fe61a1..d4df017383d1054a82f901cac0ca644cd5899b3b 100644 (file)
@@ -1422,6 +1422,20 @@ bool FunctionDecl::hasBody(const FunctionDecl *&Definition) const {
   return false;
 }
 
+bool FunctionDecl::hasTrivialBody() const
+{
+  Stmt *S = getBody();
+  if (!S) {
+    // Since we don't have a body for this function, we don't know if it's
+    // trivial or not.
+    return false;
+  }
+
+  if (isa<CompoundStmt>(S) && cast<CompoundStmt>(S)->body_empty())
+    return true;
+  return false;
+}
+
 bool FunctionDecl::isDefined(const FunctionDecl *&Definition) const {
   for (redecl_iterator I = redecls_begin(), E = redecls_end(); I != E; ++I) {
     if (I->IsDeleted || I->Body || I->IsLateTemplateParsed) {
index 4390f3a3e3e940b45d7147fe46b04c067d426aa4..f6fc202eaae230baa9984c728775ef9cd300e4ce 100644 (file)
 using namespace clang;
 using namespace CodeGen;
 
-/// Determines whether the given function has a trivial body that does
-/// not require any specific codegen.
-static bool HasTrivialBody(const FunctionDecl *FD) {
-  Stmt *S = FD->getBody();
-  if (!S)
-    return true;
-  if (isa<CompoundStmt>(S) && cast<CompoundStmt>(S)->body_empty())
-    return true;
-  return false;
-}
-
 /// Try to emit a base destructor as an alias to its primary
 /// base-class destructor.
 bool CodeGenModule::TryEmitBaseDestructorAsAlias(const CXXDestructorDecl *D) {
@@ -47,7 +36,7 @@ bool CodeGenModule::TryEmitBaseDestructorAsAlias(const CXXDestructorDecl *D) {
 
   // If the destructor doesn't have a trivial body, we have to emit it
   // separately.
-  if (!HasTrivialBody(D))
+  if (!D->hasTrivialBody())
     return true;
 
   const CXXRecordDecl *Class = D->getParent();
index 4172948b6023acbd4d2c40d0bbce9a3d34a4c227..785b9e129a1a317ed2c52986d2683f0b0db51e27 100644 (file)
@@ -741,6 +741,38 @@ void CodeGenFunction::EmitCtorPrologue(const CXXConstructorDecl *CD,
     EmitMemberInitializer(*this, ClassDecl, MemberInitializers[I], CD, Args);
 }
 
+/// CanSkipVTablePointerInitialization - Check whether we need to initialize
+/// any vtable pointers before calling this destructor.
+static bool CanSkipVTablePointerInitialization(ASTContext &Context,
+                                           const CXXDestructorDecl *Dtor) {
+  if (!Dtor->hasTrivialBody())
+    return false;
+
+  // Check the fields.
+  const CXXRecordDecl *ClassDecl = Dtor->getParent();
+  for (CXXRecordDecl::field_iterator I = ClassDecl->field_begin(),
+       E = ClassDecl->field_end(); I != E; ++I) {
+    const FieldDecl *Field = *I;
+    
+    QualType FieldBaseElementType = 
+      Context.getBaseElementType(Field->getType());
+
+    const RecordType *RT = FieldBaseElementType->getAs<RecordType>();
+    if (!RT)
+      continue;
+    
+    CXXRecordDecl *FieldClassDecl = cast<CXXRecordDecl>(RT->getDecl());
+    if (FieldClassDecl->hasTrivialDestructor())
+      continue;
+    if (FieldClassDecl->getDestructor()->hasTrivialBody())
+      continue;
+
+    return false;
+  }
+
+  return true;
+}
+
 /// EmitDestructorBody - Emits the body of the current destructor.
 void CodeGenFunction::EmitDestructorBody(FunctionArgList &Args) {
   const CXXDestructorDecl *Dtor = cast<CXXDestructorDecl>(CurGD.getDecl());
@@ -792,7 +824,8 @@ void CodeGenFunction::EmitDestructorBody(FunctionArgList &Args) {
     EnterDtorCleanups(Dtor, Dtor_Base);
 
     // Initialize the vtable pointers before entering the body.
-    InitializeVTablePointers(Dtor->getParent());
+    if (!CanSkipVTablePointerInitialization(getContext(), Dtor))
+        InitializeVTablePointers(Dtor->getParent());
 
     if (isTryBody)
       EmitStmt(cast<CXXTryStmt>(Body)->getTryBlock());
index fea3582d321a38efce41d5ee1d942eb753246d88..c54d3e5297f0e2d1cc1cd81dd656a2d394bc1bd1 100644 (file)
@@ -8,8 +8,8 @@
 // CHECK: @_ZTCSd0_Si = linkonce_odr unnamed_addr constant 
 // CHECK: @_ZTCSd16_So = linkonce_odr unnamed_addr constant
 // CHECK: @_ZTTSo = linkonce_odr unnamed_addr constant
-// CHECK: @_ZTVSo = linkonce_odr unnamed_addr constant
 // CHECK: @_ZTTSi = linkonce_odr unnamed_addr constant
+// CHECK: @_ZTVSo = linkonce_odr unnamed_addr constant
 // CHECK: @_ZTVSi = linkonce_odr unnamed_addr constant
 namespace std {
   struct A { A(); };
diff --git a/test/CodeGenCXX/skip-vtable-pointer-initialization.cpp b/test/CodeGenCXX/skip-vtable-pointer-initialization.cpp
new file mode 100644 (file)
index 0000000..f992bd3
--- /dev/null
@@ -0,0 +1,106 @@
+// RUN: %clang_cc1 %s -triple=x86_64-apple-darwin10 -emit-llvm -o - | FileCheck %s
+
+namespace Test1 {
+
+// Check that we don't initialize the vtable pointer in A::~A(), since the destructor body is trivial.
+struct A {
+  virtual void f();
+  ~A();
+};
+
+// CHECK: define void @_ZN5Test11AD2Ev
+// CHECK-NOT: store i8** getelementptr inbounds ([3 x i8*]* @_ZTVN5Test11AE, i64 0, i64 2), i8***
+A::~A() 
+{
+}
+
+}
+
+namespace Test2 {
+
+// Check that we do initialize the vtable pointer in A::~A() since the destructor body isn't trivial.
+struct A {
+  virtual void f();
+  ~A();
+};
+
+// CHECK: define void @_ZN5Test21AD2Ev
+// CHECK: store i8** getelementptr inbounds ([3 x i8*]* @_ZTVN5Test21AE, i64 0, i64 2), i8***
+A::~A() {
+  f();
+}
+
+}
+
+namespace Test3 {
+
+// Check that we don't initialize the vtable pointer in A::~A(), since the destructor body is trivial
+// and Field's destructor body is also trivial.
+struct Field {
+  ~Field() { }
+};
+
+struct A {
+  virtual void f();
+  ~A();
+
+  Field field;
+};
+
+// CHECK: define void @_ZN5Test31AD2Ev
+// CHECK-NOT: store i8** getelementptr inbounds ([3 x i8*]* @_ZTVN5Test31AE, i64 0, i64 2), i8***
+A::~A() {
+  
+}
+
+}
+
+namespace Test4 {
+
+// Check that we do initialize the vtable pointer in A::~A(), since Field's destructor body
+// isn't trivial.
+
+void f();
+
+struct Field {
+  ~Field() { f(); }
+};
+
+struct A {
+  virtual void f();
+  ~A();
+
+  Field field;
+};
+
+// CHECK: define void @_ZN5Test41AD2Ev
+// CHECK: store i8** getelementptr inbounds ([3 x i8*]* @_ZTVN5Test41AE, i64 0, i64 2), i8***
+A::~A()
+{
+}
+
+}
+
+namespace Test5 {
+
+// Check that we do initialize the vtable pointer in A::~A(), since Field's destructor isn't
+// available in this translation unit.
+
+struct Field {
+  ~Field();
+};
+
+struct A {
+  virtual void f();
+  ~A();
+
+  Field field;
+};
+
+// CHECK: define void @_ZN5Test51AD2Ev
+// CHECK: store i8** getelementptr inbounds ([3 x i8*]* @_ZTVN5Test51AE, i64 0, i64 2), i8***
+A::~A()
+{
+}
+
+}
\ No newline at end of file