diff --git a/clang/include/clang/AST/Mangle.h b/clang/include/clang/AST/Mangle.h --- a/clang/include/clang/AST/Mangle.h +++ b/clang/include/clang/AST/Mangle.h @@ -175,6 +175,8 @@ virtual void mangleLambdaSig(const CXXRecordDecl *Lambda, raw_ostream &) = 0; + virtual void mangleDynamicStermFinalizer(const VarDecl *D, raw_ostream &) = 0; + bool isUniqueNameMangler() { return IsUniqueNameMangler; } static bool classof(const MangleContext *C) { diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -160,6 +160,7 @@ void mangleDynamicInitializer(const VarDecl *D, raw_ostream &Out) override; void mangleDynamicAtExitDestructor(const VarDecl *D, raw_ostream &Out) override; + void mangleDynamicStermFinalizer(const VarDecl *D, raw_ostream &Out) override; void mangleSEHFilterExpression(const NamedDecl *EnclosingDecl, raw_ostream &Out) override; void mangleSEHFinallyBlock(const NamedDecl *EnclosingDecl, @@ -5230,6 +5231,18 @@ Mangler.getStream() << D->getName(); } +void ItaniumMangleContextImpl::mangleDynamicStermFinalizer(const VarDecl *D, + raw_ostream &Out) { + // Clang generates these internal-linkage functions as part of its + // implementation of the XL ABI. + CXXNameMangler Mangler(*this, Out); + Mangler.getStream() << "__finalize_"; + if (shouldMangleDeclName(D)) + Mangler.mangle(D); + else + Mangler.getStream() << D->getName(); +} + void ItaniumMangleContextImpl::mangleSEHFilterExpression( const NamedDecl *EnclosingDecl, raw_ostream &Out) { CXXNameMangler Mangler(*this, Out); diff --git a/clang/lib/CodeGen/CGCXXABI.h b/clang/lib/CodeGen/CGCXXABI.h --- a/clang/lib/CodeGen/CGCXXABI.h +++ b/clang/lib/CodeGen/CGCXXABI.h @@ -108,6 +108,8 @@ virtual bool hasMostDerivedReturn(GlobalDecl GD) const { return false; } + virtual bool useSinitAndSterm() const { return false; } + /// 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). 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 @@ -21,6 +21,7 @@ #include "llvm/IR/Intrinsics.h" #include "llvm/IR/MDBuilder.h" #include "llvm/Support/Path.h" +#include "llvm/Transforms/Utils/ModuleUtils.h" using namespace clang; using namespace CodeGen; @@ -239,7 +240,7 @@ } const CGFunctionInfo &FI = CGM.getTypes().arrangeNullaryFunction(); - llvm::Function *fn = CGM.CreateGlobalInitOrDestructFunction( + llvm::Function *fn = CGM.CreateGlobalInitOrCleanUpFunction( ty, FnName.str(), FI, VD.getLocation()); CodeGenFunction CGF(CGM); @@ -249,7 +250,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,8 +271,12 @@ void CodeGenFunction::registerGlobalDtorWithAtExit(llvm::Constant *dtorStub) { // extern "C" int atexit(void (*f)(void)); + assert(cast(dtorStub)->getFunctionType() == + llvm::FunctionType::get(CGM.VoidTy, false) && + "Argument to atexit has a wrong 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(), @@ -282,6 +287,30 @@ EmitNounwindRuntimeCall(atexit, dtorStub); } +llvm::Value * +CodeGenFunction::unregisterGlobalDtorWithUnAtExit(llvm::Function *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 + // termination and the unatexit returns a value of 0, otherwise a non-zero + // value is returned. + // + // extern "C" int unatexit(void (*f)(void)); + assert(dtorStub->getFunctionType() == + llvm::FunctionType::get(CGM.VoidTy, false) && + "Argument to unatexit has a wrong type."); + + llvm::FunctionType *unatexitTy = + llvm::FunctionType::get(IntTy, {dtorStub->getType()}, /*isVarArg=*/false); + + llvm::FunctionCallee unatexit = + CGM.CreateRuntimeFunction(unatexitTy, "unatexit", llvm::AttributeList()); + + cast(unatexit.getCallee())->setDoesNotThrow(); + + return EmitNounwindRuntimeCall(unatexit, dtorStub); +} + void CodeGenFunction::EmitCXXGuardedInit(const VarDecl &D, llvm::GlobalVariable *DeclPtr, bool PerformInit) { @@ -333,19 +362,23 @@ Builder.CreateCondBr(NeedsInit, InitBlock, NoInitBlock, Weights); } -llvm::Function *CodeGenModule::CreateGlobalInitOrDestructFunction( +llvm::Function *CodeGenModule::CreateGlobalInitOrCleanUpFunction( llvm::FunctionType *FTy, const Twine &Name, const CGFunctionInfo &FI, - SourceLocation Loc, bool TLS) { - llvm::Function *Fn = - llvm::Function::Create(FTy, llvm::GlobalValue::InternalLinkage, - Name, &getModule()); + SourceLocation Loc, bool TLS, bool IsExternalLinkage) { + llvm::Function *Fn = llvm::Function::Create( + FTy, + IsExternalLinkage ? llvm::GlobalValue::ExternalLinkage + : llvm::GlobalValue::InternalLinkage, + Name, &getModule()); + if (!getLangOpts().AppleKext && !TLS) { // Set the section if needed. if (const char *Section = getTarget().getStaticInitSectionSpecifier()) Fn->setSection(Section); } - SetInternalFunctionAttributes(GlobalDecl(), Fn, FI); + if (Fn->hasInternalLinkage()) + SetInternalFunctionAttributes(GlobalDecl(), Fn, FI); Fn->setCallingConv(getRuntimeCC()); @@ -461,10 +494,8 @@ } // Create a variable initialization function. - llvm::Function *Fn = - CreateGlobalInitOrDestructFunction(FTy, FnName.str(), - getTypes().arrangeNullaryFunction(), - D->getLocation()); + llvm::Function *Fn = CreateGlobalInitOrCleanUpFunction( + FTy, FnName.str(), getTypes().arrangeNullaryFunction(), D->getLocation()); auto *ISA = D->getAttr(); CodeGenFunction(*this).GenerateCXXGlobalVarDeclInitFunc(Fn, D, Addr, @@ -557,11 +588,27 @@ if (CXXGlobalInits.empty() && PrioritizedCXXGlobalInits.empty()) return; + const bool UseSinitAndSterm = getCXXABI().useSinitAndSterm(); + if (UseSinitAndSterm) { + GlobalUniqueModuleId = getUniqueModuleId(&getModule()); + + // FIXME: We need to figure out what to hash on or encode into the unique ID + // we need. + if (GlobalUniqueModuleId.compare("") == 0) + llvm::report_fatal_error( + "cannot produce a unique identifier for this module" + " based on strong external symbols"); + GlobalUniqueModuleId = GlobalUniqueModuleId.substr(1); + } + 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()); @@ -581,7 +628,7 @@ std::string PrioritySuffix = llvm::utostr(Priority); // Priority is always <= 65535 (enforced by sema). PrioritySuffix = std::string(6-PrioritySuffix.size(), '0')+PrioritySuffix; - llvm::Function *Fn = CreateGlobalInitOrDestructFunction( + llvm::Function *Fn = CreateGlobalInitOrCleanUpFunction( FTy, "_GLOBAL__I_" + PrioritySuffix, FI); for (; I < PrioE; ++I) @@ -593,12 +640,27 @@ 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. - llvm::Function *Fn = CreateGlobalInitOrDestructFunction( - FTy, - llvm::Twine("_GLOBAL__sub_I_", getTransformedFileName(getModule())), FI); + if (UseSinitAndSterm && CXXGlobalInits.empty()) + return; + + // Create our global initialization function. + SmallString<128> FuncName; + bool IsExternalLinkage = false; + if (UseSinitAndSterm) { + llvm::Twine("__sinit80000000_clang_", GlobalUniqueModuleId) + .toVector(FuncName); + IsExternalLinkage = true; + } else { + // 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. + llvm::Twine("_GLOBAL__sub_I_", getTransformedFileName(getModule())) + .toVector(FuncName); + } + + llvm::Function *Fn = CreateGlobalInitOrCleanUpFunction( + FTy, FuncName, FI, SourceLocation(), false /* TLS */, + IsExternalLinkage); CodeGenFunction(*this).GenerateCXXGlobalInitFunc(Fn, CXXGlobalInits); AddGlobalCtor(Fn); @@ -624,20 +686,38 @@ CXXGlobalInits.clear(); } -void CodeGenModule::EmitCXXGlobalDtorFunc() { - if (CXXGlobalDtors.empty()) +void CodeGenModule::EmitCXXGlobalCleanUpFunc() { + if (CXXGlobalDtorsOrStermFinalizers.empty()) return; llvm::FunctionType *FTy = llvm::FunctionType::get(VoidTy, false); - - // Create our global destructor function. const CGFunctionInfo &FI = getTypes().arrangeNullaryFunction(); - llvm::Function *Fn = - CreateGlobalInitOrDestructFunction(FTy, "_GLOBAL__D_a", FI); - CodeGenFunction(*this).GenerateCXXGlobalDtorsFunc(Fn, CXXGlobalDtors); + // Create our global cleanup function. + llvm::Function *Fn = nullptr; + if (getCXXABI().useSinitAndSterm()) { + if (GlobalUniqueModuleId.empty()) { + GlobalUniqueModuleId = getUniqueModuleId(&getModule()); + // FIXME: We need to figure out what to hash on or encode into the unique + // ID we need. + if (GlobalUniqueModuleId.compare("") == 0) + llvm::report_fatal_error( + "cannot produce a unique identifier for this module" + " based on strong external symbols"); + GlobalUniqueModuleId = GlobalUniqueModuleId.substr(1); + } + + Fn = CreateGlobalInitOrCleanUpFunction( + FTy, llvm::Twine("__sterm80000000_clang_", GlobalUniqueModuleId), FI, + SourceLocation(), false /* TLS */, true /* IsExternalLinkage */); + } else { + Fn = CreateGlobalInitOrCleanUpFunction(FTy, "_GLOBAL__D_a", FI); + } + + CodeGenFunction(*this).GenerateCXXGlobalCleanUpFunc( + Fn, CXXGlobalDtorsOrStermFinalizers); AddGlobalDtor(Fn); - CXXGlobalDtors.clear(); + CXXGlobalDtorsOrStermFinalizers.clear(); } /// Emit the code necessary to initialize the given global variable. @@ -733,10 +813,10 @@ FinishFunction(); } -void CodeGenFunction::GenerateCXXGlobalDtorsFunc( +void CodeGenFunction::GenerateCXXGlobalCleanUpFunc( llvm::Function *Fn, const std::vector> &DtorsAndObjects) { + llvm::Constant *>> &DtorsOrStermFinalizers) { { auto NL = ApplyDebugLocation::CreateEmpty(*this); StartFunction(GlobalDecl(), getContext().VoidTy, Fn, @@ -744,13 +824,22 @@ // Emit an artificial location for this function. auto AL = ApplyDebugLocation::CreateArtificial(*this); - // Emit the dtors, in reverse order from construction. - for (unsigned i = 0, e = DtorsAndObjects.size(); i != e; ++i) { + // Emit the cleanups, in reverse order from construction. + for (unsigned i = 0, e = DtorsOrStermFinalizers.size(); i != e; ++i) { llvm::FunctionType *CalleeTy; llvm::Value *Callee; llvm::Constant *Arg; - std::tie(CalleeTy, Callee, Arg) = DtorsAndObjects[e - i - 1]; - llvm::CallInst *CI = Builder.CreateCall(CalleeTy, Callee, Arg); + std::tie(CalleeTy, Callee, Arg) = DtorsOrStermFinalizers[e - i - 1]; + + llvm::CallInst *CI = nullptr; + if (Arg == nullptr) { + assert( + CGM.getCXXABI().useSinitAndSterm() && + "Arg could not be nullptr unless using sinit and sterm functions."); + CI = Builder.CreateCall(CalleeTy, Callee); + } else + CI = 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()); @@ -774,7 +863,7 @@ const CGFunctionInfo &FI = CGM.getTypes().arrangeBuiltinFunctionDeclaration(getContext().VoidTy, args); llvm::FunctionType *FTy = CGM.getTypes().GetFunctionType(FI); - llvm::Function *fn = CGM.CreateGlobalInitOrDestructFunction( + llvm::Function *fn = CGM.CreateGlobalInitOrCleanUpFunction( FTy, "__cxx_global_array_dtor", FI, VD->getLocation()); CurEHLocation = VD->getBeginLoc(); diff --git a/clang/lib/CodeGen/CGOpenMPRuntime.cpp b/clang/lib/CodeGen/CGOpenMPRuntime.cpp --- a/clang/lib/CodeGen/CGOpenMPRuntime.cpp +++ b/clang/lib/CodeGen/CGOpenMPRuntime.cpp @@ -1837,7 +1837,7 @@ llvm::FunctionType *FTy = CGM.getTypes().GetFunctionType(FI); std::string Name = getName({"__kmpc_global_ctor_", ""}); llvm::Function *Fn = - CGM.CreateGlobalInitOrDestructFunction(FTy, Name, FI, Loc); + CGM.CreateGlobalInitOrCleanUpFunction(FTy, Name, FI, Loc); CtorCGF.StartFunction(GlobalDecl(), CGM.getContext().VoidPtrTy, Fn, FI, Args, Loc, Loc); llvm::Value *ArgVal = CtorCGF.EmitLoadOfScalar( @@ -1870,7 +1870,7 @@ llvm::FunctionType *FTy = CGM.getTypes().GetFunctionType(FI); std::string Name = getName({"__kmpc_global_dtor_", ""}); llvm::Function *Fn = - CGM.CreateGlobalInitOrDestructFunction(FTy, Name, FI, Loc); + CGM.CreateGlobalInitOrCleanUpFunction(FTy, Name, FI, Loc); auto NL = ApplyDebugLocation::CreateEmpty(DtorCGF); DtorCGF.StartFunction(GlobalDecl(), CGM.getContext().VoidTy, Fn, FI, Args, Loc, Loc); @@ -1913,7 +1913,7 @@ auto *InitFunctionTy = llvm::FunctionType::get(CGM.VoidTy, /*isVarArg*/ false); std::string Name = getName({"__omp_threadprivate_init_", ""}); - llvm::Function *InitFunction = CGM.CreateGlobalInitOrDestructFunction( + llvm::Function *InitFunction = CGM.CreateGlobalInitOrCleanUpFunction( InitFunctionTy, Name, CGM.getTypes().arrangeNullaryFunction()); CodeGenFunction InitCGF(CGM); FunctionArgList ArgList; @@ -1975,7 +1975,7 @@ const CGFunctionInfo &FI = CGM.getTypes().arrangeNullaryFunction(); llvm::FunctionType *FTy = CGM.getTypes().GetFunctionType(FI); - llvm::Function *Fn = CGM.CreateGlobalInitOrDestructFunction( + llvm::Function *Fn = CGM.CreateGlobalInitOrCleanUpFunction( FTy, Twine(Buffer, "_ctor"), FI, Loc); auto NL = ApplyDebugLocation::CreateEmpty(CtorCGF); CtorCGF.StartFunction(GlobalDecl(), CGM.getContext().VoidTy, Fn, FI, @@ -2013,7 +2013,7 @@ const CGFunctionInfo &FI = CGM.getTypes().arrangeNullaryFunction(); llvm::FunctionType *FTy = CGM.getTypes().GetFunctionType(FI); - llvm::Function *Fn = CGM.CreateGlobalInitOrDestructFunction( + llvm::Function *Fn = CGM.CreateGlobalInitOrCleanUpFunction( FTy, Twine(Buffer, "_dtor"), FI, Loc); auto NL = ApplyDebugLocation::CreateEmpty(DtorCGF); DtorCGF.StartFunction(GlobalDecl(), CGM.getContext().VoidTy, Fn, FI, @@ -10039,7 +10039,7 @@ const auto &FI = CGM.getTypes().arrangeNullaryFunction(); llvm::FunctionType *FTy = CGM.getTypes().GetFunctionType(FI); std::string ReqName = getName({"omp_offloading", "requires_reg"}); - RequiresRegFn = CGM.CreateGlobalInitOrDestructFunction(FTy, ReqName, FI); + RequiresRegFn = CGM.CreateGlobalInitOrCleanUpFunction(FTy, ReqName, FI); CGF.StartFunction(GlobalDecl(), C.VoidTy, RequiresRegFn, FI, {}); OpenMPOffloadingRequiresDirFlags Flags = OMP_REQ_NONE; // TODO: check for other requires clauses. 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 @@ -4197,6 +4197,9 @@ /// Call atexit() with function dtorStub. void registerGlobalDtorWithAtExit(llvm::Constant *dtorStub); + /// Call unatexit() with function dtorStub. + llvm::Value *unregisterGlobalDtorWithUnAtExit(llvm::Function *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 @@ -4220,12 +4223,12 @@ ArrayRef CXXThreadLocals, ConstantAddress Guard = ConstantAddress::invalid()); - /// GenerateCXXGlobalDtorsFunc - Generates code for destroying global + /// GenerateCXXGlobalCleanUpFunc - Generates code for cleaning up global /// variables. - void GenerateCXXGlobalDtorsFunc( + void GenerateCXXGlobalCleanUpFunc( llvm::Function *Fn, const std::vector> &DtorsAndObjects); + llvm::Constant *>> &DtorsOrStermFinalizers); void GenerateCXXGlobalVarDeclInitFunc(llvm::Function *Fn, const VarDecl *D, 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 @@ -397,6 +397,10 @@ /// emitted when the translation unit is complete. CtorList GlobalDtors; + /// A unique trailing identifier as a part of sinit/sterm function when + /// UseSinitAndSterm of CXXABI is set as true. + std::string GlobalUniqueModuleId; + /// An ordered map of canonical GlobalDecls to their mangled names. llvm::MapVector MangledDeclNames; llvm::StringMap Manglings; @@ -465,9 +469,11 @@ SmallVector PrioritizedCXXGlobalInits; /// Global destructor functions and arguments that need to run on termination. + /// When UseSinitAndSterm is set, it instead contains sterm finalizer + /// functions, which also run on unloading a shared library. std::vector< std::tuple> - CXXGlobalDtors; + CXXGlobalDtorsOrStermFinalizers; /// The complete set of modules that has been imported. llvm::SetVector ImportedModules; @@ -815,11 +821,10 @@ llvm::GlobalValue::LinkageTypes Linkage, unsigned Alignment); - llvm::Function * - CreateGlobalInitOrDestructFunction(llvm::FunctionType *ty, const Twine &name, - const CGFunctionInfo &FI, - SourceLocation Loc = SourceLocation(), - bool TLS = false); + llvm::Function *CreateGlobalInitOrCleanUpFunction( + llvm::FunctionType *ty, const Twine &name, const CGFunctionInfo &FI, + SourceLocation Loc = SourceLocation(), bool TLS = false, + bool IsExternalLinkage = false); /// Return the AST address space of the underlying global variable for D, as /// determined by its declaration. Normally this is the same as the address @@ -1048,8 +1053,14 @@ /// Add a destructor and object to add to the C++ global destructor function. void AddCXXDtorEntry(llvm::FunctionCallee DtorFn, llvm::Constant *Object) { - CXXGlobalDtors.emplace_back(DtorFn.getFunctionType(), DtorFn.getCallee(), - Object); + CXXGlobalDtorsOrStermFinalizers.emplace_back(DtorFn.getFunctionType(), + DtorFn.getCallee(), Object); + } + + /// Add an sterm finalizer to the C++ global cleanup function. + void AddCXXStermFinalizerEntry(llvm::FunctionCallee DtorFn) { + CXXGlobalDtorsOrStermFinalizers.emplace_back(DtorFn.getFunctionType(), + DtorFn.getCallee(), nullptr); } /// Create or return a runtime function declaration with the specified type @@ -1449,8 +1460,8 @@ /// Emit the function that initializes C++ globals. void EmitCXXGlobalInitFunc(); - /// Emit the function that destroys C++ globals. - void EmitCXXGlobalDtorFunc(); + /// Emit the function that performs cleanup associated with C++ globals. + void EmitCXXGlobalCleanUpFunc(); /// Emit the function that initializes the specified global (if PerformInit is /// true) and registers its destructor. 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 @@ -411,7 +411,7 @@ checkAliases(); emitMultiVersionFunctions(); EmitCXXGlobalInitFunc(); - EmitCXXGlobalDtorFunc(); + EmitCXXGlobalCleanUpFunc(); registerGlobalDtorsWithAtExit(); EmitCXXThreadLocalInitFunc(); if (ObjCRuntime) 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 @@ -526,6 +526,12 @@ void registerGlobalDtor(CodeGenFunction &CGF, const VarDecl &D, llvm::FunctionCallee dtor, llvm::Constant *addr) override; + + bool useSinitAndSterm() const override { return true; } + +private: + void emitCXXStermFinalizer(const VarDecl &D, llvm::Function *dtorStub, + llvm::Constant *addr); }; } @@ -2525,7 +2531,7 @@ std::string GlobalInitFnName = std::string("__GLOBAL_init_") + llvm::to_string(Priority); llvm::FunctionType *FTy = llvm::FunctionType::get(VoidTy, false); - llvm::Function *GlobalInitFn = CreateGlobalInitOrDestructFunction( + llvm::Function *GlobalInitFn = CreateGlobalInitOrCleanUpFunction( FTy, GlobalInitFnName, getTypes().arrangeNullaryFunction(), SourceLocation()); ASTContext &Ctx = getContext(); @@ -2679,9 +2685,9 @@ llvm::FunctionType *FTy = llvm::FunctionType::get(CGM.VoidTy, /*isVarArg=*/false); const CGFunctionInfo &FI = CGM.getTypes().arrangeNullaryFunction(); - InitFunc = CGM.CreateGlobalInitOrDestructFunction(FTy, "__tls_init", FI, - SourceLocation(), - /*TLS=*/true); + InitFunc = CGM.CreateGlobalInitOrCleanUpFunction(FTy, "__tls_init", FI, + SourceLocation(), + /*TLS=*/true); llvm::GlobalVariable *Guard = new llvm::GlobalVariable( CGM.getModule(), CGM.Int8Ty, /*isConstant=*/false, llvm::GlobalVariable::InternalLinkage, @@ -4516,6 +4522,67 @@ 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."); + if (D.getTLSKind() != VarDecl::TLS_None) + llvm::report_fatal_error("thread local storage not yet implemented on AIX"); + + // Create __dtor function for the var decl. + llvm::Function *dtorStub = CGF.createAtExitStub(D, dtor, addr); + + if (CGM.getCodeGenOpts().CXAAtExit) + llvm::report_fatal_error("using __cxa_atexit unsupported on AIX"); + // Register above __dtor with atexit(). + CGF.registerGlobalDtorWithAtExit(dtorStub); + + // Emit __finalize function to unregister __dtor and (as appropriate) call + // __dtor. + emitCXXStermFinalizer(D, dtorStub, addr); +} + +void XLCXXABI::emitCXXStermFinalizer(const VarDecl &D, llvm::Function *dtorStub, + llvm::Constant *addr) { + llvm::FunctionType *FTy = llvm::FunctionType::get(CGM.VoidTy, false); + SmallString<256> FnName; + { + llvm::raw_svector_ostream Out(FnName); + getMangleContext().mangleDynamicStermFinalizer(&D, Out); + } + + // Create the finalization action associated with a variable. + const CGFunctionInfo &FI = CGM.getTypes().arrangeNullaryFunction(); + llvm::Function *StermFinalizer = CGM.CreateGlobalInitOrCleanUpFunction( + FTy, FnName.str(), FI, D.getLocation()); + + CodeGenFunction CGF(CGM); + + CGF.StartFunction(GlobalDecl(), CGM.getContext().VoidTy, StermFinalizer, FI, + FunctionArgList()); + + // The unatexit subroutine unregisters __dtor functions that were previously + // registered by the atexit subroutine. If the referenced function is found, + // the unatexit returns a value of 0, meaning that the cleanup is still + // pending (and we should call the __dtor function). + llvm::Value *V = CGF.unregisterGlobalDtorWithUnAtExit(dtorStub); + + llvm::Value *NeedsDestruct = CGF.Builder.CreateIsNull(V, "needs_destruct"); + + llvm::BasicBlock *DestructCallBlock = CGF.createBasicBlock("destruct.call"); + llvm::BasicBlock *EndBlock = CGF.createBasicBlock("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 dtorStub. + llvm::CallInst *CI = CGF.Builder.CreateCall(dtorStub); + + // Make sure the call and the callee agree on calling convention. + CI->setCallingConv(dtorStub->getCallingConv()); + + CGF.EmitBlock(EndBlock); + + CGF.FinishFunction(); + + CGM.AddCXXStermFinalizerEntry(StermFinalizer); } diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp --- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp @@ -2349,7 +2349,7 @@ if (!NonComdatInits.empty()) { llvm::FunctionType *FTy = llvm::FunctionType::get(CGM.VoidTy, /*isVarArg=*/false); - llvm::Function *InitFunc = CGM.CreateGlobalInitOrDestructFunction( + llvm::Function *InitFunc = CGM.CreateGlobalInitOrCleanUpFunction( FTy, "__tls_init", CGM.getTypes().arrangeNullaryFunction(), SourceLocation(), /*TLS=*/true); CodeGenFunction(CGM).GenerateCXXGlobalInitFunc(InitFunc, NonComdatInits); 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 @@ -6939,13 +6939,20 @@ handlePassObjectSizeAttr(S, D, AL); break; case ParsedAttr::AT_Constructor: - handleConstructorAttr(S, D, AL); + 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: - handleDestructorAttr(S, D, AL); + 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: handleEnableIfAttr(S, D, AL); @@ -7139,7 +7146,11 @@ handleVecTypeHint(S, D, AL); break; case ParsedAttr::AT_InitPriority: - handleInitPriorityAttr(S, D, AL); + if (S.Context.getTargetInfo().getTriple().isOSAIX()) + llvm::report_fatal_error( + "'init_priority' attribute is not yet supported on AIX"); + else + handleInitPriorityAttr(S, D, AL); break; case ParsedAttr::AT_Packed: handlePackedAttr(S, D, AL); diff --git a/clang/test/CodeGen/aix-constructor-attribute.cpp b/clang/test/CodeGen/aix-constructor-attribute.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/aix-constructor-attribute.cpp @@ -0,0 +1,20 @@ +// 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 foo() __attribute__((constructor(180))); + +class test { + int a; + +public: + test(int c) { a = c; } + ~test() { a = 0; } +}; + +test t(1); + +// CHECK: fatal error: error in backend: 'constructor' attribute is not yet supported on AIX diff --git a/clang/test/CodeGen/aix-destructor-attribute.cpp b/clang/test/CodeGen/aix-destructor-attribute.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/aix-destructor-attribute.cpp @@ -0,0 +1,20 @@ +// 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/CodeGen/aix-init-priority-attribute.cpp b/clang/test/CodeGen/aix-init-priority-attribute.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/aix-init-priority-attribute.cpp @@ -0,0 +1,19 @@ +// 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 + +class test { + int a; + +public: + test(int c) { a = c; } + ~test() { a = 0; } +}; + +__attribute__((init_priority(2000))) +test t(1); + +// CHECK: fatal error: error in backend: 'init_priority' attribute is not yet supported on AIX diff --git a/clang/test/CodeGen/static-init.cpp b/clang/test/CodeGen/static-init.cpp deleted file mode 100644 --- a/clang/test/CodeGen/static-init.cpp +++ /dev/null @@ -1,12 +0,0 @@ -// RUN: not %clang_cc1 -triple powerpc-ibm-aix-xcoff -S -emit-llvm -x c++ %s \ -// RUN: -o /dev/null 2>&1 | FileCheck %s - -// RUN: not %clang_cc1 -triple powerpc64-ibm-aix-xcoff -S -emit-llvm -x c++ %s \ -// RUN: -o /dev/null 2>&1 | FileCheck %s - -struct test { - test(); - ~test(); -} t; - -// CHECK: error in backend: Static initialization has not been implemented on XL ABI yet. diff --git a/clang/test/CodeGenCXX/aix-static-init.cpp b/clang/test/CodeGenCXX/aix-static-init.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/aix-static-init.cpp @@ -0,0 +1,193 @@ +// RUN: %clang_cc1 -triple powerpc-ibm-aix-xcoff -S -emit-llvm -x c++ \ +// RUN: -fno-use-cxa-atexit -std=c++2a < %s | \ +// RUN: FileCheck --check-prefixes=CHECK,CHECK32 %s + +// RUN: %clang_cc1 -triple powerpc64-ibm-aix-xcoff -S -emit-llvm -x c++ \ +// RUN: -fno-use-cxa-atexit -std=c++2a < %s | \ +// RUN: FileCheck --check-prefixes=CHECK,CHECK64 %s + +namespace test1 { + struct Test1 { + Test1(); + ~Test1(); + } t1, t2; +} // namespace test1 + +namespace test2 { + int foo() { return 3; } + int x = foo(); +} // namespace test2 + +namespace test3 { + struct Test3 { + constexpr Test3() {}; + ~Test3() {}; + }; + + constinit Test3 t; +} // namespace test3 + +namespace test4 { + struct Test4 { + Test4(); + ~Test4(); + }; + + void f() { + static Test4 staticLocal; + } +} // namespace test4 + +// CHECK: @llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* @__sinit80000000_clang_1145401da454a7baad10bfe313c46638, i8* null }] +// CHECK: @llvm.global_dtors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* @__sterm80000000_clang_1145401da454a7baad10bfe313c46638, i8* null }] + +// CHECK: define internal void @__cxx_global_var_init() [[ATTR:#[0-9]+]] { +// CHECK: entry: +// CHECK: call void @_ZN5test15Test1C1Ev(%"struct.test1::Test1"* @_ZN5test12t1E) +// CHECK: %0 = call i32 @atexit(void ()* @__dtor__ZN5test12t1E) +// CHECK: ret void +// CHECK: } + +// CHECK: define internal void @__dtor__ZN5test12t1E() [[ATTR:#[0-9]+]] { +// CHECK: entry: +// CHECK: call void @_ZN5test15Test1D1Ev(%"struct.test1::Test1"* @_ZN5test12t1E) +// CHECK: ret void +// CHECK: } + +// CHECK: declare i32 @atexit(void ()*) + +// CHECK: define internal void @__finalize__ZN5test12t1E() [[ATTR:#[0-9]+]] { +// CHECK: entry: +// CHECK: %0 = call i32 @unatexit(void ()* @__dtor__ZN5test12t1E) +// 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 @__dtor__ZN5test12t1E() +// CHECK: br label %destruct.end + +// CHECK: destruct.end: +// CHECK: ret void +// CHECK: } + +// CHECK: declare i32 @unatexit(void ()*) + +// CHECK: define internal void @__cxx_global_var_init.1() [[ATTR:#[0-9]+]] { +// CHECK: entry: +// CHECK: call void @_ZN5test15Test1C1Ev(%"struct.test1::Test1"* @_ZN5test12t2E) +// CHECK: %0 = call i32 @atexit(void ()* @__dtor__ZN5test12t2E) +// CHECK: ret void +// CHECK: } + +// CHECK: define internal void @__dtor__ZN5test12t2E() [[ATTR:#[0-9]+]] { +// CHECK: entry: +// CHECK: call void @_ZN5test15Test1D1Ev(%"struct.test1::Test1"* @_ZN5test12t2E) +// CHECK: ret void +// CHECK: } + +// CHECK: define internal void @__finalize__ZN5test12t2E() [[ATTR:#[0-9]+]] { +// CHECK: entry: +// CHECK: %0 = call i32 @unatexit(void ()* @__dtor__ZN5test12t2E) +// 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 @__dtor__ZN5test12t2E() +// CHECK: br label %destruct.end + +// CHECK: destruct.end: +// CHECK: ret void +// CHECK: } + +// CHECK: define internal void @__cxx_global_var_init.2() [[ATTR:#[0-9]+]] { +// CHECK: entry: +// CHECK32: %call = call i32 @_ZN5test23fooEv() +// CHECK64: %call = call signext i32 @_ZN5test23fooEv() +// CHECK: store i32 %call, i32* @_ZN5test21xE +// CHECK: ret void +// CHECK: } + +// CHECK: define internal void @__cxx_global_var_init.3() [[ATTR:#[0-9]+]] { +// CHECK: entry: +// CHECK: %0 = call i32 @atexit(void ()* @__dtor__ZN5test31tE) +// CHECK: ret void +// CHECK: } + +// CHECK: define internal void @__dtor__ZN5test31tE() [[ATTR:#[0-9]+]] { +// CHECK: entry: +// CHECK: call void @_ZN5test35Test3D1Ev(%"struct.test3::Test3"* @_ZN5test31tE) +// CHECK: ret void +// CHECK: } + +// CHECK: define internal void @__finalize__ZN5test31tE() [[ATTR:#[0-9]+]] { +// CHECK: entry: +// CHECK: %0 = call i32 @unatexit(void ()* @__dtor__ZN5test31tE) +// 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 @__dtor__ZN5test31tE() +// CHECK: br label %destruct.end + +// CHECK: destruct.end: +// CHECK: ret void +// CHECK: } + +// CHECK: define void @_ZN5test41fEv() [[ATTR:#[0-9]+]] { +// CHECK: entry: +// CHECK: %0 = load atomic i8, i8* bitcast (i64* @_ZGVZN5test41fEvE11staticLocal to i8*) acquire +// CHECK: %guard.uninitialized = icmp eq i8 %0, 0 +// CHECK: br i1 %guard.uninitialized, label %init.check, label %init.end + +// CHECK: init.check: +// CHECK: %1 = call i32 @__cxa_guard_acquire(i64* @_ZGVZN5test41fEvE11staticLocal) +// CHECK: %tobool = icmp ne i32 %1, 0 +// CHECK: br i1 %tobool, label %init, label %init.end + +// CHECK: init: +// CHECK: call void @_ZN5test45Test4C1Ev(%"struct.test4::Test4"* @_ZZN5test41fEvE11staticLocal) +// CHECK: %2 = call i32 @atexit(void ()* @__dtor__ZZN5test41fEvE11staticLocal) +// CHECK: call void @__cxa_guard_release(i64* @_ZGVZN5test41fEvE11staticLocal) +// CHECK: br label %init.end + +// CHECK: init.end: +// CHECK: ret void +// CHECK: } + +// CHECK: define internal void @__dtor__ZZN5test41fEvE11staticLocal() [[ATTR:#[0-9]+]] { +// CHECK: entry: +// CHECK: call void @_ZN5test45Test4D1Ev(%"struct.test4::Test4"* @_ZZN5test41fEvE11staticLocal) +// CHECK: ret void +// CHECK: } + +// CHECK: define internal void @__finalize__ZZN5test41fEvE11staticLocal() [[ATTR:#[0-9]+]] { +// CHECK: entry: +// CHECK: %0 = call i32 @unatexit(void ()* @__dtor__ZZN5test41fEvE11staticLocal) +// 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 @__dtor__ZZN5test41fEvE11staticLocal() +// CHECK: br label %destruct.end + +// CHECK: destruct.end: +// CHECK: ret void +// CHECK: } + +// CHECK: define void @__sinit80000000_clang_1145401da454a7baad10bfe313c46638() [[ATTR:#[0-9]+]] { +// CHECK: entry: +// CHECK: call void @__cxx_global_var_init() +// CHECK: call void @__cxx_global_var_init.1() +// CHECK: call void @__cxx_global_var_init.2() +// CHECK: call void @__cxx_global_var_init.3() +// CHECK: ret void +// CHECK: } + +// CHECK: define void @__sterm80000000_clang_1145401da454a7baad10bfe313c46638() [[ATTR:#[0-9]+]] { +// CHECK: entry: +// CHECK: call void @__finalize__ZZN5test41fEvE11staticLocal() +// CHECK: call void @__finalize__ZN5test31tE() +// CHECK: call void @__finalize__ZN5test12t2E() +// CHECK: call void @__finalize__ZN5test12t1E() +// CHECK: ret void +// CHECK: }