]> granicus.if.org Git - clang/commitdiff
Abstract the emission of global destructors into ABI-specific code
authorJohn McCall <rjmccall@apple.com>
Tue, 1 May 2012 06:13:13 +0000 (06:13 +0000)
committerJohn McCall <rjmccall@apple.com>
Tue, 1 May 2012 06:13:13 +0000 (06:13 +0000)
and only consider using __cxa_atexit in the Itanium logic.  The
default logic is to use atexit().

Emit "guarded" initializers in Microsoft mode unconditionally.
This is definitely not correct, but it's closer to correct than
just not emitting the initializer.

Based on a patch by Timur Iskhodzhanov!

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

lib/CodeGen/CGCXXABI.cpp
lib/CodeGen/CGCXXABI.h
lib/CodeGen/CGDeclCXX.cpp
lib/CodeGen/CGExpr.cpp
lib/CodeGen/CodeGenFunction.h
lib/CodeGen/ItaniumCXXABI.cpp
lib/CodeGen/MicrosoftCXXABI.cpp
test/CodeGenCXX/microsoft-abi-static-initializers.cpp [new file with mode: 0644]

index 8561ae3ed50d158076c5a23e7a948c461b662069..32393bb8170d7a23e2d643e30094deba38981859 100644 (file)
@@ -221,6 +221,13 @@ void CGCXXABI::EmitGuardedInit(CodeGenFunction &CGF,
   ErrorUnsupportedABI(CGF, "static local variable initialization");
 }
 
+void CGCXXABI::registerGlobalDtor(CodeGenFunction &CGF,
+                                  llvm::Constant *dtor,
+                                  llvm::Constant *addr) {
+  // The default behavior is to use atexit.
+  CGF.registerGlobalDtorWithAtExit(dtor, addr);
+}
+
 /// Returns the adjustment, in bytes, required for the given
 /// member-pointer operation.  Returns null if no adjustment is
 /// required.
index 1621016baa46880a6ba84ed2ccf98cc9539772d8..2f87b062649395c649705acf27b065601f252933 100644 (file)
@@ -272,6 +272,13 @@ public:
   virtual void EmitGuardedInit(CodeGenFunction &CGF, const VarDecl &D,
                                llvm::GlobalVariable *DeclPtr, bool PerformInit);
 
+  /// Emit code to force the execution of a destructor during global
+  /// teardown.  The default implementation of this uses atexit.
+  ///
+  /// \param dtor - a function taking a single pointer argument
+  /// \param addr - a pointer to pass to the destructor function.
+  virtual void registerGlobalDtor(CodeGenFunction &CGF, llvm::Constant *dtor,
+                                  llvm::Constant *addr);
 };
 
 /// Creates an instance of a C++ ABI class.
index 10f0b83e40535342150d300d771dfc72180c7b92..a27516bb5b752c246a14db4f62d9f27d84e0832e 100644 (file)
@@ -98,7 +98,7 @@ static void EmitDeclDestroy(CodeGenFunction &CGF, const VarDecl &D,
     argument = llvm::Constant::getNullValue(CGF.Int8PtrTy);
   }
 
-  CGF.EmitCXXGlobalDtorRegistration(function, argument);
+  CGM.getCXXABI().registerGlobalDtor(CGF, function, argument);
 }
 
 /// Emit code to cause the variable at the given address to be considered as
@@ -145,39 +145,6 @@ void CodeGenFunction::EmitCXXGlobalVarDeclInit(const VarDecl &D,
   EmitStoreOfScalar(RV.getScalarVal(), DeclPtr, false, Alignment, T);
 }
 
-/// Register a global destructor using __cxa_atexit.
-static void emitGlobalDtorWithCXAAtExit(CodeGenFunction &CGF,
-                                        llvm::Constant *dtor,
-                                        llvm::Constant *addr) {
-  // We're assuming that the destructor function is something we can
-  // reasonably call with the default CC.  Go ahead and cast it to the
-  // right prototype.
-  llvm::Type *dtorTy =
-    llvm::FunctionType::get(CGF.VoidTy, CGF.Int8PtrTy, false)->getPointerTo();
-
-  // extern "C" int __cxa_atexit(void (*f)(void *), void *p, void *d);
-  llvm::Type *paramTys[] = { dtorTy, CGF.Int8PtrTy, CGF.Int8PtrTy };
-  llvm::FunctionType *atexitTy =
-    llvm::FunctionType::get(CGF.IntTy, paramTys, false);
-
-  // Fetch the actual function.
-  llvm::Constant *atexit =
-    CGF.CGM.CreateRuntimeFunction(atexitTy, "__cxa_atexit");
-  if (llvm::Function *fn = dyn_cast<llvm::Function>(atexit))
-    fn->setDoesNotThrow();
-
-  // Create a variable that binds the atexit to this shared object.
-  llvm::Constant *handle =
-    CGF.CGM.CreateRuntimeVariable(CGF.Int8Ty, "__dso_handle");
-
-  llvm::Value *args[] = {
-    llvm::ConstantExpr::getBitCast(dtor, dtorTy),
-    llvm::ConstantExpr::getBitCast(addr, CGF.Int8PtrTy),
-    handle
-  };
-  CGF.Builder.CreateCall(atexit, args);
-}
-
 static llvm::Function *
 CreateGlobalInitOrDestructFunction(CodeGenModule &CGM,
                                    llvm::FunctionType *ty,
@@ -212,43 +179,22 @@ static llvm::Constant *createAtExitStub(CodeGenModule &CGM,
   return fn;
 }
 
-/// Register a global destructor using atexit.
-static void emitGlobalDtorWithAtExit(CodeGenFunction &CGF,
-                                     llvm::Constant *dtor,
-                                     llvm::Constant *addr) {
+/// Register a global destructor using the C atexit runtime function.
+void CodeGenFunction::registerGlobalDtorWithAtExit(llvm::Constant *dtor,
+                                                   llvm::Constant *addr) {
   // Create a function which calls the destructor.
-  llvm::Constant *dtorStub = createAtExitStub(CGF.CGM, dtor, addr);
+  llvm::Constant *dtorStub = createAtExitStub(CGM, dtor, addr);
 
   // extern "C" int atexit(void (*f)(void));
   llvm::FunctionType *atexitTy =
-    llvm::FunctionType::get(CGF.IntTy, dtorStub->getType(), false);
+    llvm::FunctionType::get(IntTy, dtorStub->getType(), false);
 
   llvm::Constant *atexit =
-    CGF.CGM.CreateRuntimeFunction(atexitTy, "atexit");
+    CGM.CreateRuntimeFunction(atexitTy, "atexit");
   if (llvm::Function *atexitFn = dyn_cast<llvm::Function>(atexit))
     atexitFn->setDoesNotThrow();
 
-  CGF.Builder.CreateCall(atexit, dtorStub);
-}
-
-void CodeGenFunction::EmitCXXGlobalDtorRegistration(llvm::Constant *dtor,
-                                                    llvm::Constant *addr) {
-  // Use __cxa_atexit if available.
-  if (CGM.getCodeGenOpts().CXAAtExit) {
-    emitGlobalDtorWithCXAAtExit(*this, dtor, addr);
-    return;
-  }
-
-  // In Apple kexts, we want to add a global destructor entry.
-  // FIXME: shouldn't this be guarded by some variable?
-  if (CGM.getContext().getLangOpts().AppleKext) {
-    // Generate a global destructor entry.
-    CGM.AddCXXDtorEntry(dtor, addr);
-    return;
-  }
-
-  // Otherwise, we just use atexit.
-  emitGlobalDtorWithAtExit(*this, dtor, addr);
+  Builder.CreateCall(atexit, dtorStub)->setDoesNotThrow();
 }
 
 void CodeGenFunction::EmitCXXGuardedInit(const VarDecl &D,
index 5f2b1f055dba9b7bd553d7d18c89b31c3b3c44e2..a60b43662013fe7a5b4a4a24935b1fc5cc6b15aa 100644 (file)
@@ -462,7 +462,7 @@ CodeGenFunction::EmitReferenceBindingToExpr(const Expr *E,
     if (ReferenceTemporaryDtor) {
       llvm::Constant *DtorFn = 
         CGM.GetAddrOfCXXDestructor(ReferenceTemporaryDtor, Dtor_Complete);
-      EmitCXXGlobalDtorRegistration(DtorFn, 
+      CGM.getCXXABI().registerGlobalDtor(*this, DtorFn, 
                                     cast<llvm::Constant>(ReferenceTemporary));
     } else {
       assert(!ObjCARCReferenceLifetimeType.isNull());
index 001a37100201d41fee22ea8a91ee76f4f60acad5..a25b02aac875db663ffec6f7b5b1364347b0f953 100644 (file)
@@ -2411,10 +2411,9 @@ public:
   void EmitCXXGlobalVarDeclInit(const VarDecl &D, llvm::Constant *DeclPtr,
                                 bool PerformInit);
 
-  /// EmitCXXGlobalDtorRegistration - Emits a call to register the global ptr
-  /// with the C++ runtime so that its destructor will be called at exit.
-  void EmitCXXGlobalDtorRegistration(llvm::Constant *DtorFn,
-                                     llvm::Constant *DeclPtr);
+  /// Call atexit() with a function that passes the given argument to
+  /// the given function.
+  void registerGlobalDtorWithAtExit(llvm::Constant *fn, llvm::Constant *addr);
 
   /// Emit code in this function to perform a guarded variable
   /// initialization.  Guarded initializations are used when it's not
index 1a36a085709c3ae667054010e2c4955b89e871a2..3188590d11eff8f920c4e45f21e96a6f97260dfd 100644 (file)
@@ -119,6 +119,8 @@ public:
 
   void EmitGuardedInit(CodeGenFunction &CGF, const VarDecl &D,
                        llvm::GlobalVariable *DeclPtr, bool PerformInit);
+  void registerGlobalDtor(CodeGenFunction &CGF, llvm::Constant *dtor,
+                          llvm::Constant *addr);
 };
 
 class ARMCXXABI : public ItaniumCXXABI {
@@ -1097,3 +1099,55 @@ void ItaniumCXXABI::EmitGuardedInit(CodeGenFunction &CGF,
 
   CGF.EmitBlock(EndBlock);
 }
+
+/// Register a global destructor using __cxa_atexit.
+static void emitGlobalDtorWithCXAAtExit(CodeGenFunction &CGF,
+                                        llvm::Constant *dtor,
+                                        llvm::Constant *addr) {
+  // We're assuming that the destructor function is something we can
+  // reasonably call with the default CC.  Go ahead and cast it to the
+  // right prototype.
+  llvm::Type *dtorTy =
+    llvm::FunctionType::get(CGF.VoidTy, CGF.Int8PtrTy, false)->getPointerTo();
+
+  // extern "C" int __cxa_atexit(void (*f)(void *), void *p, void *d);
+  llvm::Type *paramTys[] = { dtorTy, CGF.Int8PtrTy, CGF.Int8PtrTy };
+  llvm::FunctionType *atexitTy =
+    llvm::FunctionType::get(CGF.IntTy, paramTys, false);
+
+  // Fetch the actual function.
+  llvm::Constant *atexit =
+    CGF.CGM.CreateRuntimeFunction(atexitTy, "__cxa_atexit");
+  if (llvm::Function *fn = dyn_cast<llvm::Function>(atexit))
+    fn->setDoesNotThrow();
+
+  // Create a variable that binds the atexit to this shared object.
+  llvm::Constant *handle =
+    CGF.CGM.CreateRuntimeVariable(CGF.Int8Ty, "__dso_handle");
+
+  llvm::Value *args[] = {
+    llvm::ConstantExpr::getBitCast(dtor, dtorTy),
+    llvm::ConstantExpr::getBitCast(addr, CGF.Int8PtrTy),
+    handle
+  };
+  CGF.Builder.CreateCall(atexit, args)->setDoesNotThrow();
+}
+
+/// Register a global destructor as best as we know how.
+void ItaniumCXXABI::registerGlobalDtor(CodeGenFunction &CGF,
+                                       llvm::Constant *dtor,
+                                       llvm::Constant *addr) {
+  // Use __cxa_atexit if available.
+  if (CGM.getCodeGenOpts().CXAAtExit) {
+    return emitGlobalDtorWithCXAAtExit(CGF, dtor, addr);
+  }
+
+  // In Apple kexts, we want to add a global destructor entry.
+  // FIXME: shouldn't this be guarded by some variable?
+  if (CGM.getContext().getLangOpts().AppleKext) {
+    // Generate a global destructor entry.
+    return CGM.AddCXXDtorEntry(dtor, addr);
+  }
+
+  CGF.registerGlobalDtorWithAtExit(dtor, addr);
+}
index a88c6ba5f101cb18be7c40b7388efe918872230d..b0e6317d671e695c06e7b4c1e3203e825749486b 100644 (file)
@@ -56,6 +56,11 @@ public:
     // TODO: 'for base' flag
   }
 
+  void EmitGuardedInit(CodeGenFunction &CGF, const VarDecl &D,
+                       llvm::GlobalVariable *DeclPtr,
+                       bool PerformInit);
+
+
   // ==== Notes on array cookies =========
   //
   // MSVC seems to only use cookies when the class has a destructor; a
@@ -149,6 +154,17 @@ llvm::Value* MicrosoftCXXABI::InitializeArrayCookie(CodeGenFunction &CGF,
                                                 cookieSize.getQuantity());
 }
 
+void MicrosoftCXXABI::EmitGuardedInit(CodeGenFunction &CGF, const VarDecl &D,
+                                      llvm::GlobalVariable *DeclPtr,
+                                      bool PerformInit) {
+  // FIXME: this code was only tested for global initialization.
+  // Not sure whether we want thread-safe static local variables as VS
+  // doesn't make them thread-safe.
+
+  // Emit the initializer and add a global destructor if appropriate.
+  CGF.EmitCXXGlobalVarDeclInit(D, DeclPtr, PerformInit);
+}
+
 CGCXXABI *clang::CodeGen::CreateMicrosoftCXXABI(CodeGenModule &CGM) {
   return new MicrosoftCXXABI(CGM);
 }
diff --git a/test/CodeGenCXX/microsoft-abi-static-initializers.cpp b/test/CodeGenCXX/microsoft-abi-static-initializers.cpp
new file mode 100644 (file)
index 0000000..4ccd6ba
--- /dev/null
@@ -0,0 +1,52 @@
+// RUN: %clang_cc1 -emit-llvm %s -o - -cxx-abi microsoft -triple=i386-pc-win32 | FileCheck %s
+
+struct S {
+  S() {}
+  ~S() {}
+} s;
+
+// CHECK: define internal void [[INIT_s:@.*global_var.*]] nounwind
+// CHECK: call void @"\01??0S@@QAE@XZ"
+// CHECK: call i32 @atexit(void ()* @"__dtor_\01?s@@3US@@A")
+// CHECK: ret void
+
+// CHECK: define internal void @"__dtor_\01?s@@3US@@A"() nounwind {
+// CHECK: call void @"\01??1S@@QAE@XZ"
+// CHECK: ret void
+
+// Force WeakODRLinkage by using templates
+class A {
+ public:
+  A() {}
+  ~A() {}
+};
+
+template<typename T>
+class B {
+ public:
+  static A foo;
+};
+
+template<typename T> A B<T>::foo;
+
+void force_usage() {
+  (void)B<int>::foo;  // (void) - force usage
+}
+
+// CHECK: define internal void [[INIT_foo:@.*global_var.*]] nounwind
+// CHECK: call void @"\01??0A@@QAE@XZ"
+// CHECK: call i32 @atexit(void ()* [[FOO_DTOR:@"__dtor_.*foo@.*]])
+// CHECK: ret void
+
+// CHECK: define linkonce_odr void @"\01??0A@@QAE@XZ"
+
+// CHECK: define linkonce_odr void @"\01??1A@@QAE@XZ"
+
+// CHECK: define internal void [[FOO_DTOR]]
+// CHECK: call void @"\01??1A@@QAE@XZ"{{.*}}foo
+// CHECK: ret void
+
+// CHECK: define internal void @_GLOBAL__I_a() nounwind {
+// CHECK: call void [[INIT_s]]
+// CHECK: call void [[INIT_foo]]
+// CHECK: ret void