diff --git a/clang/lib/CodeGen/CGDeclCXX.cpp b/clang/lib/CodeGen/CGDeclCXX.cpp index c551901acc43..8131b5285075 100644 --- a/clang/lib/CodeGen/CGDeclCXX.cpp +++ b/clang/lib/CodeGen/CGDeclCXX.cpp @@ -499,7 +499,8 @@ CodeGenModule::EmitCXXGlobalVarDeclInitFunc(const VarDecl *D, } else if (PerformInit && ISA) { EmitPointerToInitFunc(D, Addr, Fn, ISA); } else if (auto *IPA = D->getAttr()) { - OrderGlobalInits Key(IPA->getPriority(), PrioritizedCXXGlobalInits.size()); + OrderGlobalInitsOrStermFinalizers Key(IPA->getPriority(), + PrioritizedCXXGlobalInits.size()); PrioritizedCXXGlobalInits.push_back(std::make_pair(Key, Fn)); } else if (isTemplateInstantiation(D->getTemplateSpecializationKind()) || getContext().GetGVALinkageForVariable(D) == GVA_DiscardableODR) { @@ -566,6 +567,17 @@ static SmallString<128> getTransformedFileName(llvm::Module &M) { return FileName; } +static std::string getPrioritySuffix(unsigned int Priority) { + assert(Priority <= 65535 && "Priority should always be <= 65535."); + + // Compute the function suffix from priority. Prepend with zeroes to make + // sure the function names are also ordered as priorities. + std::string PrioritySuffix = llvm::utostr(Priority); + PrioritySuffix = std::string(6 - PrioritySuffix.size(), '0') + PrioritySuffix; + + return PrioritySuffix; +} + void CodeGenModule::EmitCXXGlobalInitFunc() { while (!CXXGlobalInits.empty() && !CXXGlobalInits.back()) @@ -577,12 +589,8 @@ CodeGenModule::EmitCXXGlobalInitFunc() { llvm::FunctionType *FTy = llvm::FunctionType::get(VoidTy, false); const CGFunctionInfo &FI = getTypes().arrangeNullaryFunction(); - const bool UseSinitAndSterm = getCXXABI().useSinitAndSterm(); // 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()); @@ -596,14 +604,10 @@ CodeGenModule::EmitCXXGlobalInitFunc() { PrioE = std::upper_bound(I + 1, E, *I, GlobalInitPriorityCmp()); LocalCXXGlobalInits.clear(); - unsigned Priority = I->first.priority; - // Compute the function suffix from priority. Prepend with zeroes to make - // sure the function names are also ordered as priorities. - std::string PrioritySuffix = llvm::utostr(Priority); - // Priority is always <= 65535 (enforced by sema). - PrioritySuffix = std::string(6-PrioritySuffix.size(), '0')+PrioritySuffix; + + unsigned int Priority = I->first.priority; llvm::Function *Fn = CreateGlobalInitOrCleanUpFunction( - FTy, "_GLOBAL__I_" + PrioritySuffix, FI); + FTy, "_GLOBAL__I_" + getPrioritySuffix(Priority), FI); for (; I < PrioE; ++I) LocalCXXGlobalInits.push_back(I->second); @@ -614,7 +618,7 @@ CodeGenModule::EmitCXXGlobalInitFunc() { PrioritizedCXXGlobalInits.clear(); } - if (UseSinitAndSterm && CXXGlobalInits.empty()) + if (getCXXABI().useSinitAndSterm() && CXXGlobalInits.empty()) return; // Include the filename in the symbol name. Including "sub_" matches gcc @@ -649,12 +653,50 @@ CodeGenModule::EmitCXXGlobalInitFunc() { } void CodeGenModule::EmitCXXGlobalCleanUpFunc() { - if (CXXGlobalDtorsOrStermFinalizers.empty()) + if (CXXGlobalDtorsOrStermFinalizers.empty() && + PrioritizedCXXStermFinalizers.empty()) return; llvm::FunctionType *FTy = llvm::FunctionType::get(VoidTy, false); const CGFunctionInfo &FI = getTypes().arrangeNullaryFunction(); + // Create our global prioritized cleanup function. + if (!PrioritizedCXXStermFinalizers.empty()) { + SmallVector LocalCXXStermFinalizers; + llvm::array_pod_sort(PrioritizedCXXStermFinalizers.begin(), + PrioritizedCXXStermFinalizers.end()); + // Iterate over "chunks" of dtors with same priority and emit each chunk + // into separate function. Note - everything is sorted first by priority, + // second - by lex order, so we emit dtor functions in proper order. + for (SmallVectorImpl::iterator + I = PrioritizedCXXStermFinalizers.begin(), + E = PrioritizedCXXStermFinalizers.end(); + I != E;) { + SmallVectorImpl::iterator PrioE = + std::upper_bound(I + 1, E, *I, StermFinalizerPriorityCmp()); + + LocalCXXStermFinalizers.clear(); + + unsigned int Priority = I->first.priority; + llvm::Function *Fn = CreateGlobalInitOrCleanUpFunction( + FTy, "_GLOBAL__a_" + getPrioritySuffix(Priority), FI); + + for (; I < PrioE; ++I) { + llvm::FunctionCallee DtorFn = I->second; + LocalCXXStermFinalizers.emplace_back(DtorFn.getFunctionType(), + DtorFn.getCallee(), nullptr); + } + + CodeGenFunction(*this).GenerateCXXGlobalCleanUpFunc( + Fn, LocalCXXStermFinalizers); + AddGlobalDtor(Fn, Priority); + } + PrioritizedCXXStermFinalizers.clear(); + } + + if (CXXGlobalDtorsOrStermFinalizers.empty()) + return; + // Create our global cleanup function. llvm::Function *Fn = CreateGlobalInitOrCleanUpFunction(FTy, "_GLOBAL__D_a", FI); @@ -761,8 +803,9 @@ CodeGenFunction::GenerateCXXGlobalInitFunc(llvm::Function *Fn, void CodeGenFunction::GenerateCXXGlobalCleanUpFunc( llvm::Function *Fn, - const std::vector> &DtorsOrStermFinalizers) { + ArrayRef> + DtorsOrStermFinalizers) { { auto NL = ApplyDebugLocation::CreateEmpty(*this); StartFunction(GlobalDecl(), getContext().VoidTy, Fn, diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index c14918912323..f14a1c248396 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -4369,8 +4369,9 @@ public: /// variables. void GenerateCXXGlobalCleanUpFunc( llvm::Function *Fn, - const std::vector> &DtorsOrStermFinalizers); + ArrayRef> + DtorsOrStermFinalizers); void GenerateCXXGlobalVarDeclInitFunc(llvm::Function *Fn, const VarDecl *D, diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index ad432445e315..83230dbd3dee 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -103,17 +103,17 @@ enum ForDefinition_t : bool { ForDefinition = true }; -struct OrderGlobalInits { +struct OrderGlobalInitsOrStermFinalizers { unsigned int priority; unsigned int lex_order; - OrderGlobalInits(unsigned int p, unsigned int l) + OrderGlobalInitsOrStermFinalizers(unsigned int p, unsigned int l) : priority(p), lex_order(l) {} - bool operator==(const OrderGlobalInits &RHS) const { + bool operator==(const OrderGlobalInitsOrStermFinalizers &RHS) const { return priority == RHS.priority && lex_order == RHS.lex_order; } - bool operator<(const OrderGlobalInits &RHS) const { + bool operator<(const OrderGlobalInitsOrStermFinalizers &RHS) const { return std::tie(priority, lex_order) < std::tie(RHS.priority, RHS.lex_order); } @@ -457,7 +457,8 @@ private: /// that we don't re-emit the initializer. llvm::DenseMap DelayedCXXInitPosition; - typedef std::pair GlobalInitData; + typedef std::pair + GlobalInitData; struct GlobalInitPriorityCmp { bool operator()(const GlobalInitData &LHS, @@ -473,10 +474,26 @@ private: /// 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> + typedef std::tuple + CXXGlobalDtorsOrStermFinalizer_t; + SmallVector CXXGlobalDtorsOrStermFinalizers; + typedef std::pair + StermFinalizerData; + + struct StermFinalizerPriorityCmp { + bool operator()(const StermFinalizerData &LHS, + const StermFinalizerData &RHS) const { + return LHS.first.priority < RHS.first.priority; + } + }; + + /// Global variables with sterm finalizers whose order of initialization is + /// set by init_priority attribute. + SmallVector PrioritizedCXXStermFinalizers; + /// The complete set of modules that has been imported. llvm::SetVector ImportedModules; @@ -1078,6 +1095,14 @@ public: AddGlobalDtor(StermFinalizer, Priority); } + void AddCXXPrioritizedStermFinalizerEntry(llvm::Function *StermFinalizer, + int Priority) { + OrderGlobalInitsOrStermFinalizers Key(Priority, + PrioritizedCXXStermFinalizers.size()); + PrioritizedCXXStermFinalizers.push_back( + std::make_pair(Key, StermFinalizer)); + } + /// 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. diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp index e0cbcb238d1c..c10ee0446912 100644 --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -4728,16 +4728,17 @@ void XLCXXABI::emitCXXStermFinalizer(const VarDecl &D, llvm::Function *dtorStub, CGF.FinishFunction(); - assert(!D.getAttr() && - "Prioritized sinit and sterm functions are not yet supported."); - - if (isTemplateInstantiation(D.getTemplateSpecializationKind()) || - getContext().GetGVALinkageForVariable(&D) == GVA_DiscardableODR) + if (auto *IPA = D.getAttr()) { + CGM.AddCXXPrioritizedStermFinalizerEntry(StermFinalizer, + IPA->getPriority()); + } else if (isTemplateInstantiation(D.getTemplateSpecializationKind()) || + getContext().GetGVALinkageForVariable(&D) == GVA_DiscardableODR) { // According to C++ [basic.start.init]p2, class template static data // members (i.e., implicitly or explicitly instantiated specializations) // have unordered initialization. As a consequence, we can put them into // their own llvm.global_dtors entry. CGM.AddCXXStermFinalizerToGlobalDtor(StermFinalizer, 65535); - else + } else { CGM.AddCXXStermFinalizerEntry(StermFinalizer); + } } diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 138b9f0132b5..550fa503bf56 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -7926,10 +7926,6 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, handleVecTypeHint(S, D, AL); break; case ParsedAttr::AT_InitPriority: - 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: diff --git a/clang/test/CodeGen/aix-init-priority-attribute.cpp b/clang/test/CodeGen/aix-init-priority-attribute.cpp index a19a54247e69..0aa2bc2c3985 100644 --- a/clang/test/CodeGen/aix-init-priority-attribute.cpp +++ b/clang/test/CodeGen/aix-init-priority-attribute.cpp @@ -1,19 +1,72 @@ -// 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 < %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 < %s | \ // RUN: FileCheck %s -class test { - int a; - -public: - test(int c) { a = c; } - ~test() { a = 0; } +struct test { + test() {} + ~test() {} }; -__attribute__((init_priority(2000))) -test t(1); +__attribute__((init_priority(200))) +test t1; +__attribute__((init_priority(200))) +test t2; +__attribute__((init_priority(300))) +test t3; +__attribute__((init_priority(150))) +test t4; +test t5; -// CHECK: fatal error: error in backend: 'init_priority' attribute is not yet supported on AIX +// CHECK: @llvm.global_ctors = appending global [4 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 150, void ()* @_GLOBAL__I_000150, i8* null }, { i32, void ()*, i8* } { i32 200, void ()* @_GLOBAL__I_000200, i8* null }, { i32, void ()*, i8* } { i32 300, void ()* @_GLOBAL__I_000300, i8* null }, { 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 150, void ()* @_GLOBAL__a_000150, i8* null }, { i32, void ()*, i8* } { i32 200, void ()* @_GLOBAL__a_000200, i8* null }, { i32, void ()*, i8* } { i32 300, void ()* @_GLOBAL__a_000300, i8* null }, { i32, void ()*, i8* } { i32 65535, void ()* @_GLOBAL__D_a, i8* null }] + +// CHECK: define internal void @_GLOBAL__I_000150() [[ATTR:#[0-9]+]] { +// CHECK: entry: +// CHECK: call void @__cxx_global_var_init.3() +// CHECK: ret void +// CHECK: } + +// CHECK: define internal void @_GLOBAL__I_000200() [[ATTR:#[0-9]+]] { +// CHECK: entry: +// CHECK: call void @__cxx_global_var_init() +// CHECK: call void @__cxx_global_var_init.1() +// CHECK: ret void +// CHECK: } + +// CHECK: define internal void @_GLOBAL__I_000300() [[ATTR:#[0-9]+]] { +// CHECK: entry: +// CHECK: call void @__cxx_global_var_init.2() +// CHECK: ret void +// CHECK: } + +// CHECK: define internal void @_GLOBAL__sub_I__() [[ATTR:#[0-9]+]] { +// CHECK: entry: +// CHECK: call void @__cxx_global_var_init.4() +// CHECK: ret void +// CHECK: } + +// CHECK: define internal void @_GLOBAL__a_000150() [[ATTR:#[0-9]+]] { +// CHECK: entry: +// CHECK: call void @__finalize_t4() +// CHECK: ret void +// CHECK: } + +// CHECK: define internal void @_GLOBAL__a_000200() [[ATTR:#[0-9]+]] { +// CHECK: entry: +// CHECK: call void @__finalize_t2() +// CHECK: call void @__finalize_t1() +// CHECK: ret void +// CHECK: } + +// CHECK: define internal void @_GLOBAL__a_000300() [[ATTR:#[0-9]+]] { +// CHECK: entry: +// CHECK: call void @__finalize_t3() +// CHECK: ret void +// CHECK: } + +// CHECK: define internal void @_GLOBAL__D_a() [[ATTR:#[0-9]+]] { +// CHECK: entry: +// CHECK: call void @__finalize_t5() +// CHECK: ret void +// CHECK: }