Index: clang/include/clang/AST/Mangle.h =================================================================== --- clang/include/clang/AST/Mangle.h +++ clang/include/clang/AST/Mangle.h @@ -172,6 +172,8 @@ virtual void mangleLambdaSig(const CXXRecordDecl *Lambda, raw_ostream &) = 0; + virtual void mangleDynamicDestructor(const VarDecl *D, raw_ostream &Out) = 0; + static bool classof(const MangleContext *C) { return C->getKind() == MK_Itanium; } Index: clang/lib/AST/ItaniumMangle.cpp =================================================================== --- clang/lib/AST/ItaniumMangle.cpp +++ clang/lib/AST/ItaniumMangle.cpp @@ -161,6 +161,7 @@ void mangleDynamicInitializer(const VarDecl *D, raw_ostream &Out) override; void mangleDynamicAtExitDestructor(const VarDecl *D, raw_ostream &Out) override; + void mangleDynamicDestructor(const VarDecl *D, raw_ostream &Out) override; void mangleSEHFilterExpression(const NamedDecl *EnclosingDecl, raw_ostream &Out) override; void mangleSEHFinallyBlock(const NamedDecl *EnclosingDecl, @@ -5059,6 +5060,13 @@ Mangler.getStream() << D->getName(); } +void ItaniumMangleContextImpl::mangleDynamicDestructor(const VarDecl *D, + raw_ostream &Out) { + // Clang generates these internal-linkage functions as part of its + // implementation of the XL_Clang ABI. + Out << "__cxx_global_var_destruct_" << *D; +} + void ItaniumMangleContextImpl::mangleSEHFilterExpression( const NamedDecl *EnclosingDecl, raw_ostream &Out) { CXXNameMangler Mangler(*this, Out); Index: clang/lib/CodeGen/CGCXXABI.h =================================================================== --- clang/lib/CodeGen/CGCXXABI.h +++ clang/lib/CodeGen/CGCXXABI.h @@ -107,6 +107,10 @@ virtual bool hasMostDerivedReturn(GlobalDecl GD) const { return false; } + virtual bool useSinitAndSterm() const { return false; } + + virtual bool isCXXGlobalInitAndDtorFuncInternal() const { return true; } + /// Returns true if the target allows calling a function through a pointer /// with a different signature than the actual function (or equivalently, /// bitcasting a function or function pointer to a different function type). Index: clang/lib/CodeGen/CGDeclCXX.cpp =================================================================== --- clang/lib/CodeGen/CGDeclCXX.cpp +++ clang/lib/CodeGen/CGDeclCXX.cpp @@ -20,7 +20,9 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/IR/Intrinsics.h" #include "llvm/IR/MDBuilder.h" +#include "llvm/Support/Format.h" #include "llvm/Support/Path.h" +#include "llvm/Transforms/Utils/ModuleUtils.h" using namespace clang; using namespace CodeGen; @@ -249,7 +251,7 @@ llvm::CallInst *call = CGF.Builder.CreateCall(dtor, addr); - // Make sure the call and the callee agree on calling convention. + // Make sure the call and the callee agree on calling convention. if (auto *dtorFn = dyn_cast( dtor.getCallee()->stripPointerCastsAndAliases())) call->setCallingConv(dtorFn->getCallingConv()); @@ -270,18 +272,40 @@ void CodeGenFunction::registerGlobalDtorWithAtExit(llvm::Constant *dtorStub) { // extern "C" int atexit(void (*f)(void)); + assert(cast(dtorStub)->getFunctionType() == + llvm::FunctionType::get(CGM.VoidTy, false) && + "atexit has wrong parameter type."); + llvm::FunctionType *atexitTy = - llvm::FunctionType::get(IntTy, dtorStub->getType(), false); + llvm::FunctionType::get(IntTy, dtorStub->getType(), false); llvm::FunctionCallee atexit = - CGM.CreateRuntimeFunction(atexitTy, "atexit", llvm::AttributeList(), - /*Local=*/true); + CGM.CreateRuntimeFunction(atexitTy, "atexit", llvm::AttributeList()); if (llvm::Function *atexitFn = dyn_cast(atexit.getCallee())) atexitFn->setDoesNotThrow(); EmitNounwindRuntimeCall(atexit, dtorStub); } +llvm::Value * +CodeGenFunction::unregisterGlobalDtorWithUnAtExit(llvm::Constant *dtorStub) { + // extern "C" int unatexit(void (*f)(void)); + assert(cast(dtorStub)->getFunctionType() == + llvm::FunctionType::get(CGM.VoidTy, false) && + "unatexit has wrong parameter type."); + + llvm::FunctionType *unatexitTy = + llvm::FunctionType::get(IntTy, {dtorStub->getType()}, /*isVarArg=*/false); + + llvm::FunctionCallee unatexit = + CGM.CreateRuntimeFunction(unatexitTy, "unatexit", llvm::AttributeList()); + if (llvm::Function *unatexitFn = + cast(unatexit.getCallee())) + unatexitFn->setDoesNotThrow(); + + return EmitNounwindRuntimeCall(unatexit, dtorStub); +} + void CodeGenFunction::EmitCXXGuardedInit(const VarDecl &D, llvm::GlobalVariable *DeclPtr, bool PerformInit) { @@ -347,6 +371,9 @@ SetInternalFunctionAttributes(GlobalDecl(), Fn, FI); + if (!getCXXABI().isCXXGlobalInitAndDtorFuncInternal()) + Fn->setLinkage(llvm::Function::ExternalLinkage); + Fn->setCallingConv(getRuntimeCC()); if (!getLangOpts().Exceptions) @@ -533,8 +560,28 @@ CXXThreadLocals.clear(); } +static std::string getTransformedFileName(llvm::Module &M) { + SmallString<128> FileName = llvm::sys::path::filename(M.getName()); + + if (FileName.empty()) + FileName = ""; + + for (size_t i = 0; i < FileName.size(); ++i) { + // Replace everything that's not [a-zA-Z0-9._] with a _. This set happens + // to be the set of C preprocessing numbers. + if (!isPreprocessingNumberBody(FileName[i])) + FileName[i] = '_'; + } + + return FileName.c_str(); +} + void CodeGenModule::EmitCXXGlobalInitFunc() { + bool UseSinitAndSterm = getCXXABI().useSinitAndSterm(); + if (UseSinitAndSterm) + GlobalUniqueModuleId = getUniqueModuleId(&getModule()).substr(1); + while (!CXXGlobalInits.empty() && !CXXGlobalInits.back()) CXXGlobalInits.pop_back(); @@ -544,8 +591,11 @@ llvm::FunctionType *FTy = llvm::FunctionType::get(VoidTy, false); const CGFunctionInfo &FI = getTypes().arrangeNullaryFunction(); - // Create our global initialization function. + // Create our global prioritized initialization function. if (!PrioritizedCXXGlobalInits.empty()) { + assert(!UseSinitAndSterm && "Prioritized Sinit and Sterm functions are not" + " supported yet."); + SmallVector LocalCXXGlobalInits; llvm::array_pod_sort(PrioritizedCXXGlobalInits.begin(), PrioritizedCXXGlobalInits.end()); @@ -577,45 +627,43 @@ PrioritizedCXXGlobalInits.clear(); } - // Include the filename in the symbol name. Including "sub_" matches gcc and - // makes sure these symbols appear lexicographically behind the symbols with - // priority emitted above. - SmallString<128> FileName = llvm::sys::path::filename(getModule().getName()); - if (FileName.empty()) - FileName = ""; - - for (size_t i = 0; i < FileName.size(); ++i) { - // Replace everything that's not [a-zA-Z0-9._] with a _. This set happens - // to be the set of C preprocessing numbers. - if (!isPreprocessingNumberBody(FileName[i])) - FileName[i] = '_'; - } - - llvm::Function *Fn = CreateGlobalInitOrDestructFunction( - FTy, llvm::Twine("_GLOBAL__sub_I_", FileName), FI); - - CodeGenFunction(*this).GenerateCXXGlobalInitFunc(Fn, CXXGlobalInits); - AddGlobalCtor(Fn); - - // In OpenCL global init functions must be converted to kernels in order to - // be able to launch them from the host. - // FIXME: Some more work might be needed to handle destructors correctly. - // Current initialization function makes use of function pointers callbacks. - // We can't support function pointers especially between host and device. - // However it seems global destruction has little meaning without any - // dynamic resource allocation on the device and program scope variables are - // destroyed by the runtime when program is released. - if (getLangOpts().OpenCL) { - GenOpenCLArgMetadata(Fn); - Fn->setCallingConv(llvm::CallingConv::SPIR_KERNEL); - } + // Create our global initialization function. + if (!CXXGlobalInits.empty()) { + // Include the filename in the symbol name. When not using sinit and sterm + // functions, including "sub_" matches gcc and makes sure these symbols + // appear lexicographically behind the symbols with priority emitted above. + std::string FuncSuffix = UseSinitAndSterm + ? GlobalUniqueModuleId + : getTransformedFileName(getModule()); + + llvm::Function *Fn = CreateGlobalInitOrDestructFunction( + FTy, + (UseSinitAndSterm ? "__sinit80000000_clang_" : "_GLOBAL__sub_I_") + + FuncSuffix, FI, SourceLocation()); + + CodeGenFunction(*this).GenerateCXXGlobalInitFunc(Fn, CXXGlobalInits); + AddGlobalCtor(Fn); + + // In OpenCL global init functions must be converted to kernels in order to + // be able to launch them from the host. + // FIXME: Some more work might be needed to handle destructors correctly. + // Current initialization function makes use of function pointers callbacks. + // We can't support function pointers especially between host and device. + // However it seems global destruction has little meaning without any + // dynamic resource allocation on the device and program scope variables are + // destroyed by the runtime when program is released. + if (getLangOpts().OpenCL) { + GenOpenCLArgMetadata(Fn); + Fn->setCallingConv(llvm::CallingConv::SPIR_KERNEL); + } - if (getLangOpts().HIP) { - Fn->setCallingConv(llvm::CallingConv::AMDGPU_KERNEL); - Fn->addFnAttr("device-init"); - } + if (getLangOpts().HIP) { + Fn->setCallingConv(llvm::CallingConv::AMDGPU_KERNEL); + Fn->addFnAttr("device-init"); + } - CXXGlobalInits.clear(); + CXXGlobalInits.clear(); + } } void CodeGenModule::EmitCXXGlobalDtorFunc() { @@ -623,14 +671,23 @@ return; llvm::FunctionType *FTy = llvm::FunctionType::get(VoidTy, false); + const CGFunctionInfo &FI = getTypes().arrangeNullaryFunction(); + bool UseSinitAndSterm = getCXXABI().useSinitAndSterm(); // Create our global destructor function. - const CGFunctionInfo &FI = getTypes().arrangeNullaryFunction(); - llvm::Function *Fn = - CreateGlobalInitOrDestructFunction(FTy, "_GLOBAL__D_a", FI); + llvm::Function *Fn = nullptr; + if (UseSinitAndSterm) { + std::string FuncSuffix = GlobalUniqueModuleId; + Fn = CreateGlobalInitOrDestructFunction( + FTy, llvm::Twine("__sterm80000000_clang_") + FuncSuffix, FI, + SourceLocation()); + } else { + Fn = CreateGlobalInitOrDestructFunction(FTy, "_GLOBAL__D_a", FI); + } CodeGenFunction(*this).GenerateCXXGlobalDtorsFunc(Fn, CXXGlobalDtors); AddGlobalDtor(Fn); + CXXGlobalDtors.clear(); } /// Emit the code necessary to initialize the given global variable. @@ -743,7 +800,9 @@ llvm::Value *Callee; llvm::Constant *Arg; std::tie(CalleeTy, Callee, Arg) = DtorsAndObjects[e - i - 1]; - llvm::CallInst *CI = Builder.CreateCall(CalleeTy, Callee, Arg); + llvm::CallInst *CI = (Arg == nullptr) + ? Builder.CreateCall(CalleeTy, Callee) + : Builder.CreateCall(CalleeTy, Callee, Arg); // Make sure the call and the callee agree on calling convention. if (llvm::Function *F = dyn_cast(Callee)) CI->setCallingConv(F->getCallingConv()); Index: clang/lib/CodeGen/CodeGenFunction.h =================================================================== --- clang/lib/CodeGen/CodeGenFunction.h +++ clang/lib/CodeGen/CodeGenFunction.h @@ -3979,6 +3979,9 @@ /// Call atexit() with function dtorStub. void registerGlobalDtorWithAtExit(llvm::Constant *dtorStub); + /// Call unatexit() with 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 /// possible to prove that an initialization will be done exactly Index: clang/lib/CodeGen/CodeGenModule.h =================================================================== --- clang/lib/CodeGen/CodeGenModule.h +++ clang/lib/CodeGen/CodeGenModule.h @@ -17,6 +17,7 @@ #include "CodeGenTypeCache.h" #include "CodeGenTypes.h" #include "SanitizerMetadata.h" +#include "clang/AST/Attr.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclOpenMP.h" @@ -395,6 +396,10 @@ /// emitted when the translation unit is complete. CtorList GlobalDtors; + /// A unique trailing identifier as a part of sinit/sterm function when + /// UserSinitAndSterm set as true. + std::string GlobalUniqueModuleId; + /// An ordered map of canonical GlobalDecls to their mangled names. llvm::MapVector MangledDeclNames; llvm::StringMap Manglings; @@ -1044,6 +1049,12 @@ Object); } + /// Add a destructor to the C++ global destructor function. + void AddCXXDtorEntry(llvm::FunctionCallee DtorFn) { + CXXGlobalDtors.emplace_back(DtorFn.getFunctionType(), DtorFn.getCallee(), + nullptr); + } + /// Create or return a runtime function declaration with the specified type /// and name. If \p AssumeConvergent is true, the call will have the /// convergent attribute added. Index: clang/lib/CodeGen/ItaniumCXXABI.cpp =================================================================== --- clang/lib/CodeGen/ItaniumCXXABI.cpp +++ clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -524,7 +524,16 @@ void registerGlobalDtor(CodeGenFunction &CGF, const VarDecl &D, llvm::FunctionCallee dtor, - llvm::Constant *addr) override; + llvm::Constant *addr) override; + + bool useSinitAndSterm() const override { return true; } + + bool isCXXGlobalInitAndDtorFuncInternal() const override { return false; } + +private: + void emitCXXGlobalVarDeclDestructFunc(const VarDecl &D, + llvm::Constant *dtorStub, + llvm::Constant *addr); }; } @@ -4423,8 +4432,66 @@ /// Register a global destructor as best as we know how. void XLCXXABI::registerGlobalDtor(CodeGenFunction &CGF, const VarDecl &D, - llvm::FunctionCallee dtor, - llvm::Constant *addr) { - llvm::report_fatal_error("Static initialization has not been implemented on" - " XL ABI yet."); + llvm::FunctionCallee dtor, + llvm::Constant *addr) { + if (D.getTLSKind() != VarDecl::TLS_None) + llvm::report_fatal_error("Thread local storage unimplemented on AIX yet."); + + // Create __srterm function for the var decl. + llvm::Constant *dtorStub = CGF.createAtExitStub(D, dtor, addr); + + // Register above __srterm with atexit(). + CGF.registerGlobalDtorWithAtExit(dtorStub); + + // Emit __sterm function to unregister __srterm and call __srterm. + emitCXXGlobalVarDeclDestructFunc(D, dtorStub, addr); +} + +void XLCXXABI::emitCXXGlobalVarDeclDestructFunc(const VarDecl &D, + llvm::Constant *dtorStub, + llvm::Constant *addr) { + llvm::FunctionType *FTy = llvm::FunctionType::get(CGM.VoidTy, false); + SmallString<256> FnName; + { + llvm::raw_svector_ostream Out(FnName); + getMangleContext().mangleDynamicDestructor(&D, Out); + } + + // Create a variable destruction function. + const CGFunctionInfo &FI = CGM.getTypes().arrangeNullaryFunction(); + llvm::Function *StermFn = + CGM.CreateGlobalInitOrDestructFunction(FTy, FnName.str(), FI, + D.getLocation()); + + CodeGenFunction CGF(CGM); + + CGF.StartFunction(GlobalDecl(), CGM.getContext().VoidTy, StermFn, FI, + FunctionArgList()); + + // Unregister the dtorStub. + llvm::Value *V = CGF.unregisterGlobalDtorWithUnAtExit(dtorStub); + + llvm::Value *NeedsDestruct = CGF.Builder.CreateIsNull(V, "guard.hasSrterm"); + + llvm::BasicBlock *DestructCheckBlock = CGF.createBasicBlock("destruct.check"); + llvm::BasicBlock *EndBlock = CGF.createBasicBlock("destruct.end"); + + // Check if the guard variable is zero. + CGF.EmitCXXGuardedInitBranch(NeedsDestruct, DestructCheckBlock, EndBlock, + CodeGenFunction::GuardKind::VariableGuard, &D); + + CGF.EmitBlock(DestructCheckBlock); + + // Emit the call to dtorStub. + llvm::CallInst *CI = CGF.Builder.CreateCall(dtorStub); + + // Make sure the call and the callee agree on calling convention. + if (llvm::Function *F = cast(dtorStub)) + CI->setCallingConv(F->getCallingConv()); + + CGF.EmitBlock(EndBlock); + + CGF.FinishFunction(); + + CGM.AddCXXDtorEntry(StermFn); } Index: clang/lib/Sema/SemaDeclAttr.cpp =================================================================== --- clang/lib/Sema/SemaDeclAttr.cpp +++ clang/lib/Sema/SemaDeclAttr.cpp @@ -6819,7 +6819,11 @@ handlePassObjectSizeAttr(S, D, AL); break; case ParsedAttr::AT_Constructor: - handleConstructorAttr(S, D, AL); + if (S.Context.getTargetInfo().getTriple().isOSAIX()) + S.Diag(AL.getLoc(), diag::warn_attribute_type_not_supported) + << AL << ""; + else + handleConstructorAttr(S, D, AL); break; case ParsedAttr::AT_CXX11NoReturn: handleSimpleAttribute(S, D, AL); @@ -6828,7 +6832,11 @@ handleDeprecatedAttr(S, D, AL); break; case ParsedAttr::AT_Destructor: - handleDestructorAttr(S, D, AL); + if (S.Context.getTargetInfo().getTriple().isOSAIX()) + S.Diag(AL.getLoc(), diag::warn_attribute_type_not_supported) + << AL << ""; + else + handleDestructorAttr(S, D, AL); break; case ParsedAttr::AT_EnableIf: handleEnableIfAttr(S, D, AL); @@ -7055,7 +7063,11 @@ handleSimpleAttribute(S, D, AL); break; case ParsedAttr::AT_InitPriority: - handleInitPriorityAttr(S, D, AL); + if (S.Context.getTargetInfo().getTriple().isOSAIX()) + S.Diag(AL.getLoc(), diag::warn_attribute_type_not_supported) + << AL << ""; + else + handleInitPriorityAttr(S, D, AL); break; case ParsedAttr::AT_Packed: handlePackedAttr(S, D, AL); Index: clang/test/CodeGen/aix-priority-attribute.cpp =================================================================== --- /dev/null +++ clang/test/CodeGen/aix-priority-attribute.cpp @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -triple powerpc-ibm-aix-xcoff -x c++ -emit-llvm < %s 2>&1 | \ +// RUN: FileCheck %s +// RUN: %clang_cc1 -triple powerpc64-ibm-aix-xcoff -x c++ -emit-llvm < %s 2>&1 | \ +// RUN: FileCheck %s + +int foo() __attribute__((constructor(180))); +int bar() __attribute__((destructor(180))); + +class test { + int a; +public: + test(int c) {a = c;} + ~test() {a = 0;} +}; + +__attribute__ ((init_priority (2000))) +test t(1); + +// CHECK: warning: 'constructor' attribute argument not supported: +// CHECK: int foo() __attribute__((constructor(180))); + +// CHECK: warning: 'destructor' attribute argument not supported: +// check: int bar() __attribute__((destructor(180))); + +// CHECK: warning: 'init_priority' attribute argument not supported: +// CHECK: __attribute__ ((init_priority (2000))) + Index: clang/test/CodeGen/static-init.cpp =================================================================== --- clang/test/CodeGen/static-init.cpp +++ clang/test/CodeGen/static-init.cpp @@ -1,12 +1,55 @@ -// RUN: not %clang_cc1 -triple powerpc-ibm-aix-xcoff -S -emit-llvm -x c++ %s \ -// RUN: 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple powerpc-ibm-aix-xcoff -S -emit-llvm -x c++ < %s \ +// RUN: | FileCheck %s -// RUN: not %clang_cc1 -triple powerpc64-ibm-aix-xcoff -S -emit-llvm -x c++ %s \ -// RUN: 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple powerpc64-ibm-aix-xcoff -S -emit-llvm -x c++ < %s \ +// RUN: | FileCheck %s struct test { test(); ~test(); } t; -// CHECK: error in backend: Static initialization has not been implemented on XL ABI yet. +// CHECK: @llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* @__sinit80000000_clang_b2e4830f1c9d2d063e5ea946868f3bfd, i8* null }] +// CHECK: @llvm.global_dtors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* @__sterm80000000_clang_b2e4830f1c9d2d063e5ea946868f3bfd, i8* null }] +// CHECK: define dso_local void @__cxx_global_var_init() #0 { +// CHECK: entry: +// CHECK: call void @_ZN4testC1Ev(%struct.test* @t) +// CHECK: %0 = call i32 @atexit(void ()* @__dtor_t) +// CHECK: ret void +// CHECK: } + +// CHECK: define dso_local void @__dtor_t() #0 { +// CHECK: entry: +// CHECK: call void @_ZN4testD1Ev(%struct.test* @t) +// CHECK: ret void +// CHECK: } + +// CHECK: declare i32 @atexit(void ()*) #3 + +// CHECK: define dso_local void @__cxx_global_var_destruct_t() #0 { +// CHECK: entry: +// CHECK: %0 = call i32 @unatexit(void ()* @__dtor_t) +// CHECK: %guard.hasSrterm = icmp eq i32 %0, 0 +// CHECK: br i1 %guard.hasSrterm, label %destruct.check, label %destruct.end + +// CHECK: destruct.check: +// CHECK: call void @__dtor_t() +// CHECK: br label %destruct.end + +// CHECK: destruct.end: +// CHECK: ret void +// CHECK: } + +// CHECK: declare i32 @unatexit(void ()*) #3 + +// CHECK: define dso_local void @__sinit80000000_clang_b2e4830f1c9d2d063e5ea946868f3bfd() #0 { +// CHECK: entry: +// CHECK: call void @__cxx_global_var_init() +// CHECK: ret void +// CHECK: } + +// CHECK: define dso_local void @__sterm80000000_clang_b2e4830f1c9d2d063e5ea946868f3bfd() #0 { +// CHECK: entry: +// CHECK: call void @__cxx_global_var_destruct_t() +// CHECK: ret void +// CHECK: }