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, @@ -5222,6 +5223,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 @@ -20,7 +20,9 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/IR/Intrinsics.h" #include "llvm/IR/MDBuilder.h" +#include "llvm/Support/Format.h" #include "llvm/Support/Path.h" +#include "llvm/Transforms/Utils/ModuleUtils.h" using namespace clang; using namespace CodeGen; @@ -249,7 +251,7 @@ llvm::CallInst *call = CGF.Builder.CreateCall(dtor, addr); - // Make sure the call and the callee agree on calling convention. + // Make sure the call and the callee agree on calling convention. if (auto *dtorFn = dyn_cast( dtor.getCallee()->stripPointerCastsAndAliases())) call->setCallingConv(dtorFn->getCallingConv()); @@ -270,8 +272,12 @@ void CodeGenFunction::registerGlobalDtorWithAtExit(llvm::Constant *dtorStub) { // extern "C" int atexit(void (*f)(void)); + assert(cast(dtorStub)->getFunctionType() == + llvm::FunctionType::get(CGM.VoidTy, false) && + "atexit has wrong parameter type."); + llvm::FunctionType *atexitTy = - llvm::FunctionType::get(IntTy, dtorStub->getType(), false); + llvm::FunctionType::get(IntTy, dtorStub->getType(), false); llvm::FunctionCallee atexit = CGM.CreateRuntimeFunction(atexitTy, "atexit", llvm::AttributeList(), @@ -282,6 +288,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) && + "unatexit has wrong parameter type."); + + llvm::FunctionType *unatexitTy = + llvm::FunctionType::get(IntTy, {dtorStub->getType()}, /*isVarArg=*/false); + + llvm::FunctionCallee unatexit = + CGM.CreateRuntimeFunction(unatexitTy, "unatexit", llvm::AttributeList()); + + cast(unatexit.getCallee())->setDoesNotThrow(); + + return EmitNounwindRuntimeCall(unatexit, dtorStub); +} + void CodeGenFunction::EmitCXXGuardedInit(const VarDecl &D, llvm::GlobalVariable *DeclPtr, bool PerformInit) { @@ -533,6 +563,22 @@ CXXThreadLocals.clear(); } +static StringRef getTransformedFileName(llvm::Module &M, SmallString<128> &FileName) { + FileName = llvm::sys::path::filename(M.getName()); + + if (FileName.empty()) + FileName = ""; + + for (size_t i = 0; i < FileName.size(); ++i) { + // Replace everything that's not [a-zA-Z0-9._] with a _. This set happens + // to be the set of C preprocessing numbers. + if (!isPreprocessingNumberBody(FileName[i])) + FileName[i] = '_'; + } + + return FileName; +} + void CodeGenModule::EmitCXXGlobalInitFunc() { while (!CXXGlobalInits.empty() && !CXXGlobalInits.back()) @@ -541,11 +587,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()); @@ -577,22 +639,29 @@ PrioritizedCXXGlobalInits.clear(); } - // Include the filename in the symbol name. Including "sub_" matches gcc and - // makes sure these symbols appear lexicographically behind the symbols with - // priority emitted above. - SmallString<128> FileName = llvm::sys::path::filename(getModule().getName()); - if (FileName.empty()) - FileName = ""; + // Create our global initialization function. + if (CXXGlobalInits.empty()) + return; - for (size_t i = 0; i < FileName.size(); ++i) { - // Replace everything that's not [a-zA-Z0-9._] with a _. This set happens - // to be the set of C preprocessing numbers. - if (!isPreprocessingNumberBody(FileName[i])) - FileName[i] = '_'; + SmallString<128> FuncName; + if (UseSinitAndSterm) + FuncName = llvm::Twine("__sinit80000000_clang_", GlobalUniqueModuleId) + .toNullTerminatedStringRef(FuncName); + 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. + SmallString<128> Storage; + FuncName = llvm::Twine("_GLOBAL__sub_I_", + getTransformedFileName(getModule(), Storage)) + .toNullTerminatedStringRef(FuncName); } - llvm::Function *Fn = CreateGlobalInitOrDestructFunction( - FTy, llvm::Twine("_GLOBAL__sub_I_", FileName), FI); + llvm::Function *Fn = + CreateGlobalInitOrDestructFunction(FTy, FuncName, FI, SourceLocation()); + + if (getCXXABI().useSinitAndSterm()) + Fn->setLinkage(llvm::Function::ExternalLinkage); CodeGenFunction(*this).GenerateCXXGlobalInitFunc(Fn, CXXGlobalInits); AddGlobalCtor(Fn); @@ -618,19 +687,32 @@ CXXGlobalInits.clear(); } -void CodeGenModule::EmitCXXGlobalDtorFunc() { - if (CXXGlobalDtors.empty()) +void CodeGenModule::EmitCXXGlobalDestructFunc() { + if (CXXGlobalDtorsOrStermFinalizers.empty()) return; llvm::FunctionType *FTy = llvm::FunctionType::get(VoidTy, false); + const CGFunctionInfo &FI = getTypes().arrangeNullaryFunction(); // Create our global destructor function. - const CGFunctionInfo &FI = getTypes().arrangeNullaryFunction(); - llvm::Function *Fn = - CreateGlobalInitOrDestructFunction(FTy, "_GLOBAL__D_a", FI); + llvm::Function *Fn = nullptr; + if (getCXXABI().useSinitAndSterm()) { + assert(!GlobalUniqueModuleId.empty() && + "Invalid empty GlobalUniqueModuleId."); + + Fn = CreateGlobalInitOrDestructFunction( + FTy, llvm::Twine("__sterm80000000_clang_", GlobalUniqueModuleId), FI, + SourceLocation()); - CodeGenFunction(*this).GenerateCXXGlobalDtorsFunc(Fn, CXXGlobalDtors); + Fn->setLinkage(llvm::Function::ExternalLinkage); + } else { + Fn = CreateGlobalInitOrDestructFunction(FTy, "_GLOBAL__D_a", FI); + } + + CodeGenFunction(*this).GenerateCXXGlobalDestructFunc( + Fn, CXXGlobalDtorsOrStermFinalizers); AddGlobalDtor(Fn); + CXXGlobalDtorsOrStermFinalizers.clear(); } /// Emit the code necessary to initialize the given global variable. @@ -726,10 +808,10 @@ FinishFunction(); } -void CodeGenFunction::GenerateCXXGlobalDtorsFunc( +void CodeGenFunction::GenerateCXXGlobalDestructFunc( llvm::Function *Fn, const std::vector> &DtorsAndObjects) { + llvm::Constant *>> &DtorsOrStermFinalizers) { { auto NL = ApplyDebugLocation::CreateEmpty(*this); StartFunction(GlobalDecl(), getContext().VoidTy, Fn, @@ -738,12 +820,21 @@ auto AL = ApplyDebugLocation::CreateArtificial(*this); // Emit the dtors, in reverse order from construction. - for (unsigned i = 0, e = DtorsAndObjects.size(); i != e; ++i) { + 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()); 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 @@ -4171,6 +4171,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 @@ -4194,12 +4197,12 @@ ArrayRef CXXThreadLocals, ConstantAddress Guard = ConstantAddress::invalid()); - /// GenerateCXXGlobalDtorsFunc - Generates code for destroying global + /// GenerateCXXGlobalDestructFunc - Generates code for destroying global /// variables. - void GenerateCXXGlobalDtorsFunc( + void GenerateCXXGlobalDestructFunc( 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,12 @@ SmallVector PrioritizedCXXGlobalInits; /// Global destructor functions and arguments that need to run on termination. + /// Global destructor functions and arguments that need to run on termination; + /// When UseSinitAndSterm is set, it contains sterm finalizers functions + /// instead that need to run on unloading a shared library. std::vector< std::tuple> - CXXGlobalDtors; + CXXGlobalDtorsOrStermFinalizers; /// The complete set of modules that has been imported. llvm::SetVector ImportedModules; @@ -1045,8 +1052,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 a destructor to the C++ global destructor function. + void AddCXXDtorEntry(llvm::FunctionCallee DtorFn) { + CXXGlobalDtorsOrStermFinalizers.emplace_back(DtorFn.getFunctionType(), + DtorFn.getCallee(), nullptr); } /// Create or return a runtime function declaration with the specified type @@ -1446,8 +1459,8 @@ /// Emit the function that initializes C++ globals. void EmitCXXGlobalInitFunc(); - /// Emit the function that destroys C++ globals. - void EmitCXXGlobalDtorFunc(); + /// Emit the function that destructs C++ globals. + void EmitCXXGlobalDestructFunc(); /// 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(); + EmitCXXGlobalDestructFunc(); 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); }; } @@ -4432,6 +4438,68 @@ 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 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 a variable destruction function. + const CGFunctionInfo &FI = CGM.getTypes().arrangeNullaryFunction(); + llvm::Function *StermFinalizer = CGM.CreateGlobalInitOrDestructFunction( + 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 and the related __dtor function will be + // called to destruct the object. Otherwise, a non-zero value is returned. + llvm::Value *V = CGF.unregisterGlobalDtorWithUnAtExit(dtorStub); + + llvm::Value *NeedsDestruct = + CGF.Builder.CreateIsNull(V, "needsDestruct"); + + 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.EmitCXXGuardedInitBranch(NeedsDestruct, DestructCallBlock, EndBlock, + CodeGenFunction::GuardKind::VariableGuard, &D); + + 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.AddCXXDtorEntry(StermFinalizer); } 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 unsupported on AIX yet"); + 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 unsupported on AIX yet"); + 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 unsupported on AIX yet"); + 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 unsupported on AIX yet 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 unsupported on AIX yet 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 unsupported on AIX yet diff --git a/clang/test/CodeGen/static-init.cpp b/clang/test/CodeGen/static-init.cpp --- a/clang/test/CodeGen/static-init.cpp +++ b/clang/test/CodeGen/static-init.cpp @@ -1,12 +1,139 @@ -// RUN: not %clang_cc1 -triple powerpc-ibm-aix-xcoff -S -emit-llvm -x c++ %s \ -// RUN: -o /dev/null 2>&1 | FileCheck %s +// 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: not %clang_cc1 -triple powerpc64-ibm-aix-xcoff -S -emit-llvm -x c++ %s \ -// RUN: -o /dev/null 2>&1 | FileCheck %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 -struct test { - test(); - ~test(); -} t; +namespace test1 { + struct Test1 { + Test1(); + ~Test1(); + } t1, t2; +}; // namespace test1 -// CHECK: error in backend: Static initialization has not been implemented on XL ABI yet. +namespace test2 { + int foo() { return 3; } + int x = foo(); +}; // namespace test2 + +namespace test3 { + struct Test { + constexpr Test() {}; + ~Test() {}; + }; + + constinit Test t; +}; // namespace test3 + +// CHECK: @llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* @__sinit80000000_clang_510e898aa8d263cac999dd03eeed5b51, i8* null }] +// CHECK: @llvm.global_dtors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* @__sterm80000000_clang_510e898aa8d263cac999dd03eeed5b51, i8* null }] + +// CHECK: define internal void @__cxx_global_var_init() #0 { +// 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() #0 { +// 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() #0 { +// CHECK: entry: +// CHECK: %0 = call i32 @unatexit(void ()* @__dtor__ZN5test12t1E) +// CHECK: %needsDestruct = icmp eq i32 %0, 0 +// CHECK: br i1 %needsDestruct, 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() #0 { +// 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() #0 { +// CHECK: entry: +// CHECK: call void @_ZN5test15Test1D1Ev(%"struct.test1::Test1"* @_ZN5test12t2E) +// CHECK: ret void +// CHECK: } + +// CHECK: define internal void @__finalize__ZN5test12t2E() #0 { +// CHECK: entry: +// CHECK: %0 = call i32 @unatexit(void ()* @__dtor__ZN5test12t2E) +// CHECK: %needsDestruct = icmp eq i32 %0, 0 +// CHECK: br i1 %needsDestruct, 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() #0 { +// 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() #0 { +// CHECK: entry: +// CHECK: %0 = call i32 @atexit(void ()* @__dtor__ZN5test31tE) +// CHECK: ret void +// CHECK: } + +// CHECK: define internal void @__dtor__ZN5test31tE() #0 { +// CHECK: entry: +// CHECK: call void @_ZN5test34TestD1Ev(%"struct.test3::Test"* @_ZN5test31tE) +// CHECK: ret void +// CHECK: } + +// CHECK: define internal void @__finalize__ZN5test31tE() #0 { +// CHECK: entry: +// CHECK: %0 = call i32 @unatexit(void ()* @__dtor__ZN5test31tE) +// CHECK: %needsDestruct = icmp eq i32 %0, 0 +// CHECK: br i1 %needsDestruct, 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 dso_local void @__sinit80000000_clang_510e898aa8d263cac999dd03eeed5b51() #0 { +// 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 dso_local void @__sterm80000000_clang_510e898aa8d263cac999dd03eeed5b51() #0 { +// CHECK: entry: +// CHECK: call void @__finalize__ZN5test31tE() +// CHECK: call void @__finalize__ZN5test12t2E() +// CHECK: call void @__finalize__ZN5test12t1E() +// CHECK: ret void +// CHECK: }