From: John McCall Date: Fri, 6 Apr 2012 18:21:06 +0000 (+0000) Subject: Use atexit when __cxa_atexit isn't available instead of adding a X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=30fa3707c440222f65fcbb78ee8677462ea0d9ce;p=clang Use atexit when __cxa_atexit isn't available instead of adding a global destructor entry. For some reason this isn't enabled for apple-kexts; it'd be good to have documentation for that. Based on a patch by Nakamura Takumi! git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@154191 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/CodeGen/CGDeclCXX.cpp b/lib/CodeGen/CGDeclCXX.cpp index ddfe26581e..99d50312b5 100644 --- a/lib/CodeGen/CGDeclCXX.cpp +++ b/lib/CodeGen/CGDeclCXX.cpp @@ -145,37 +145,109 @@ void CodeGenFunction::EmitCXXGlobalVarDeclInit(const VarDecl &D, EmitStoreOfScalar(RV.getScalarVal(), DeclPtr, false, Alignment, T); } -void -CodeGenFunction::EmitCXXGlobalDtorRegistration(llvm::Constant *DtorFn, - llvm::Constant *DeclPtr) { - // Generate a global destructor entry if not using __cxa_atexit. - if (!CGM.getCodeGenOpts().CXAAtExit) { - CGM.AddCXXDtorEntry(DtorFn, DeclPtr); - return; - } +/// 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(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); +} - // Get the destructor function type - llvm::Type *DtorFnTy = llvm::FunctionType::get(VoidTy, Int8PtrTy, false); - DtorFnTy = llvm::PointerType::getUnqual(DtorFnTy); +static llvm::Function * +CreateGlobalInitOrDestructFunction(CodeGenModule &CGM, + llvm::FunctionType *ty, + const Twine &name); + +/// Create a stub function, suitable for being passed to atexit, +/// which passes the given address to the given destructor function. +static llvm::Constant *createAtExitStub(CodeGenModule &CGM, + llvm::Constant *dtor, + llvm::Constant *addr) { + // Get the destructor function type, void(*)(void). + llvm::FunctionType *ty = llvm::FunctionType::get(CGM.VoidTy, false); + llvm::Function *fn = + CreateGlobalInitOrDestructFunction(CGM, ty, + Twine("__dtor_", addr->getName())); + + CodeGenFunction CGF(CGM); + + CGF.StartFunction(GlobalDecl(), CGM.getContext().VoidTy, fn, + CGM.getTypes().arrangeNullaryFunction(), + FunctionArgList(), SourceLocation()); + + llvm::CallInst *call = CGF.Builder.CreateCall(dtor, addr); + + // Make sure the call and the callee agree on calling convention. + if (llvm::Function *dtorFn = + dyn_cast(dtor->stripPointerCasts())) + call->setCallingConv(dtorFn->getCallingConv()); + + CGF.FinishFunction(); - llvm::Type *Params[] = { DtorFnTy, Int8PtrTy, Int8PtrTy }; + return fn; +} - // Get the __cxa_atexit function type - // extern "C" int __cxa_atexit ( void (*f)(void *), void *p, void *d ); - llvm::FunctionType *AtExitFnTy = - llvm::FunctionType::get(ConvertType(getContext().IntTy), Params, false); +/// Register a global destructor using atexit. +static void emitGlobalDtorWithAtExit(CodeGenFunction &CGF, + llvm::Constant *dtor, + llvm::Constant *addr) { + // Create a function which calls the destructor. + llvm::Constant *dtorStub = createAtExitStub(CGF.CGM, dtor, addr); - llvm::Constant *AtExitFn = CGM.CreateRuntimeFunction(AtExitFnTy, - "__cxa_atexit"); - if (llvm::Function *Fn = dyn_cast(AtExitFn)) - Fn->setDoesNotThrow(); + // extern "C" int atexit(void (*f)(void)); + llvm::FunctionType *atexitTy = + llvm::FunctionType::get(CGF.IntTy, dtorStub->getType(), false); + + llvm::Constant *atexit = + CGF.CGM.CreateRuntimeFunction(atexitTy, "atexit"); + if (llvm::Function *atexitFn = dyn_cast(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); + } - llvm::Constant *Handle = CGM.CreateRuntimeVariable(Int8PtrTy, - "__dso_handle"); - llvm::Value *Args[3] = { llvm::ConstantExpr::getBitCast(DtorFn, DtorFnTy), - llvm::ConstantExpr::getBitCast(DeclPtr, Int8PtrTy), - llvm::ConstantExpr::getBitCast(Handle, Int8PtrTy) }; - Builder.CreateCall(AtExitFn, Args); + // Otherwise, we just use atexit. + emitGlobalDtorWithAtExit(*this, dtor, addr); } void CodeGenFunction::EmitCXXGuardedInit(const VarDecl &D, diff --git a/test/CodeGenCXX/arm.cpp b/test/CodeGenCXX/arm.cpp index 36c6c1c6f8..6c60f3057c 100644 --- a/test/CodeGenCXX/arm.cpp +++ b/test/CodeGenCXX/arm.cpp @@ -20,9 +20,17 @@ public: // The global dtor needs the right calling conv with -fno-use-cxa-atexit // rdar://7817590 -// Checked at end of file. bar baz; +// PR9593 +// Make sure atexit(3) is used for global dtors. + +// CHECK: call [[BAR:%.*]]* @_ZN3barC1Ev( +// CHECK-NEXT: call i32 @atexit(void ()* @__dtor_baz) + +// CHECK: define internal void @__dtor_baz() +// CHECK: call [[BAR]]* @_ZN3barD1Ev([[BAR]]* @baz) + // Destructors and constructors must return this. namespace test1 { void foo(); @@ -357,5 +365,5 @@ namespace test8 { // CHECK: call void @_ZN5test21CD0Ev( // CHECK: ret void -// CHECK: @_GLOBAL__D_a() -// CHECK: call %class.bar* @_ZN3barD1Ev(%class.bar* @baz) +// CH_ECK: @_GLOBAL__D_a() +// CH_ECK: call %class.bar* @_ZN3barD1Ev(%class.bar* @baz) diff --git a/test/CodeGenCXX/global-dtor-no-atexit.cpp b/test/CodeGenCXX/global-dtor-no-atexit.cpp index 1e125e3f7d..def97b2361 100644 --- a/test/CodeGenCXX/global-dtor-no-atexit.cpp +++ b/test/CodeGenCXX/global-dtor-no-atexit.cpp @@ -3,10 +3,15 @@ // PR7097 // RUN: %clang_cc1 -triple x86_64 %s -fno-use-cxa-atexit -mconstructor-aliases -emit-llvm -o - | FileCheck %s -// CHECK: define internal void @_GLOBAL__D_a() -// CHECK: call void @_ZN1AD1Ev(%class.A* @b) -// CHECK: call void @_ZN1AD1Ev(%class.A* @a) -// CHECK: } +// CHECK: call void @_ZN1AC1Ev([[A:%.*]]* @a) +// CHECK-NEXT: call i32 @atexit(void ()* @__dtor_a) +// CHECK: define internal void @__dtor_a() nounwind +// CHECK: call void @_ZN1AD1Ev([[A]]* @a) + +// CHECK: call void @_ZN1AC1Ev([[A]]* @b) +// CHECK-NEXT: call i32 @atexit(void ()* @__dtor_b) +// CHECK: define internal void @__dtor_b() nounwind +// CHECK: call void @_ZN1AD1Ev([[A]]* @b) class A { public: @@ -15,3 +20,25 @@ public: }; A a, b; + +// PR9593 +// CHECK: define void @_Z4funcv() +// CHECK: call i32 @__cxa_guard_acquire(i64* @_ZGVZ4funcvE2a1) +// CHECK: call void @_ZN1AC1Ev([[A]]* @_ZZ4funcvE2a1) +// CHECK-NEXT: call i32 @atexit(void ()* @__dtor__ZZ4funcvE2a1) +// CHECK-NEXT: call void @__cxa_guard_release(i64* @_ZGVZ4funcvE2a1) + +// CHECK: call i32 @__cxa_guard_acquire(i64* @_ZGVZ4funcvE2a2) +// CHECK: call void @_ZN1AC1Ev([[A]]* @_ZZ4funcvE2a2) +// CHECK-NEXT: call i32 @atexit(void ()* @__dtor__ZZ4funcvE2a2) +// CHECK-NEXT: call void @__cxa_guard_release(i64* @_ZGVZ4funcvE2a2) + +// CHECK: define internal void @__dtor__ZZ4funcvE2a1() nounwind +// CHECK: call void @_ZN1AD1Ev([[A]]* @_ZZ4funcvE2a1) + +// CHECK: define internal void @__dtor__ZZ4funcvE2a2() nounwind +// CHECK: call void @_ZN1AD1Ev([[A]]* @_ZZ4funcvE2a2) + +void func() { + static A a1, a2; +} diff --git a/test/CodeGenCXX/global-init.cpp b/test/CodeGenCXX/global-init.cpp index 053210bdfb..8e6ef775ca 100644 --- a/test/CodeGenCXX/global-init.cpp +++ b/test/CodeGenCXX/global-init.cpp @@ -12,7 +12,7 @@ struct C { void *field; }; struct D { ~D(); }; -// CHECK: @__dso_handle = external unnamed_addr global i8* +// CHECK: @__dso_handle = external unnamed_addr global i8 // CHECK: @c = global %struct.C zeroinitializer, align 8 // It's okay if we ever implement the IR-generation optimization to remove this. @@ -24,18 +24,18 @@ struct D { ~D(); }; // CHECK: @_ZN6PR59741bE = global %"struct.PR5974::B"* bitcast (i8* getelementptr (i8* bitcast (%"struct.PR5974::C"* @_ZN6PR59741cE to i8*), i64 4) to %"struct.PR5974::B"*), align 8 // CHECK: call void @_ZN1AC1Ev(%struct.A* @a) -// CHECK: call i32 @__cxa_atexit(void (i8*)* bitcast (void (%struct.A*)* @_ZN1AD1Ev to void (i8*)*), i8* getelementptr inbounds (%struct.A* @a, i32 0, i32 0), i8* bitcast (i8** @__dso_handle to i8*)) +// CHECK: call i32 @__cxa_atexit(void (i8*)* bitcast (void (%struct.A*)* @_ZN1AD1Ev to void (i8*)*), i8* getelementptr inbounds (%struct.A* @a, i32 0, i32 0), i8* @__dso_handle) A a; // CHECK: call void @_ZN1BC1Ev(%struct.B* @b) -// CHECK: call i32 @__cxa_atexit(void (i8*)* bitcast (void (%struct.B*)* @_ZN1BD1Ev to void (i8*)*), i8* getelementptr inbounds (%struct.B* @b, i32 0, i32 0), i8* bitcast (i8** @__dso_handle to i8*)) +// CHECK: call i32 @__cxa_atexit(void (i8*)* bitcast (void (%struct.B*)* @_ZN1BD1Ev to void (i8*)*), i8* getelementptr inbounds (%struct.B* @b, i32 0, i32 0), i8* @__dso_handle) B b; // PR6205: this should not require a global initializer // CHECK-NOT: call void @_ZN1CC1Ev(%struct.C* @c) C c; -// CHECK: call i32 @__cxa_atexit(void (i8*)* bitcast (void (%struct.D*)* @_ZN1DD1Ev to void (i8*)*), i8* getelementptr inbounds (%struct.D* @d, i32 0, i32 0), i8* bitcast (i8** @__dso_handle to i8*)) +// CHECK: call i32 @__cxa_atexit(void (i8*)* bitcast (void (%struct.D*)* @_ZN1DD1Ev to void (i8*)*), i8* getelementptr inbounds (%struct.D* @d, i32 0, i32 0), i8* @__dso_handle) D d; // diff --git a/test/CodeGenCXX/static-init.cpp b/test/CodeGenCXX/static-init.cpp index 7e840f5622..ed659de5e0 100644 --- a/test/CodeGenCXX/static-init.cpp +++ b/test/CodeGenCXX/static-init.cpp @@ -17,7 +17,7 @@ void f() { // CHECK: load atomic i8* bitcast (i64* @_ZGVZ1fvE1a to i8*) acquire, align 1 // CHECK: call i32 @__cxa_guard_acquire // CHECK: call void @_ZN1AC1Ev - // CHECK: call i32 @__cxa_atexit(void (i8*)* bitcast (void (%struct.A*)* @_ZN1AD1Ev to void (i8*)*), i8* getelementptr inbounds (%struct.A* @_ZZ1fvE1a, i32 0, i32 0), i8* bitcast (i8** @__dso_handle to i8*)) + // CHECK: call i32 @__cxa_atexit(void (i8*)* bitcast (void (%struct.A*)* @_ZN1AD1Ev to void (i8*)*), i8* getelementptr inbounds (%struct.A* @_ZZ1fvE1a, i32 0, i32 0), i8* @__dso_handle) // CHECK: call void @__cxa_guard_release static A a; }