diff --git a/clang/lib/CodeGen/CGDeclCXX.cpp b/clang/lib/CodeGen/CGDeclCXX.cpp --- a/clang/lib/CodeGen/CGDeclCXX.cpp +++ b/clang/lib/CodeGen/CGDeclCXX.cpp @@ -273,8 +273,10 @@ void CodeGenFunction::registerGlobalDtorWithAtExit(llvm::Constant *dtorStub) { // extern "C" int atexit(void (*f)(void)); - assert(cast(dtorStub)->getFunctionType() == - llvm::FunctionType::get(CGM.VoidTy, false) && + llvm::PointerType *Expected = + llvm::PointerType::get(llvm::FunctionType::get(CGM.VoidTy, false), + dtorStub->getType()->getPointerAddressSpace()); + assert(dtorStub->getType() == Expected && "Argument to atexit has a wrong type."); llvm::FunctionType *atexitTy = @@ -290,7 +292,7 @@ } llvm::Value * -CodeGenFunction::unregisterGlobalDtorWithUnAtExit(llvm::Function *dtorStub) { +CodeGenFunction::unregisterGlobalDtorWithUnAtExit(llvm::Constant *dtorStub) { // The unatexit subroutine unregisters __dtor functions that were previously // registered by the atexit subroutine. If the referenced function is found, // it is removed from the list of functions that are called at normal program @@ -298,8 +300,10 @@ // value is returned. // // extern "C" int unatexit(void (*f)(void)); - assert(dtorStub->getFunctionType() == - llvm::FunctionType::get(CGM.VoidTy, false) && + llvm::PointerType *Expected = + llvm::PointerType::get(llvm::FunctionType::get(CGM.VoidTy, false), + dtorStub->getType()->getPointerAddressSpace()); + assert(dtorStub->getType() == Expected && "Argument to unatexit has a wrong type."); llvm::FunctionType *unatexitTy = diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -4300,7 +4300,7 @@ void registerGlobalDtorWithAtExit(llvm::Constant *dtorStub); /// Call unatexit() with function dtorStub. - llvm::Value *unregisterGlobalDtorWithUnAtExit(llvm::Function *dtorStub); + llvm::Value *unregisterGlobalDtorWithUnAtExit(llvm::Constant *dtorStub); /// Emit code in this function to perform a guarded variable /// initialization. Guarded initializations are used when it's not diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -1478,7 +1478,8 @@ // FIXME: Hardcoding priority here is gross. void AddGlobalCtor(llvm::Function *Ctor, int Priority = 65535, llvm::Constant *AssociatedData = nullptr); - void AddGlobalDtor(llvm::Function *Dtor, int Priority = 65535); + void AddGlobalDtor(llvm::Function *Dtor, int Priority = 65535, + bool IsDtorAttrFunc = false); /// EmitCtorList - Generates a global array of functions and priorities using /// the given list and name. This array will have appending linkage and is @@ -1508,6 +1509,11 @@ /// __cxa_atexit, if it is available, or atexit otherwise. void registerGlobalDtorsWithAtExit(); + // When using sinit and sterm functions, unregister + // __attribute__((destructor)) annotated functions which were previously + // registered by the atexit subroutine using unatexit. + void unregisterGlobalDtorsWithUnAtExit(); + void emitMultiVersionFunctions(); /// Emit any vtables which we deferred and still have a use for. diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -1278,11 +1278,10 @@ /// AddGlobalDtor - Add a function to the list that will be called /// when the module is unloaded. -void CodeGenModule::AddGlobalDtor(llvm::Function *Dtor, int Priority) { - if (CodeGenOpts.RegisterGlobalDtorsWithAtExit) { - if (getCXXABI().useSinitAndSterm()) - llvm::report_fatal_error( - "register global dtors with atexit() is not supported yet"); +void CodeGenModule::AddGlobalDtor(llvm::Function *Dtor, int Priority, + bool IsDtorAttrFunc) { + if (CodeGenOpts.RegisterGlobalDtorsWithAtExit && + (!getContext().getTargetInfo().getTriple().isOSAIX() || IsDtorAttrFunc)) { DtorsUsingAtExit[Priority].push_back(Dtor); return; } @@ -4696,7 +4695,7 @@ if (const ConstructorAttr *CA = D->getAttr()) AddGlobalCtor(Fn, CA->getPriority()); if (const DestructorAttr *DA = D->getAttr()) - AddGlobalDtor(Fn, DA->getPriority()); + AddGlobalDtor(Fn, DA->getPriority(), true); if (D->hasAttr()) AddGlobalAnnotations(D, Fn); } diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -2530,48 +2530,135 @@ CGF.EmitNounwindRuntimeCall(atexit, args); } -void CodeGenModule::registerGlobalDtorsWithAtExit() { +static llvm::Function *createGlobalInitOrCleanupFn(CodeGen::CodeGenModule &CGM, + StringRef FnName, + int Priority, bool IsInit) { + // Create a function that registers/unregisters destructors that have the same + // priority. + CodeGenFunction CGF(CGM); + + llvm::FunctionType *FTy = llvm::FunctionType::get(CGF.VoidTy, false); + llvm::Function *GlobalInitOrCleanupFn = CGM.CreateGlobalInitOrCleanUpFunction( + FTy, FnName, CGM.getTypes().arrangeNullaryFunction(), SourceLocation()); + + return GlobalInitOrCleanupFn; +} + +static FunctionDecl * +createGlobalInitOrCleanupFnDecl(CodeGen::CodeGenModule &CGM, StringRef FnName) { + ASTContext &Ctx = CGM.getContext(); + QualType FunctionTy = Ctx.getFunctionType(Ctx.VoidTy, llvm::None, {}); + return FunctionDecl::Create( + Ctx, Ctx.getTranslationUnitDecl(), SourceLocation(), SourceLocation(), + &Ctx.Idents.get(FnName), FunctionTy, nullptr, SC_Static, false, false); +} + +void CodeGenModule::unregisterGlobalDtorsWithUnAtExit() { for (const auto &I : DtorsUsingAtExit) { int Priority = I.first; + std::string GlobalCleanupFnName = + std::string("__GLOBAL_cleanup_") + llvm::to_string(Priority); + + llvm::Function *GlobalCleanupFn = createGlobalInitOrCleanupFn( + *this, GlobalCleanupFnName, Priority, false /* IsInit */); + + FunctionDecl *GlobalCleanupFD = + createGlobalInitOrCleanupFnDecl(*this, GlobalCleanupFnName); + + CodeGenFunction CGF(*this); + CGF.StartFunction(GlobalDecl(GlobalCleanupFD), getContext().VoidTy, + GlobalCleanupFn, getTypes().arrangeNullaryFunction(), + FunctionArgList(), SourceLocation(), SourceLocation()); + + // Get the destructor function type, void(*)(void). + llvm::FunctionType *dtorFuncTy = llvm::FunctionType::get(CGF.VoidTy, false); + llvm::Type *dtorTy = dtorFuncTy->getPointerTo(); + + // Destructor functions are run/unregistered in non-ascending + // order of their priorities. const llvm::TinyPtrVector &Dtors = I.second; + auto itv = Dtors.rbegin(); + while (itv != Dtors.rend()) { + llvm::Function *Dtor = *itv; + + // 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::Constant *dtor = llvm::ConstantExpr::getBitCast(Dtor, dtorTy); + llvm::Value *V = CGF.unregisterGlobalDtorWithUnAtExit(dtor); + llvm::Value *NeedsDestruct = + CGF.Builder.CreateIsNull(V, "needs_destruct"); + + llvm::BasicBlock *DestructCallBlock = + CGF.createBasicBlock("destruct.call"); + llvm::BasicBlock *EndBlock = CGF.createBasicBlock( + (itv + 1) != Dtors.rend() ? "unatexit.call" : "destruct.end"); + // Check if unatexit returns a value of 0. If it does, jump to + // DestructCallBlock, otherwise jump to EndBlock directly. + CGF.Builder.CreateCondBr(NeedsDestruct, DestructCallBlock, EndBlock); + + CGF.EmitBlock(DestructCallBlock); + + // Emit the call to casted Dtor. + llvm::CallInst *CI = CGF.Builder.CreateCall(dtorFuncTy, dtor); + // Make sure the call and the callee agree on calling convention. + CI->setCallingConv(Dtor->getCallingConv()); + + CGF.EmitBlock(EndBlock); + + itv++; + } + + CGF.FinishFunction(); + AddGlobalDtor(GlobalCleanupFn, Priority); + } +} + +void CodeGenModule::registerGlobalDtorsWithAtExit() { + for (const auto &I : DtorsUsingAtExit) { + int Priority = I.first; + std::string GlobalInitFnName = + std::string("__GLOBAL_init_") + llvm::to_string(Priority); + llvm::Function *GlobalInitFn = createGlobalInitOrCleanupFn( + *this, GlobalInitFnName, Priority, true /* IsInit */); + FunctionDecl *GlobalInitFD = + createGlobalInitOrCleanupFnDecl(*this, GlobalInitFnName); + + CodeGenFunction CGF(*this); + CGF.StartFunction(GlobalDecl(GlobalInitFD), getContext().VoidTy, + GlobalInitFn, getTypes().arrangeNullaryFunction(), + FunctionArgList(), SourceLocation(), SourceLocation()); - // Create a function that registers destructors that have the same priority. - // // Since constructor functions are run in non-descending order of their // priorities, destructors are registered in non-descending order of their // priorities, and since destructor functions are run in the reverse order // of their registration, destructor functions are run in non-ascending // order of their priorities. - CodeGenFunction CGF(*this); - std::string GlobalInitFnName = - std::string("__GLOBAL_init_") + llvm::to_string(Priority); - llvm::FunctionType *FTy = llvm::FunctionType::get(VoidTy, false); - llvm::Function *GlobalInitFn = CreateGlobalInitOrCleanUpFunction( - FTy, GlobalInitFnName, getTypes().arrangeNullaryFunction(), - SourceLocation()); - ASTContext &Ctx = getContext(); - QualType ReturnTy = Ctx.VoidTy; - QualType FunctionTy = Ctx.getFunctionType(ReturnTy, llvm::None, {}); - FunctionDecl *FD = FunctionDecl::Create( - Ctx, Ctx.getTranslationUnitDecl(), SourceLocation(), SourceLocation(), - &Ctx.Idents.get(GlobalInitFnName), FunctionTy, nullptr, SC_Static, - false, false); - CGF.StartFunction(GlobalDecl(FD), ReturnTy, GlobalInitFn, - getTypes().arrangeNullaryFunction(), FunctionArgList(), - SourceLocation(), SourceLocation()); - + const llvm::TinyPtrVector &Dtors = I.second; for (auto *Dtor : Dtors) { // Register the destructor function calling __cxa_atexit if it is // available. Otherwise fall back on calling atexit. - if (getCodeGenOpts().CXAAtExit) + if (getCodeGenOpts().CXAAtExit) { emitGlobalDtorWithCXAAtExit(CGF, Dtor, nullptr, false); - else - CGF.registerGlobalDtorWithAtExit(Dtor); + } else { + // Get the destructor function type, void(*)(void). + llvm::Type *dtorTy = + llvm::FunctionType::get(CGF.VoidTy, false)->getPointerTo(); + + // 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. + CGF.registerGlobalDtorWithAtExit( + llvm::ConstantExpr::getBitCast(Dtor, dtorTy)); + } } CGF.FinishFunction(); AddGlobalCtor(GlobalInitFn, Priority, nullptr); } + + if (getCXXABI().useSinitAndSterm()) + unregisterGlobalDtorsWithUnAtExit(); } /// Register a global destructor as best as we know how. diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -7485,19 +7485,12 @@ handlePassObjectSizeAttr(S, D, AL); break; case ParsedAttr::AT_Constructor: - if (S.Context.getTargetInfo().getTriple().isOSAIX()) - llvm::report_fatal_error( - "'constructor' attribute is not yet supported on AIX"); - else handleConstructorAttr(S, D, AL); break; case ParsedAttr::AT_Deprecated: handleDeprecatedAttr(S, D, AL); break; case ParsedAttr::AT_Destructor: - if (S.Context.getTargetInfo().getTriple().isOSAIX()) - llvm::report_fatal_error("'destructor' attribute is not yet supported on AIX"); - else handleDestructorAttr(S, D, AL); break; case ParsedAttr::AT_EnableIf: diff --git a/clang/test/CodeGen/aix-constructor-attribute.cpp b/clang/test/CodeGen/aix-constructor-attribute.cpp --- a/clang/test/CodeGen/aix-constructor-attribute.cpp +++ b/clang/test/CodeGen/aix-constructor-attribute.cpp @@ -1,20 +1,31 @@ -// RUN: not %clang_cc1 -triple powerpc-ibm-aix-xcoff -x c++ -emit-llvm < %s \ -// RUN: 2>&1 | \ +// RUN: %clang_cc1 -triple powerpc-ibm-aix-xcoff -x c++ -emit-llvm \ +// RUN: -fno-use-cxa-atexit < %s |\ // RUN: FileCheck %s -// RUN: not %clang_cc1 -triple powerpc64-ibm-aix-xcoff -x c++ -emit-llvm < %s \ -// RUN: 2>&1 | \ +// RUN: %clang_cc1 -triple powerpc64-ibm-aix-xcoff -x c++ -emit-llvm \ +// RUN: -fno-use-cxa-atexit < %s | \ // RUN: FileCheck %s -int foo() __attribute__((constructor(180))); +// CHECK: @llvm.global_ctors = appending global [4 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* bitcast (i32 ()* @_Z4foo3v to void ()*), i8* null }, { i32, void ()*, i8* } { i32 180, void ()* bitcast (i32 ()* @_Z4foo2v to void ()*), i8* null }, { i32, void ()*, i8* } { i32 180, void ()* bitcast (i32 ()* @_Z3foov to void ()*), i8* null }, { i32, void ()*, i8* } { i32 65535, void ()* @_GLOBAL__sub_I__, i8* null }] +// CHECK: @llvm.global_dtors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* @_GLOBAL__D_a, i8* null }] -class test { - int a; +int foo() __attribute__((constructor(180))); +int foo2() __attribute__((constructor(180))); +int foo3() __attribute__((constructor(65535))); +struct Test { public: - test(int c) { a = c; } - ~test() { a = 0; } -}; + Test() {} + ~Test() {} +} t; + +int foo3() { + return 3; +} -test t(1); +int foo2() { + return 2; +} -// CHECK: fatal error: error in backend: 'constructor' attribute is not yet supported on AIX +int foo() { + return 1; +} diff --git a/clang/test/CodeGen/aix-destructor-attribute.cpp b/clang/test/CodeGen/aix-destructor-attribute.cpp deleted file mode 100644 --- a/clang/test/CodeGen/aix-destructor-attribute.cpp +++ /dev/null @@ -1,20 +0,0 @@ -// RUN: not %clang_cc1 -triple powerpc-ibm-aix-xcoff -x c++ -emit-llvm < %s \ -// RUN: 2>&1 | \ -// RUN: FileCheck %s -// RUN: not %clang_cc1 -triple powerpc64-ibm-aix-xcoff -x c++ -emit-llvm < %s \ -// RUN: 2>&1 | \ -// RUN: FileCheck %s - -int bar() __attribute__((destructor(180))); - -class test { - int a; - -public: - test(int c) { a = c; } - ~test() { a = 0; } -}; - -test t(1); - -// CHECK: fatal error: error in backend: 'destructor' attribute is not yet supported on AIX diff --git a/clang/test/CodeGenCXX/aix-destructor-attribute.cpp b/clang/test/CodeGenCXX/aix-destructor-attribute.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/aix-destructor-attribute.cpp @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -triple powerpc-ibm-aix-xcoff -x c++ -emit-llvm \ +// RUN: -fno-use-cxa-atexit < %s | \ +// RUN: FileCheck %s +// RUN: %clang_cc1 -triple powerpc64-ibm-aix-xcoff -x c++ -emit-llvm \ +// RUN: -fno-use-cxa-atexit < %s | \ +// RUN: FileCheck %s + +// CHECK: @llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* @_GLOBAL__sub_I__, i8* null }] +// CHECK: @llvm.global_dtors = appending global [4 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 100, void ()* bitcast (i32 ()* @_Z3barv to void ()*), i8* null }, { i32, void ()*, i8* } { i32 65535, void ()* bitcast (i32 ()* @_Z4bar2v to void ()*), i8* null }, { i32, void ()*, i8* } { i32 65535, void ()* bitcast (i32 (i32)* @_Z4bar3i to void ()*), i8* null }, { i32, void ()*, i8* } { i32 65535, void ()* @_GLOBAL__D_a, i8* null }] + +struct test { + test(); + ~test(); +} t; + +int bar() __attribute__((destructor(100))); +int bar2() __attribute__((destructor(65535))); +int bar3(int) __attribute__((destructor(65535))); + +int bar() { + return 1; +} + +int bar2() { + return 2; +} + +int bar3(int a) { + return a; +} diff --git a/clang/test/CodeGenCXX/aix-sinit-register-global-dtors-with-atexit.cpp b/clang/test/CodeGenCXX/aix-sinit-register-global-dtors-with-atexit.cpp --- a/clang/test/CodeGenCXX/aix-sinit-register-global-dtors-with-atexit.cpp +++ b/clang/test/CodeGenCXX/aix-sinit-register-global-dtors-with-atexit.cpp @@ -1,14 +1,80 @@ -// RUN: not %clang_cc1 -triple powerpc-ibm-aix-xcoff -S -emit-llvm -x c++ \ -// RUN: -fregister-global-dtors-with-atexit < %s 2>&1 | \ +// RUN: %clang_cc1 -triple powerpc-ibm-aix-xcoff -x c++ -emit-llvm \ +// RUN: -fno-use-cxa-atexit -fregister-global-dtors-with-atexit < %s | \ // RUN: FileCheck %s - -// RUN: not %clang_cc1 -triple powerpc64-ibm-aix-xcoff -S -emit-llvm -x c++ \ -// RUN: -fregister-global-dtors-with-atexit < %s 2>&1 | \ +// RUN: %clang_cc1 -triple powerpc64-ibm-aix-xcoff -x c++ -emit-llvm \ +// RUN: -fno-use-cxa-atexit -fregister-global-dtors-with-atexit < %s | \ // RUN: FileCheck %s -struct T { - T(); - ~T(); +int bar() __attribute__((destructor(100))); +int bar2() __attribute__((destructor(65535))); +int bar3(int a) __attribute__((destructor(65535))); + +int bar() { + return 1; +} + +int bar2() { + return 2; +} + +int bar3(int a) { + return 3; +} + +struct test { + test(); + ~test(); } t; -// CHECK: error in backend: register global dtors with atexit() is not supported yet +// CHECK: @llvm.global_ctors = appending global [3 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* @_GLOBAL__sub_I__, i8* null }, { i32, void ()*, i8* } { i32 100, void ()* @__GLOBAL_init_100, i8* null }, { i32, void ()*, i8* } { i32 65535, void ()* @__GLOBAL_init_65535, i8* null }] +// CHECK: @llvm.global_dtors = appending global [3 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* @_GLOBAL__D_a, i8* null }, { i32, void ()*, i8* } { i32 100, void ()* @__GLOBAL_cleanup_100, i8* null }, { i32, void ()*, i8* } { i32 65535, void ()* @__GLOBAL_cleanup_65535, i8* null }] + +// CHECK: define internal void @__GLOBAL_init_100() [[ATTR:#[0-9]+]] { +// CHECK: entry: +// CHECK: %0 = call i32 @atexit(void ()* bitcast (i32 ()* @_Z3barv to void ()*)) +// CHECK: ret void +// CHECK: } + +// CHECK: define internal void @__GLOBAL_init_65535() [[ATTR:#[0-9]+]] { +// CHECK: entry: +// CHECK: %0 = call i32 @atexit(void ()* bitcast (i32 ()* @_Z4bar2v to void ()*)) +// CHECK: %1 = call i32 @atexit(void ()* bitcast (i32 (i32)* @_Z4bar3i to void ()*)) +// CHECK: ret void +// CHECK: } + +// CHECK: define internal void @__GLOBAL_cleanup_100() [[ATTR:#[0-9]+]] { +// CHECK: entry: +// CHECK: %0 = call i32 @unatexit(void ()* bitcast (i32 ()* @_Z3barv to void ()*)) +// CHECK: %needs_destruct = icmp eq i32 %0, 0 +// CHECK: br i1 %needs_destruct, label %destruct.call, label %destruct.end + +// CHECK: destruct.call: +// CHECK: call void bitcast (i32 ()* @_Z3barv to void ()*)() +// CHECK: br label %destruct.end + +// CHECK: destruct.end: +// CHECK: ret void +// CHECK: } + +// CHECK: define internal void @__GLOBAL_cleanup_65535() [[ATTR:#[0-9]+]] { +// CHECK: entry: +// CHECK: %0 = call i32 @unatexit(void ()* bitcast (i32 (i32)* @_Z4bar3i to void ()*)) +// CHECK: %needs_destruct = icmp eq i32 %0, 0 +// CHECK: br i1 %needs_destruct, label %destruct.call, label %unatexit.call + +// CHECK: destruct.call: +// CHECK: call void bitcast (i32 (i32)* @_Z4bar3i to void ()*)() +// CHECK: br label %unatexit.call + +// CHECK: unatexit.call: +// CHECK: %1 = call i32 @unatexit(void ()* bitcast (i32 ()* @_Z4bar2v to void ()*)) +// CHECK: %needs_destruct1 = icmp eq i32 %1, 0 +// CHECK: br i1 %needs_destruct1, label %destruct.call2, label %destruct.end + +// CHECK: destruct.call2: +// CHECK: call void bitcast (i32 ()* @_Z4bar2v to void ()*)() +// CHECK: br label %destruct.end + +// CHECK: destruct.end: +// CHECK: ret void +// CHECK: }