diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -438,8 +438,11 @@ bool isCudaSharedVar = getLangOpts().CUDA && getLangOpts().CUDAIsDevice && D.hasAttr(); // If this value has an initializer, emit it. - if (D.getInit() && !isCudaSharedVar) + if (D.getInit() && !isCudaSharedVar) { var = AddInitializerToStaticVarDecl(D, var); + if (CGM.getTriple().isOSAIX()) + CGM.addVarWithInitTerm(&D, var); + } var->setAlignment(alignment.getAsAlign()); 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 @@ -538,6 +538,9 @@ llvm::Function *Fn = CreateGlobalInitOrCleanUpFunction( FTy, FnName.str(), getTypes().arrangeNullaryFunction(), D->getLocation()); + if (getTriple().isOSAIX()) + addVarTermAssoc(D, Fn); + auto *ISA = D->getAttr(); CodeGenFunction(*this).GenerateCXXGlobalVarDeclInitFunc(Fn, D, Addr, PerformInit); @@ -679,6 +682,11 @@ for (; I < PrioE; ++I) LocalCXXGlobalInits.push_back(I->second); + if (getTriple().isOSAIX()) { + auto GetElem = [](auto &V, unsigned i) { return V[i]; }; + updateAssociatedFunc(VFInitTermAssoc, LocalCXXGlobalInits, GetElem, Fn); + } + CodeGenFunction(*this).GenerateCXXGlobalInitFunc(Fn, LocalCXXGlobalInits); AddGlobalCtor(Fn, Priority); } @@ -695,6 +703,11 @@ FTy, llvm::Twine("_GLOBAL__sub_I_", getTransformedFileName(getModule())), FI); + if (getTriple().isOSAIX()) { + auto GetElem = [](auto &V, unsigned i) { return V[i]; }; + updateAssociatedFunc(VFInitTermAssoc, CXXGlobalInits, GetElem, Fn); + } + CodeGenFunction(*this).GenerateCXXGlobalInitFunc(Fn, CXXGlobalInits); AddGlobalCtor(Fn); @@ -721,6 +734,34 @@ CXXGlobalInits.clear(); } +void CodeGenModule::genAssocMeta() { + auto AddMeta = [this](auto &Assoc, auto &LA) { + for (auto I = Assoc.begin(), E = Assoc.end(); I != E;) { + llvm::SmallVector Metas; + auto Src = I->first; + + for (; I < E && I->first == Src; ++I) + Metas.push_back(llvm::ValueAsMetadata::get(I->second)); + + llvm::MDNode *MD = llvm::MDNode::get(getLLVMContext(), Metas); + if (auto *GO = dyn_cast(LA(Src))) + GO->addMetadata(llvm::LLVMContext::MD_associated, *MD); + } + }; + // Generate dtor to term .ref + auto LM1 = [](llvm::Function *C) { return C; }; + AddMeta(FFDtorTermAssoc, LM1); + + // Generate var to init/term .ref + auto LM2 = [this](const Decl *C) { + assert((VarsWithInitTerm.find(C) != VarsWithInitTerm.end()) && + "genAssocMeta does not have var info"); + return VarsWithInitTerm[C]; + }; + llvm::array_pod_sort(VFInitTermAssoc.begin(), VFInitTermAssoc.end()); + AddMeta(VFInitTermAssoc, LM2); +} + void CodeGenModule::EmitCXXGlobalCleanUpFunc() { if (CXXGlobalDtorsOrStermFinalizers.empty() && PrioritizedCXXStermFinalizers.empty()) @@ -756,6 +797,14 @@ DtorFn.getCallee(), nullptr); } + if (getTriple().isOSAIX()) { + auto GetElem = [](auto &V, unsigned i) { return std::get<1>(V[i]); }; + updateAssociatedFunc(VFInitTermAssoc, LocalCXXStermFinalizers, GetElem, + Fn); + updateAssociatedFunc(FFDtorTermAssoc, LocalCXXStermFinalizers, GetElem, + Fn); + } + CodeGenFunction(*this).GenerateCXXGlobalCleanUpFunc( Fn, LocalCXXStermFinalizers); AddGlobalDtor(Fn, Priority); @@ -770,6 +819,14 @@ llvm::Function *Fn = CreateGlobalInitOrCleanUpFunction(FTy, "_GLOBAL__D_a", FI); + if (getTriple().isOSAIX()) { + auto GetElem = [](auto &V, unsigned i) { return std::get<1>(V[i]); }; + updateAssociatedFunc(VFInitTermAssoc, CXXGlobalDtorsOrStermFinalizers, + GetElem, Fn); + updateAssociatedFunc(FFDtorTermAssoc, CXXGlobalDtorsOrStermFinalizers, + GetElem, Fn); + } + CodeGenFunction(*this).GenerateCXXGlobalCleanUpFunc( Fn, CXXGlobalDtorsOrStermFinalizers); AddGlobalDtor(Fn); 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 @@ -459,6 +459,21 @@ /// init_priority attribute. SmallVector PrioritizedCXXGlobalInits; + /// To support `-bcdtors:csect` on AIX, we are adding .ref pseudo-op to + /// associate variable with init/term functions, and maintain the dependency + /// between dtor and term functions. + llvm::SmallVector, 8> + VFInitTermAssoc; + llvm::SmallVector, 8> + FFDtorTermAssoc; + llvm::DenseMap VarsWithInitTerm; + + void cleanupAssoc() { + VFInitTermAssoc.clear(); + FFDtorTermAssoc.clear(); + VarsWithInitTerm.clear(); + } + /// 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. @@ -595,6 +610,42 @@ const std::string &getModuleNameHash() const { return ModuleNameHash; } + /// As wrapper functions are generated, update .ref association point to the + /// wrapper + template + void updateAssociatedFunc( + llvm::SmallVector, 8> &Assoc, TB &Funcs, + F &GetElem, llvm::Function *Fn) { + for (auto I = Assoc.begin(), E = Assoc.end(); I != E; ++I) { + bool Found = false; + for (unsigned i = 0, e = Funcs.size(); i != e; ++i) + if (I->second == GetElem(Funcs, i)) { + Found = true; + break; + } + + if (Found) + *I = std::make_pair(I->first, Fn); + } + } + + template + void updateInitTermAssociatedFunc(T &Funcs, F &GetElem, llvm::Function *Fn) { + updateAssociatedFunc(VFInitTermAssoc, Funcs, GetElem, Fn); + } + + void addVarTermAssoc(const Decl *Src, llvm::Constant *Target) { + VFInitTermAssoc.push_back(std::make_pair(Src, Target)); + } + + void addDtorTermAssoc(llvm::Function *Src, llvm::Constant *Target) { + FFDtorTermAssoc.push_back(std::make_pair(Src, Target)); + } + + void addVarWithInitTerm(const Decl *Var, llvm::Constant *C) { + VarsWithInitTerm[Var] = C; + } + /// Return a reference to the configured OpenCL runtime. CGOpenCLRuntime &getOpenCLRuntime() { assert(OpenCLRuntime != nullptr); @@ -1568,6 +1619,9 @@ /// Call replaceAllUsesWith on all pairs in GlobalValReplacements. void applyGlobalValReplacements(); + /// Generate associated metadata for static init/term + void genAssocMeta(); + void checkAliases(); std::map> DtorsUsingAtExit; 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 @@ -519,6 +519,10 @@ EmitCXXGlobalCleanUpFunc(); registerGlobalDtorsWithAtExit(); EmitCXXThreadLocalInitFunc(); + if (getTriple().isOSAIX()) { + genAssocMeta(); + cleanupAssoc(); + } if (ObjCRuntime) if (llvm::Function *ObjCInitFunction = ObjCRuntime->ModuleInitFunction()) AddGlobalCtor(ObjCInitFunction); @@ -4789,8 +4793,11 @@ maybeSetTrivialComdat(*D, *GV); // Emit the initializer function if necessary. - if (NeedsGlobalCtor || NeedsGlobalDtor) + if (NeedsGlobalCtor || NeedsGlobalDtor) { EmitCXXGlobalVarDeclInitFunc(D, GV, NeedsGlobalCtor); + if (getTriple().isOSAIX()) + addVarWithInitTerm(D, GV); + } SanitizerMD->reportGlobalToASan(GV, *D, NeedsGlobalCtor); 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 @@ -2817,6 +2817,12 @@ InitFunc = CGM.CreateGlobalInitOrCleanUpFunction(FTy, "__tls_init", FI, SourceLocation(), /*TLS=*/true); + + if (CGM.getTriple().isOSAIX()) { + auto GetElem = [](auto &V, unsigned i) { return V[i]; }; + CGM.updateInitTermAssociatedFunc(OrderedInits, GetElem, InitFunc); + } + llvm::GlobalVariable *Guard = new llvm::GlobalVariable( CGM.getModule(), CGM.Int8Ty, /*isConstant=*/false, llvm::GlobalVariable::InternalLinkage, @@ -4781,6 +4787,11 @@ llvm::Function *StermFinalizer = CGM.CreateGlobalInitOrCleanUpFunction( FTy, FnName.str(), FI, D.getLocation()); + if (CGM.getTriple().isOSAIX()) { + CGM.addVarTermAssoc(&D, StermFinalizer); + CGM.addDtorTermAssoc(dtorStub, StermFinalizer); + } + CodeGenFunction CGF(CGM); CGF.StartFunction(GlobalDecl(), CGM.getContext().VoidTy, StermFinalizer, FI, diff --git a/clang/test/CodeGen/PowerPC/aix-init-ref-null.cpp b/clang/test/CodeGen/PowerPC/aix-init-ref-null.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/PowerPC/aix-init-ref-null.cpp @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 -triple powerpc64-ibm-aix-xcoff -emit-llvm -O3 -x c++ \ +// RUN: -debug-info-kind=limited < %s | \ +// RUN: FileCheck %s + +typedef enum { + A, B +} E; + +class base { +protected: + base(E v): m(v) { } +private: + E m; +}; + +struct ext: base { + ext():base(A){} +}; + +ext base_rview; + +// CHECK: @base_rview = local_unnamed_addr global %struct.ext zeroinitializer, align [[ALIGN:[0-9]+]], !dbg ![[DBG:[0-9]+]], !associated ![[ASSOC0:[0-9]+]] +// CHECK: @llvm.global_ctors = appending global [0 x { i32, ptr, ptr }] zeroinitializer +// CHECK: ![[ASSOC0]] = distinct !{null} diff --git a/clang/test/CodeGen/PowerPC/aix-ref-static-var.cpp b/clang/test/CodeGen/PowerPC/aix-ref-static-var.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/PowerPC/aix-ref-static-var.cpp @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -triple powerpc64-ibm-aix-xcoff -emit-llvm -x c++ \ +// RUN: -debug-info-kind=limited < %s | \ +// RUN: FileCheck %s +// + +struct S { + ~S(); +}; +void f() { + static S s; +} + +// CHECK: @_ZZ1fvE1s = internal global %struct.S zeroinitializer, align [[ALIGN:[0-9]+]], !dbg ![[DBG0:[0-9]+]], !associated ![[ASSOC0:[0-9]+]] +// CHECK: define internal void @__dtor__ZZ1fvE1s() [[ATTR:#[0-9]+]] !dbg ![[DBG1:[0-9]+]] !associated ![[ASSOC0:[0-9]+]] { +// CHECK: ![[ASSOC0]] = !{ptr @_GLOBAL__D_a} diff --git a/clang/test/CodeGen/PowerPC/aix-ref-tls_init.cpp b/clang/test/CodeGen/PowerPC/aix-ref-tls_init.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/PowerPC/aix-ref-tls_init.cpp @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -triple powerpc64-ibm-aix-xcoff -emit-llvm -x c++ \ +// RUN: -debug-info-kind=limited < %s | \ +// RUN: FileCheck %s + +int &f(); +thread_local int &r = f(); +int &g() { return r; } + +// CHECK: @r = thread_local global ptr null, align [[ALIGN:[0-9]+]], !dbg ![[DBG0:[0-9]+]], !associated ![[ASSOC0:[0-9]+]] +// CHECK: ![[ASSOC0]] = !{ptr @__tls_init} diff --git a/clang/test/CodeGenCXX/aix-static-init-debug-info.cpp b/clang/test/CodeGenCXX/aix-static-init-debug-info.cpp --- a/clang/test/CodeGenCXX/aix-static-init-debug-info.cpp +++ b/clang/test/CodeGenCXX/aix-static-init-debug-info.cpp @@ -13,6 +13,8 @@ X v; +// CHECK: @v = global %struct.X zeroinitializer, align {{[0-9]+}}, !dbg !{{[0-9]+}}, !associated ![[ASSOC1:[0-9]+]] + // CHECK: define internal void @__cxx_global_var_init() [[ATTR:#[0-9]+]] !dbg ![[DBGVAR16:[0-9]+]] { // CHECK: entry: // CHECK: call void @_ZN1XC1Ev(%struct.X* {{[^,]*}} @v), !dbg ![[DBGVAR19:[0-9]+]] @@ -20,7 +22,7 @@ // CHECK: ret void, !dbg ![[DBGVAR19]] // CHECK: } -// CHECK: define internal void @__dtor_v() [[ATTR:#[0-9]+]] !dbg ![[DBGVAR20:[0-9]+]] { +// CHECK: define internal void @__dtor_v() [[ATTR:#[0-9]+]] !dbg ![[DBGVAR20:[0-9]+]] !associated ![[ASSOC2:[0-9]+]] { // CHECK: entry: // CHECK: call void @_ZN1XD1Ev(%struct.X* @v), !dbg ![[DBGVAR21b:[0-9]+]] // CHECK: ret void, !dbg ![[DBGVAR21:[0-9]+]] @@ -52,10 +54,12 @@ // CHECK: ret void // CHECK: } +// CHECK: ![[ASSOC1]] = !{void ()* @{{_GLOBAL__sub_I__|_GLOBAL__D_a}}, void ()* @{{_GLOBAL__sub_I__|_GLOBAL__D_a}}} // CHECK: ![[DBGVAR16]] = distinct !DISubprogram(name: "__cxx_global_var_init", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, type: !{{[0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !{{[0-9]+}}, retainedNodes: !{{[0-9]+}}) // CHECK: ![[DBGVAR19]] = !DILocation(line: 14, column: 3, scope: ![[DBGVAR16]]) // CHECK: ![[DBGVAR19b]] = !DILocation(line: 0, scope: ![[DBGVAR16]]) // CHECK: ![[DBGVAR20]] = distinct !DISubprogram(name: "__dtor_v", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: 14, type: !{{[0-9]+}}, scopeLine: 14, flags: DIFlagArtificial, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !{{[0-9]+}}, retainedNodes: !{{[0-9]+}}) +// CHECK: ![[ASSOC2]] = !{void ()* @_GLOBAL__D_a} // CHECK: ![[DBGVAR21b]] = !DILocation(line: 0, scope: ![[DBGVAR20]]) // CHECK: ![[DBGVAR21]] = !DILocation(line: 14, column: 3, scope: ![[DBGVAR20]]) // CHECK: ![[DBGVAR22]] = distinct !DISubprogram(linkageName: "__finalize_v", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: 14, type: !{{[0-9]+}}, scopeLine: 14, flags: DIFlagArtificial, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !{{[0-9]+}}, retainedNodes: !{{[0-9]+}}) diff --git a/clang/test/CodeGenCXX/aix-static-init-temp-spec-and-inline-var.cpp b/clang/test/CodeGenCXX/aix-static-init-temp-spec-and-inline-var.cpp --- a/clang/test/CodeGenCXX/aix-static-init-temp-spec-and-inline-var.cpp +++ b/clang/test/CodeGenCXX/aix-static-init-temp-spec-and-inline-var.cpp @@ -44,8 +44,13 @@ A A::instance = bar(); } // namespace test2 +// CHECK: @_ZN5test12t0E = global %"struct.test1::Test1" zeroinitializer, align {{[0-9]+}}, !associated ![[ASSOC0:[0-9]+]] +// CHECK: @_ZN5test12t2E = linkonce_odr global %"struct.test1::Test1" zeroinitializer, align {{[0-9]+}}, !associated ![[ASSOC1:[0-9]+]] // CHECK: @_ZGVN5test12t2E = linkonce_odr global i64 0, align 8 +// CHECK: @_ZN5test12t1IiEE = linkonce_odr global %"struct.test1::Test1" zeroinitializer, align {{[0-9]+}}, !associated ![[ASSOC2:[0-9]+]] +// CHECK: @_ZN5test21AIvE8instanceE = weak_odr global %"struct.test2::A" zeroinitializer, align {{[0-9]+}}, !associated ![[ASSOC3:[0-9]+]] // CHECK: @_ZGVN5test21AIvE8instanceE = weak_odr global i64 0, align 8 +// CHECK: @_ZN5test21AIiE8instanceE = global %"struct.test2::A.0" zeroinitializer, align {{[0-9]+}}, !associated ![[ASSOC0:[0-9]+]] // CHECK: @_ZGVN5test12t1IiEE = linkonce_odr global i64 0, align 8 // CHECK: @llvm.global_ctors = appending global [4 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* @__cxx_global_var_init.1, i8* null }, { i32, void ()*, i8* } { i32 65535, void ()* @__cxx_global_var_init.2, i8* null }, { i32, void ()*, i8* } { i32 65535, void ()* @__cxx_global_var_init.4, 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 65535, void ()* @__finalize__ZN5test12t2E, i8* null }, { i32, void ()*, i8* } { i32 65535, void ()* @__finalize__ZN5test21AIvE8instanceE, i8* null }, { i32, void ()*, i8* } { i32 65535, void ()* @__finalize__ZN5test12t1IiEE, i8* null }, { i32, void ()*, i8* } { i32 65535, void ()* @_GLOBAL__D_a, i8* null }] @@ -58,7 +63,7 @@ // CHECK: ret void // CHECK: } -// CHECK: define internal void @__dtor__ZN5test12t0E() [[ATTR:#[0-9]+]] { +// CHECK: define internal void @__dtor__ZN5test12t0E() [[ATTR:#[0-9]+]] !associated ![[ASSOC4:[0-9]+]] { // CHECK: entry: // CHECK: call void @_ZN5test15Test1D1Ev(%"struct.test1::Test1"* @_ZN5test12t0E) // CHECK: ret void @@ -100,7 +105,7 @@ // CHECK: ret void // CHECK: } -// CHECK: define internal void @__dtor__ZN5test12t2E() [[ATTR:#[0-9]+]] { +// CHECK: define internal void @__dtor__ZN5test12t2E() [[ATTR:#[0-9]+]] !associated ![[ASSOC5:[0-9]+]] { // CHECK: entry: // CHECK: call void @_ZN5test15Test1D1Ev(%"struct.test1::Test1"* @_ZN5test12t2E) // CHECK: ret void @@ -136,7 +141,7 @@ // CHECK: ret void // CHECK: } -// CHECK: define internal void @__dtor__ZN5test21AIvE8instanceE() [[ATTR:#[0-9]+]] { +// CHECK: define internal void @__dtor__ZN5test21AIvE8instanceE() [[ATTR:#[0-9]+]] !associated ![[ASSOC6:[0-9]+]] { // CHECK: entry: // CHECK: call void @_ZN5test21AIvED1Ev(%"struct.test2::A"* @_ZN5test21AIvE8instanceE) // CHECK: ret void @@ -163,7 +168,7 @@ // CHECK: ret void // CHECK: } -// CHECK: define internal void @__dtor__ZN5test21AIiE8instanceE() [[ATTR:#[0-9]+]] { +// CHECK: define internal void @__dtor__ZN5test21AIiE8instanceE() [[ATTR:#[0-9]+]] !associated ![[ASSOC4:[0-9]+]] { // CHECK: entry: // CHECK: call void @_ZN5test21AIiED1Ev(%"struct.test2::A.0"* @_ZN5test21AIiE8instanceE) // CHECK: ret void @@ -200,7 +205,7 @@ // CHECK: ret void // CHECK: } -// CHECK: define internal void @__dtor__ZN5test12t1IiEE() [[ATTR:#[0-9]+]] { +// CHECK: define internal void @__dtor__ZN5test12t1IiEE() [[ATTR:#[0-9]+]] !associated ![[ASSOC7:[0-9]+]] { // CHECK: entry: // CHECK: call void @_ZN5test15Test1D1Ev(%"struct.test1::Test1"* @_ZN5test12t1IiEE) // CHECK: ret void @@ -233,3 +238,12 @@ // CHECK: call void @__finalize__ZN5test12t0E() // CHECK: ret void // CHECK: } + +// CHECK: ![[ASSOC0]] = !{void ()* @{{_GLOBAL__sub_I__|_GLOBAL__D_a}}, void ()* @{{_GLOBAL__sub_I__|_GLOBAL__D_a}}} +// CHECK: ![[ASSOC1]] = !{void ()* @{{__cxx_global_var_init.1|__finalize__ZN5test12t2E}}, void ()* @{{__cxx_global_var_init.1|__finalize__ZN5test12t2E}}} +// CHECK: ![[ASSOC2]] = !{void ()* @{{__cxx_global_var_init.4|__finalize__ZN5test12t1IiEE}}, void ()* @{{__cxx_global_var_init.4|__finalize__ZN5test12t1IiEE}}} +// CHECK: ![[ASSOC3]] = !{void ()* @{{__finalize__ZN5test21AIvE8instanceE|__cxx_global_var_init.2}}, void ()* @{{__finalize__ZN5test21AIvE8instanceE|__cxx_global_var_init.2}}} +// CHECK: ![[ASSOC4]] = !{void ()* @_GLOBAL__D_a} +// CHECK: ![[ASSOC5]] = !{void ()* @__finalize__ZN5test12t2E} +// CHECK: ![[ASSOC6]] = !{void ()* @__finalize__ZN5test21AIvE8instanceE} +// CHECK: ![[ASSOC7]] = !{void ()* @__finalize__ZN5test12t1IiEE} diff --git a/clang/test/CodeGenCXX/aix-static-init.cpp b/clang/test/CodeGenCXX/aix-static-init.cpp --- a/clang/test/CodeGenCXX/aix-static-init.cpp +++ b/clang/test/CodeGenCXX/aix-static-init.cpp @@ -38,6 +38,10 @@ } } // namespace test4 +// CHECK: @_ZN5test12t1E = global %"struct.test1::Test1" zeroinitializer, align {{[0-9]+}}, !associated ![[ASSOC0:[0-9]+]] +// CHECK: @_ZN5test12t2E = global %"struct.test1::Test1" zeroinitializer, align {{[0-9]+}}, !associated ![[ASSOC0:[0-9]+]] +// CHECK: @_ZN5test21xE = global i32 0, align {{[0-9]+}}, !associated ![[ASSOC1:[0-9]+]] +// CHECK: @_ZN5test31tE = global %"struct.test3::Test3" undef, align {{[0-9]+}}, !associated ![[ASSOC0:[0-9]+]] // CHECK: @_ZGVZN5test41fEvE11staticLocal = internal global i64 0, align 8 // 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 [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* @_GLOBAL__D_a, i8* null }] @@ -49,7 +53,7 @@ // CHECK: ret void // CHECK: } -// CHECK: define internal void @__dtor__ZN5test12t1E() [[ATTR:#[0-9]+]] { +// CHECK: define internal void @__dtor__ZN5test12t1E() [[ATTR:#[0-9]+]] !associated ![[ASSOC2:[0-9]+]] { // CHECK: entry: // CHECK: call void @_ZN5test15Test1D1Ev(%"struct.test1::Test1"* @_ZN5test12t1E) // CHECK: ret void @@ -80,7 +84,7 @@ // CHECK: ret void // CHECK: } -// CHECK: define internal void @__dtor__ZN5test12t2E() [[ATTR:#[0-9]+]] { +// CHECK: define internal void @__dtor__ZN5test12t2E() [[ATTR:#[0-9]+]] !associated ![[ASSOC2:[0-9]+]] { // CHECK: entry: // CHECK: call void @_ZN5test15Test1D1Ev(%"struct.test1::Test1"* @_ZN5test12t2E) // CHECK: ret void @@ -114,7 +118,7 @@ // CHECK: ret void // CHECK: } -// CHECK: define internal void @__dtor__ZN5test31tE() [[ATTR:#[0-9]+]] { +// CHECK: define internal void @__dtor__ZN5test31tE() [[ATTR:#[0-9]+]] !associated ![[ASSOC2:[0-9]+]] { // CHECK: entry: // CHECK: call void @_ZN5test35Test3D1Ev(%"struct.test3::Test3"* @_ZN5test31tE) // CHECK: ret void @@ -155,7 +159,7 @@ // CHECK: ret void // CHECK: } -// CHECK: define internal void @__dtor__ZZN5test41fEvE11staticLocal() [[ATTR:#[0-9]+]] { +// CHECK: define internal void @__dtor__ZZN5test41fEvE11staticLocal() [[ATTR:#[0-9]+]] !associated ![[ASSOC2:[0-9]+]] { // CHECK: entry: // CHECK: call void @_ZN5test45Test4D1Ev(%"struct.test4::Test4"* @_ZZN5test41fEvE11staticLocal) // CHECK: ret void @@ -192,3 +196,7 @@ // CHECK: call void @__finalize__ZN5test12t1E() // CHECK: ret void // CHECK: } + +// CHECK: ![[ASSOC0]] = !{void ()* @{{_GLOBAL__sub_I__|_GLOBAL__D_a}}, void ()* @{{_GLOBAL__sub_I__|_GLOBAL__D_a}}} +// CHECK: ![[ASSOC1]] = !{void ()* @_GLOBAL__sub_I__} +// CHECK: ![[ASSOC2]] = !{void ()* @_GLOBAL__D_a} diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -7040,6 +7040,10 @@ @b = internal global i32 2, comdat $a, section "abc", !associated !0 !0 = !{i32* @a} +On XCOFF target, the ``associated`` metadata indicates connection among static +variables (static global variable, static class member etc.) and static init/ +term functions. This metadata lowers to ``.ref`` assembler pseudo-operation +which prevents discarding of the functions in linker GC. '``prof``' Metadata ^^^^^^^^^^^^^^^^^^^