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,7 +21,6 @@ #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; @@ -365,12 +364,9 @@ llvm::Function *CodeGenModule::CreateGlobalInitOrCleanUpFunction( llvm::FunctionType *FTy, const Twine &Name, const CGFunctionInfo &FI, - SourceLocation Loc, bool TLS, bool IsExternalLinkage) { + SourceLocation Loc, bool TLS) { llvm::Function *Fn = llvm::Function::Create( - FTy, - IsExternalLinkage ? llvm::GlobalValue::ExternalLinkage - : llvm::GlobalValue::InternalLinkage, - Name, &getModule()); + FTy, llvm::GlobalValue::InternalLinkage, Name, &getModule()); if (!getLangOpts().AppleKext && !TLS) { // Set the section if needed. @@ -378,8 +374,7 @@ Fn->setSection(Section); } - if (Fn->hasInternalLinkage()) - SetInternalFunctionAttributes(GlobalDecl(), Fn, FI); + SetInternalFunctionAttributes(GlobalDecl(), Fn, FI); Fn->setCallingConv(getRuntimeCC()); @@ -589,22 +584,10 @@ 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(); + const bool UseSinitAndSterm = getCXXABI().useSinitAndSterm(); // Create our global prioritized initialization function. if (!PrioritizedCXXGlobalInits.empty()) { assert(!UseSinitAndSterm && "Prioritized sinit and sterm functions are not" @@ -644,24 +627,12 @@ 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); - } - + // 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 = CreateGlobalInitOrCleanUpFunction( - FTy, FuncName, FI, SourceLocation(), false /* TLS */, - IsExternalLinkage); + FTy, llvm::Twine("_GLOBAL__sub_I_", getTransformedFileName(getModule())), + FI); CodeGenFunction(*this).GenerateCXXGlobalInitFunc(Fn, CXXGlobalInits); AddGlobalCtor(Fn); @@ -695,25 +666,8 @@ const CGFunctionInfo &FI = getTypes().arrangeNullaryFunction(); // 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); - } + llvm::Function *Fn = + CreateGlobalInitOrCleanUpFunction(FTy, "_GLOBAL__D_a", FI); CodeGenFunction(*this).GenerateCXXGlobalCleanUpFunc( Fn, CXXGlobalDtorsOrStermFinalizers); 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 @@ -396,10 +396,6 @@ /// 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; @@ -819,8 +815,7 @@ llvm::Function *CreateGlobalInitOrCleanUpFunction( llvm::FunctionType *ty, const Twine &name, const CGFunctionInfo &FI, - SourceLocation Loc = SourceLocation(), bool TLS = false, - bool IsExternalLinkage = false); + SourceLocation Loc = SourceLocation(), bool TLS = 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 @@ -1059,6 +1054,12 @@ DtorFn.getCallee(), nullptr); } + /// Add an sterm finalizer to its own llvm.global_dtors entry. + void AddCXXStermFinalizerToGlobalDtor(llvm::Function *StermFinalizer, + int Priority) { + AddGlobalDtor(StermFinalizer, Priority); + } + /// 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 --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -4597,5 +4597,16 @@ CGF.FinishFunction(); - CGM.AddCXXStermFinalizerEntry(StermFinalizer); + assert(!D.getAttr() && + "Prioritized sinit and sterm functions are not yet supported."); + + 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 + CGM.AddCXXStermFinalizerEntry(StermFinalizer); } 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 @@ -40,13 +40,13 @@ // CHECK: ret void, !dbg ![[DBGVAR24]] // CHECK: } -// CHECK: define void @__sinit80000000_clang_c3236cbaa79f2bae3a15e6379a05f625() [[ATTR:#[0-9]+]] !dbg ![[DBGVAR25:[0-9]+]] { +// CHECK: define internal void @_GLOBAL__sub_I__() [[ATTR:#[0-9]+]] !dbg ![[DBGVAR25:[0-9]+]] { // CHECK: entry: // CHECK: call void @__cxx_global_var_init(), !dbg ![[DBGVAR26:[0-9]+]] // CHECK: ret void // CHECK: } -// CHECK: define void @__sterm80000000_clang_c3236cbaa79f2bae3a15e6379a05f625() [[ATTR:#[0-9]+]] !dbg ![[DBGVAR27:[0-9]+]] { +// CHECK: define internal void @_GLOBAL__D_a() [[ATTR:#[0-9]+]] !dbg ![[DBGVAR27:[0-9]+]] { // CHECK: entry: // CHECK: call void @__finalize_v(), !dbg ![[DBGVAR28:[0-9]+]] // CHECK: ret void @@ -58,7 +58,7 @@ // 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]+}}) // CHECK: ![[DBGVAR24]] = !DILocation(line: 14, column: 3, scope: ![[DBGVAR22]]) -// CHECK: ![[DBGVAR25]] = distinct !DISubprogram(linkageName: "__sinit80000000_clang_c3236cbaa79f2bae3a15e6379a05f625", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, type: !{{[0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: !{{[0-9]+}}, retainedNodes: !{{[0-9]+}}) +// CHECK: ![[DBGVAR25]] = distinct !DISubprogram(linkageName: "_GLOBAL__sub_I__", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, type: !{{[0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !{{[0-9]+}}, retainedNodes: !{{[0-9]+}}) // CHECK: ![[DBGVAR26]] = !DILocation(line: 0, scope: ![[DBGVAR25]]) -// CHECK: ![[DBGVAR27]] = distinct !DISubprogram(linkageName: "__sterm80000000_clang_c3236cbaa79f2bae3a15e6379a05f625", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, type: !{{[0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagDefinition, unit: !{{[0-9]+}}, retainedNodes: !{{[0-9]+}}) +// CHECK: ![[DBGVAR27]] = distinct !DISubprogram(linkageName: "_GLOBAL__D_a", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, type: !{{[0-9]+}}, flags: DIFlagArtificial, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !{{[0-9]+}}, retainedNodes: !{{[0-9]+}}) // CHECK: ![[DBGVAR28]] = !DILocation(line: 0, scope: ![[DBGVAR27]]) 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 new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/aix-static-init-temp-spec-and-inline-var.cpp @@ -0,0 +1,232 @@ +// RUN: %clang_cc1 -triple powerpc-ibm-aix-xcoff -S -emit-llvm -x c++ \ +// RUN: -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: -std=c++2a < %s | \ +// RUN: FileCheck --check-prefixes=CHECK,CHECK64 %s + +namespace test1 { +struct Test1 { + Test1(int) {} + ~Test1() {} +}; + +Test1 t0 = 2; + +template +Test1 t1 = 2; + +inline Test1 t2 = 2; + +void foo() { + (void)&t1; +} +} // namespace test1 + +namespace test2 { +template +struct A { + A() {} + ~A() {} + static A instance; +}; + +template +A A::instance; +template A<> A<>::instance; + +A &bar() { + A *a = new A; + return *a; +} +template <> +A A::instance = bar(); +} // namespace test2 + +// 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 }] + +// CHECK: define internal void @__cxx_global_var_init() [[ATTR:#[0-9]+]] { +// CHECK: entry: +// CHECK32: call void @_ZN5test15Test1C1Ei(%"struct.test1::Test1"* @_ZN5test12t0E, i32 2) +// CHECK64: call void @_ZN5test15Test1C1Ei(%"struct.test1::Test1"* @_ZN5test12t0E, i32 signext 2) +// CHECK: %0 = call i32 @atexit(void ()* @__dtor__ZN5test12t0E) +// CHECK: ret void +// CHECK: } + +// CHECK: define internal void @__dtor__ZN5test12t0E() [[ATTR:#[0-9]+]] { +// CHECK: entry: +// CHECK: call void @_ZN5test15Test1D1Ev(%"struct.test1::Test1"* @_ZN5test12t0E) +// CHECK: ret void +// CHECK: } + +// CHECK: define internal void @__finalize__ZN5test12t0E() [[ATTR:#[0-9]+]] { +// CHECK: entry: +// CHECK: %0 = call i32 @unatexit(void ()* @__dtor__ZN5test12t0E) +// 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__ZN5test12t0E() +// CHECK: br label %destruct.end + +// CHECK: destruct.end: +// CHECK: ret void +// CHECK: } + +// CHECK: define internal void @__cxx_global_var_init.1() [[ATTR:#[0-9]+]] { +// CHECK: entry: +// CHECK: %0 = load atomic i8, i8* bitcast (i64* @_ZGVN5test12t2E 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* @_ZGVN5test12t2E) +// CHECK: %tobool = icmp ne i32 %1, 0 +// CHECK: br i1 %tobool, label %init, label %init.end + +// CHECK: init: +// CHECK32: call void @_ZN5test15Test1C1Ei(%"struct.test1::Test1"* @_ZN5test12t2E, i32 2) +// CHECK64: call void @_ZN5test15Test1C1Ei(%"struct.test1::Test1"* @_ZN5test12t2E, i32 signext 2) +// CHECK: %2 = call i32 @atexit(void ()* @__dtor__ZN5test12t2E) +// CHECK: call void @__cxa_guard_release(i64* @_ZGVN5test12t2E) +// CHECK: br label %init.end + +// CHECK: init.end: +// 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: +// CHECK: %0 = load i8, i8* bitcast (i64* @_ZGVN5test21AIvE8instanceE to i8*) +// CHECK: %guard.uninitialized = icmp eq i8 %0, 0 +// CHECK: br i1 %guard.uninitialized, label %init.check, label %init.end + +// CHECK: init.check: +// CHECK: call void @_ZN5test21AIvEC1Ev(%"struct.test2::A"* @_ZN5test21AIvE8instanceE) +// CHECK: %1 = call i32 @atexit(void ()* @__dtor__ZN5test21AIvE8instanceE) +// CHECK: store i64 1, i64* @_ZGVN5test21AIvE8instanceE +// CHECK: br label %init.end + +// CHECK: init.end: +// CHECK: ret void +// CHECK: } + +// CHECK: define internal void @__dtor__ZN5test21AIvE8instanceE() [[ATTR:#[0-9]+]] { +// CHECK: entry: +// CHECK: call void @_ZN5test21AIvED1Ev(%"struct.test2::A"* @_ZN5test21AIvE8instanceE) +// CHECK: ret void +// CHECK: } + +// CHECK: define internal void @__finalize__ZN5test21AIvE8instanceE() [[ATTR:#[0-9]+]] { +// CHECK: entry: +// CHECK: %0 = call i32 @unatexit(void ()* @__dtor__ZN5test21AIvE8instanceE) +// 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__ZN5test21AIvE8instanceE() +// CHECK: br label %destruct.end + +// CHECK: destruct.end: +// CHECK: ret void +// CHECK: } + +// CHECK: define internal void @__cxx_global_var_init.3() [[ATTR:#[0-9]+]] { +// CHECK: entry: +// CHECK: %call = call nonnull align 1 dereferenceable(1) %"struct.test2::A.0"* @_ZN5test23barEv() +// CHECK: %0 = call i32 @atexit(void ()* @__dtor__ZN5test21AIiE8instanceE) +// CHECK: ret void +// CHECK: } + +// CHECK: define internal void @__dtor__ZN5test21AIiE8instanceE() [[ATTR:#[0-9]+]] { +// CHECK: entry: +// CHECK: call void @_ZN5test21AIiED1Ev(%"struct.test2::A.0"* @_ZN5test21AIiE8instanceE) +// CHECK: ret void +// CHECK: } + +// CHECK: define internal void @__finalize__ZN5test21AIiE8instanceE() [[ATTR:#[0-9]+]] { +// CHECK: entry: +// CHECK: %0 = call i32 @unatexit(void ()* @__dtor__ZN5test21AIiE8instanceE) +// 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__ZN5test21AIiE8instanceE() +// CHECK: br label %destruct.end + +// CHECK: destruct.end: +// CHECK: ret void +// CHECK: } + +// CHECK: define internal void @__cxx_global_var_init.4() [[ATTR:#[0-9]+]] { +// CHECK: entry: +// CHECK: %0 = load i8, i8* bitcast (i64* @_ZGVN5test12t1IiEE to i8*) +// CHECK: %guard.uninitialized = icmp eq i8 %0, 0 +// CHECK: br i1 %guard.uninitialized, label %init.check, label %init.end + +// CHECK: init.check: +// CHECK32: call void @_ZN5test15Test1C1Ei(%"struct.test1::Test1"* @_ZN5test12t1IiEE, i32 2) +// CHECK64: call void @_ZN5test15Test1C1Ei(%"struct.test1::Test1"* @_ZN5test12t1IiEE, i32 signext 2) +// CHECK: %1 = call i32 @atexit(void ()* @__dtor__ZN5test12t1IiEE) +// CHECK: store i64 1, i64* @_ZGVN5test12t1IiEE +// CHECK: br label %init.end + +// CHECK: init.end: +// CHECK: ret void +// CHECK: } + +// CHECK: define internal void @__dtor__ZN5test12t1IiEE() [[ATTR:#[0-9]+]] { +// CHECK: entry: +// CHECK: call void @_ZN5test15Test1D1Ev(%"struct.test1::Test1"* @_ZN5test12t1IiEE) +// CHECK: ret void +// CHECK: } + +// CHECK: define internal void @__finalize__ZN5test12t1IiEE() [[ATTR:#[0-9]+]] { +// CHECK: entry: +// CHECK: %0 = call i32 @unatexit(void ()* @__dtor__ZN5test12t1IiEE) +// 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__ZN5test12t1IiEE() +// CHECK: br label %destruct.end + +// CHECK: destruct.end: +// CHECK: ret void +// CHECK: } + +// CHECK: define internal void @_GLOBAL__sub_I__() [[ATTR:#[0-9]+]] { +// CHECK: entry: +// CHECK: call void @__cxx_global_var_init() +// CHECK: call void @__cxx_global_var_init.3() +// CHECK: ret void +// CHECK: } + +// CHECK: define internal void @_GLOBAL__D_a() [[ATTR:#[0-9]+]] { +// CHECK: entry: +// CHECK: call void @__finalize__ZN5test21AIiE8instanceE() +// CHECK: call void @__finalize__ZN5test12t0E() +// CHECK: ret void +// CHECK: } 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,8 +38,8 @@ } } // 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: @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 }] // CHECK: define internal void @__cxx_global_var_init() [[ATTR:#[0-9]+]] { // CHECK: entry: @@ -174,7 +174,7 @@ // CHECK: ret void // CHECK: } -// CHECK: define void @__sinit80000000_clang_1145401da454a7baad10bfe313c46638() [[ATTR:#[0-9]+]] { +// CHECK: define internal void @_GLOBAL__sub_I__() [[ATTR:#[0-9]+]] { // CHECK: entry: // CHECK: call void @__cxx_global_var_init() // CHECK: call void @__cxx_global_var_init.1() @@ -183,7 +183,7 @@ // CHECK: ret void // CHECK: } -// CHECK: define void @__sterm80000000_clang_1145401da454a7baad10bfe313c46638() [[ATTR:#[0-9]+]] { +// CHECK: define internal void @_GLOBAL__D_a() [[ATTR:#[0-9]+]] { // CHECK: entry: // CHECK: call void @__finalize__ZZN5test41fEvE11staticLocal() // CHECK: call void @__finalize__ZN5test31tE() diff --git a/clang/unittests/CodeGen/IncrementalProcessingTest.cpp b/clang/unittests/CodeGen/IncrementalProcessingTest.cpp --- a/clang/unittests/CodeGen/IncrementalProcessingTest.cpp +++ b/clang/unittests/CodeGen/IncrementalProcessingTest.cpp @@ -159,11 +159,6 @@ // First code should not end up in second module: ASSERT_FALSE(M[2]->getFunction("funcForProg1")); - // TODO: Remove this after the static initialization frontend implementation - // is recovered on AIX. - if (compiler.getTarget().getTriple().isOSAIX()) - return; - // Make sure global inits exist and are unique: const Function* GlobalInit1 = getGlobalInit(*M[1]); ASSERT_TRUE(GlobalInit1); diff --git a/llvm/include/llvm/CodeGen/AsmPrinter.h b/llvm/include/llvm/CodeGen/AsmPrinter.h --- a/llvm/include/llvm/CodeGen/AsmPrinter.h +++ b/llvm/include/llvm/CodeGen/AsmPrinter.h @@ -369,6 +369,32 @@ /// so, emit it and return true, otherwise do nothing and return false. bool emitSpecialLLVMGlobal(const GlobalVariable *GV); + /// `llvm.global_ctors` and `llvm.global_dtors` are arrays of Structor + /// structs. + /// + /// Priority - init priority + /// Func - global initialization or global clean-up function + /// ComdatKey - associated data + struct Structor { + int Priority = 0; + Constant *Func = nullptr; + GlobalValue *ComdatKey = nullptr; + + Structor() = default; + }; + + /// This method gathers an array of Structors and then sorts them out by + /// Priority. + /// @param List The initializer of `llvm.global_ctors` or `llvm.global_dtors` + /// array. + /// @param[out] Structors Sorted Structor structs by Priority. + void preprocessXXStructorList(const DataLayout &DL, const Constant *List, + SmallVector &Structors); + + /// This method emits `llvm.global_ctors` or `llvm.global_dtors` list. + virtual void emitXXStructorList(const DataLayout &DL, const Constant *List, + bool IsCtor); + /// Emit an alignment directive to the specified power of two boundary. If a /// global value is specified, and if that global has an explicit alignment /// requested, it will override the alignment request if required for @@ -713,8 +739,6 @@ void emitModuleIdents(Module &M); /// Emit bytes for llvm.commandline metadata. void emitModuleCommandLines(Module &M); - void emitXXStructorList(const DataLayout &DL, const Constant *List, - bool isCtor); GCMetadataPrinter *GetOrCreateGCPrinter(GCStrategy &S); /// Emit GlobalAlias or GlobalIFunc. diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -2094,47 +2094,50 @@ } } -namespace { - -struct Structor { - int Priority = 0; - Constant *Func = nullptr; - GlobalValue *ComdatKey = nullptr; - - Structor() = default; -}; - -} // end anonymous namespace - -/// EmitXXStructorList - Emit the ctor or dtor list taking into account the init -/// priority. -void AsmPrinter::emitXXStructorList(const DataLayout &DL, const Constant *List, - bool isCtor) { - // Should be an array of '{ i32, void ()*, i8* }' structs. The first value is the - // init priority. - if (!isa(List)) return; +void AsmPrinter::preprocessXXStructorList(const DataLayout &DL, + const Constant *List, + SmallVector &Structors) { + // Should be an array of '{ i32, void ()*, i8* }' structs. The first value is + // the init priority. + if (!isa(List)) + return; // Gather the structors in a form that's convenient for sorting by priority. - SmallVector Structors; for (Value *O : cast(List)->operands()) { auto *CS = cast(O); if (CS->getOperand(1)->isNullValue()) - break; // Found a null terminator, skip the rest. + break; // Found a null terminator, skip the rest. ConstantInt *Priority = dyn_cast(CS->getOperand(0)); - if (!Priority) continue; // Malformed. + if (!Priority) + continue; // Malformed. Structors.push_back(Structor()); Structor &S = Structors.back(); S.Priority = Priority->getLimitedValue(65535); S.Func = CS->getOperand(1); - if (!CS->getOperand(2)->isNullValue()) + if (!CS->getOperand(2)->isNullValue()) { + if (TM.getTargetTriple().isOSAIX()) + llvm::report_fatal_error( + "associated data of XXStructor list is not yet supported on AIX"); S.ComdatKey = dyn_cast(CS->getOperand(2)->stripPointerCasts()); + } } // Emit the function pointers in the target-specific order llvm::stable_sort(Structors, [](const Structor &L, const Structor &R) { return L.Priority < R.Priority; }); +} + +/// EmitXXStructorList - Emit the ctor or dtor list taking into account the init +/// priority. +void AsmPrinter::emitXXStructorList(const DataLayout &DL, const Constant *List, + bool IsCtor) { + SmallVector Structors; + preprocessXXStructorList(DL, List, Structors); + if (Structors.empty()) + return; + const Align Align = DL.getPointerPrefAlignment(); for (Structor &S : Structors) { const TargetLoweringObjectFile &Obj = getObjFileLowering(); @@ -2150,8 +2153,9 @@ KeySym = getSymbol(GV); } + MCSection *OutputSection = - (isCtor ? Obj.getStaticCtorSection(S.Priority, KeySym) + (IsCtor ? Obj.getStaticCtorSection(S.Priority, KeySym) : Obj.getStaticDtorSection(S.Priority, KeySym)); OutStreamer->SwitchSection(OutputSection); if (OutStreamer->getCurrentSection() != OutStreamer->getPreviousSection()) diff --git a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp --- a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp +++ b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp @@ -2122,13 +2122,13 @@ } MCSection *TargetLoweringObjectFileXCOFF::getStaticCtorSection( - unsigned Priority, const MCSymbol *KeySym) const { - report_fatal_error("XCOFF ctor section not yet implemented."); + unsigned Priority, const MCSymbol *KeySym) const { + report_fatal_error("no static constructor section on AIX"); } MCSection *TargetLoweringObjectFileXCOFF::getStaticDtorSection( - unsigned Priority, const MCSymbol *KeySym) const { - report_fatal_error("XCOFF dtor section not yet implemented."); + unsigned Priority, const MCSymbol *KeySym) const { + report_fatal_error("no static destructor section on AIX"); } const MCExpr *TargetLoweringObjectFileXCOFF::lowerRelativeReference( diff --git a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp --- a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp +++ b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp @@ -67,6 +67,7 @@ #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Target/TargetMachine.h" +#include "llvm/Transforms/Utils/ModuleUtils.h" #include #include #include @@ -153,6 +154,9 @@ /// linkage for them in AIX. SmallPtrSet ExtSymSDNodeSymbols; + /// A unique trailing identifier as a part of sinit/sterm functions. + std::string GlobalUniqueModuleId; + static void ValidateGV(const GlobalVariable *GV); // Record a list of GlobalAlias associated with a GlobalObject. // This is used for AIX's extra-label-at-definition aliasing strategy. @@ -171,6 +175,9 @@ bool doInitialization(Module &M) override; + void emitXXStructorList(const DataLayout &DL, const Constant *List, + bool IsCtor) override; + void SetupMachineFunction(MachineFunction &MF) override; void emitGlobalVariable(const GlobalVariable *GV) override; @@ -1678,11 +1685,6 @@ static bool isSpecialLLVMGlobalArrayToSkip(const GlobalVariable *GV) { return GV->hasAppendingLinkage() && StringSwitch(GV->getName()) - // TODO: Update the handling of global arrays for static init when - // we support the ".ref" directive. - // Otherwise, we can skip these arrays, because the AIX linker - // collects static init functions simply based on their name. - .Cases("llvm.global_ctors", "llvm.global_dtors", true) // TODO: Linker could still eliminate the GV if we just skip // handling llvm.used array. Skipping them for now until we or the // AIX OS team come up with a good solution. @@ -1692,8 +1694,15 @@ .Default(false); } +static bool isSpecialLLVMGlobalArrayForStaticInit(const GlobalVariable *GV) { + return StringSwitch(GV->getName()) + .Cases("llvm.global_ctors", "llvm.global_dtors", true) + .Default(false); +} + void PPCAIXAsmPrinter::emitGlobalVariable(const GlobalVariable *GV) { - if (isSpecialLLVMGlobalArrayToSkip(GV)) + // Special LLVM global arrays have been handled at the initialization. + if (isSpecialLLVMGlobalArrayToSkip(GV) || isSpecialLLVMGlobalArrayForStaticInit(GV)) return; assert(!GV->getName().startswith("llvm.") && @@ -1849,6 +1858,25 @@ for (const auto &G : M.globals()) { if (isSpecialLLVMGlobalArrayToSkip(&G)) continue; + + if (isSpecialLLVMGlobalArrayForStaticInit(&G)) { + // Generate a unique module id which is a part of sinit and sterm function + // names. + if (GlobalUniqueModuleId.empty()) { + GlobalUniqueModuleId = getUniqueModuleId(&M); + // 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); + } + + emitSpecialLLVMGlobal(&G); + continue; + } + setCsectAlignment(&G); } @@ -1916,6 +1944,28 @@ return Ret; } +void PPCAIXAsmPrinter::emitXXStructorList(const DataLayout &DL, + const Constant *List, bool IsCtor) { + SmallVector Structors; + preprocessXXStructorList(DL, List, Structors); + if (Structors.empty()) + return; + + unsigned Index = 0; + for (Structor &S : Structors) { + if (S.Priority != 65535) + report_fatal_error( + "prioritized sinit and sterm functions are not yet supported on AIX"); + + llvm::GlobalAlias::create( + GlobalValue::ExternalLinkage, + (IsCtor ? llvm::Twine("__sinit") : llvm::Twine("__sterm")) + + llvm::Twine("80000000_clang_", GlobalUniqueModuleId) + + llvm::Twine("_", llvm::utostr(Index++)), + cast(S.Func)); + } +} + /// createPPCAsmPrinterPass - Returns a pass that prints the PPC assembly code /// for a MachineFunction to the given output stream, in a format that the /// Darwin assembler can deal with. diff --git a/llvm/test/CodeGen/PowerPC/aix-static-init-default-priority.ll b/llvm/test/CodeGen/PowerPC/aix-static-init-default-priority.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/PowerPC/aix-static-init-default-priority.ll @@ -0,0 +1,60 @@ +; RUN: llc -mtriple powerpc-ibm-aix-xcoff < %s | FileCheck %s +; RUN: llc -mtriple powerpc64-ibm-aix-xcoff < %s | FileCheck %s + +@llvm.global_ctors = appending global [2 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* @init1, i8* null }, { i32, void ()*, i8* } { i32 65535, void ()* @init2, i8* null }] +@llvm.global_dtors = appending global [2 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* @destruct1, i8* null }, { i32, void ()*, i8* } { i32 65535, void ()* @destruct2, i8* null }] + +define i32 @extFunc() { +entry: + ret i32 3 +} + +define internal void @init1() { + ret void +} + +define internal void @destruct1() { + ret void +} + +define internal void @init2() { + ret void +} + +define internal void @destruct2() { + ret void +} + +; CHECK: .lglobl init1[DS] +; CHECK: .lglobl .init1 +; CHECK: .csect init1[DS] +; CHECK: __sinit80000000_clang_ac404299654d2af7eae71e75c17f7c9b_0: # @init1 +; CHECK: .init1: +; CHECK: .__sinit80000000_clang_ac404299654d2af7eae71e75c17f7c9b_0: +; CHECK: .lglobl destruct1[DS] +; CHECK: .lglobl .destruct1 +; CHECK: .csect destruct1[DS] +; CHECK: __sterm80000000_clang_ac404299654d2af7eae71e75c17f7c9b_0: # @destruct1 +; CHECK: .destruct1: +; CHECK: .__sterm80000000_clang_ac404299654d2af7eae71e75c17f7c9b_0: +; CHECK: .lglobl init2[DS] +; CHECK: .lglobl .init2 +; CHECK: .csect init2[DS] +; CHECK: __sinit80000000_clang_ac404299654d2af7eae71e75c17f7c9b_1: # @init2 +; CHECK: .init2: +; CHECK: .__sinit80000000_clang_ac404299654d2af7eae71e75c17f7c9b_1: +; CHECK: .lglobl destruct2[DS] +; CHECK: .lglobl .destruct2 +; CHECK: .csect destruct2[DS] +; CHECK: __sterm80000000_clang_ac404299654d2af7eae71e75c17f7c9b_1: # @destruct2 +; CHECK: .destruct2: +; CHECK: .__sterm80000000_clang_ac404299654d2af7eae71e75c17f7c9b_1: + +; CHECK: .globl __sinit80000000_clang_ac404299654d2af7eae71e75c17f7c9b_0 +; CHECK: .globl .__sinit80000000_clang_ac404299654d2af7eae71e75c17f7c9b_0 +; CHECK: .globl __sinit80000000_clang_ac404299654d2af7eae71e75c17f7c9b_1 +; CHECK: .globl .__sinit80000000_clang_ac404299654d2af7eae71e75c17f7c9b_1 +; CHECK: .globl __sterm80000000_clang_ac404299654d2af7eae71e75c17f7c9b_0 +; CHECK: .globl .__sterm80000000_clang_ac404299654d2af7eae71e75c17f7c9b_0 +; CHECK: .globl __sterm80000000_clang_ac404299654d2af7eae71e75c17f7c9b_1 +; CHECK: .globl .__sterm80000000_clang_ac404299654d2af7eae71e75c17f7c9b_1 diff --git a/llvm/test/CodeGen/PowerPC/aix-static-init-key-object.ll b/llvm/test/CodeGen/PowerPC/aix-static-init-key-object.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/PowerPC/aix-static-init-key-object.ll @@ -0,0 +1,12 @@ +; RUN: not --crash llc -mtriple powerpc-ibm-aix-xcoff < %s 2>&1 | FileCheck %s +; RUN: not --crash llc -mtriple powerpc64-ibm-aix-xcoff < %s 2>&1 | FileCheck %s + +@v = global i8 0 + +@llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* @foo, i8* @v}] + +define void @foo() { + ret void +} + +; CHECK: LLVM ERROR: associated data of XXStructor list is not yet supported on AIX diff --git a/llvm/test/CodeGen/PowerPC/aix-static-init-no-unique-module-id.ll b/llvm/test/CodeGen/PowerPC/aix-static-init-no-unique-module-id.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/PowerPC/aix-static-init-no-unique-module-id.ll @@ -0,0 +1,10 @@ +; RUN: not --crash llc -mtriple powerpc-ibm-aix-xcoff %s 2>&1 | FileCheck %s +; RUN: not --crash llc -mtriple powerpc64-ibm-aix-xcoff %s 2>&1 | FileCheck %s + +@llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* @foo, i8* null }] + +define internal void @foo() { + ret void +} + +; CHECK: LLVM ERROR: cannot produce a unique identifier for this module based on strong external symbols diff --git a/llvm/test/CodeGen/PowerPC/aix-static-init-non-default-priority.ll b/llvm/test/CodeGen/PowerPC/aix-static-init-non-default-priority.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/PowerPC/aix-static-init-non-default-priority.ll @@ -0,0 +1,10 @@ +; RUN: not --crash llc -mtriple powerpc-ibm-aix-xcoff < %s 2>&1 | FileCheck %s +; RUN: not --crash llc -mtriple powerpc64-ibm-aix-xcoff < %s 2>&1 | FileCheck %s + +@llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 655, void ()* @foo, i8* null }] + +define void @foo() { + ret void +} + +; CHECK: LLVM ERROR: prioritized sinit and sterm functions are not yet supported