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 @@ -262,6 +262,58 @@ return fn; } +/// Create a stub function, suitable for being passed to __pt_atexit_np, +/// which passes the given address to the given destructor function. +llvm::Function *CodeGenFunction::createTLSAtExitStub( + const VarDecl &D, llvm::FunctionCallee Dtor, llvm::Constant *Addr, + llvm::FunctionCallee &AtExit) { + SmallString<256> FnName; + { + llvm::raw_svector_ostream Out(FnName); + CGM.getCXXABI().getMangleContext().mangleDynamicAtExitDestructor(&D, Out); + } + + const CGFunctionInfo &FI = CGM.getTypes().arrangeLLVMFunctionInfo( + getContext().IntTy, /*instanceMethod=*/false, /*chainCall=*/false, + {getContext().IntTy}, FunctionType::ExtInfo(), {}, RequiredArgs::All); + + // Get the stub function type, int(*)(int,...). + llvm::FunctionType *StubTy = + llvm::FunctionType::get(CGM.IntTy, {CGM.IntTy}, true); + + llvm::Function *DtorStub = CGM.CreateGlobalInitOrCleanUpFunction( + StubTy, FnName.str(), FI, D.getLocation()); + + CodeGenFunction CGF(CGM); + + FunctionArgList Args; + ImplicitParamDecl IPD(CGM.getContext(), CGM.getContext().IntTy, + ImplicitParamDecl::Other); + Args.push_back(&IPD); + QualType ResTy = CGM.getContext().IntTy; + + CGF.StartFunction(GlobalDecl(&D, DynamicInitKind::AtExit), ResTy, DtorStub, + FI, Args, D.getLocation(), D.getInit()->getExprLoc()); + + // Emit an artificial location for this function. + auto AL = ApplyDebugLocation::CreateArtificial(CGF); + + llvm::CallInst *call = CGF.Builder.CreateCall(Dtor, Addr); + + // Make sure the call and the callee agree on calling convention. + if (auto *DtorFn = dyn_cast( + Dtor.getCallee()->stripPointerCastsAndAliases())) + call->setCallingConv(DtorFn->getCallingConv()); + + // Return 0 from function + CGF.Builder.CreateStore(llvm::Constant::getNullValue(CGM.IntTy), + CGF.ReturnValue); + + CGF.FinishFunction(); + + return DtorStub; +} + /// Register a global destructor using the C atexit runtime function. void CodeGenFunction::registerGlobalDtorWithAtExit(const VarDecl &VD, llvm::FunctionCallee dtor, 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 @@ -4369,6 +4369,11 @@ llvm::Function *createAtExitStub(const VarDecl &VD, llvm::FunctionCallee Dtor, llvm::Constant *Addr); + llvm::Function *createTLSAtExitStub(const VarDecl &VD, + llvm::FunctionCallee Dtor, + llvm::Constant *Addr, + llvm::FunctionCallee &AtExit); + /// Call atexit() with a function that passes the given argument to /// the given function. void registerGlobalDtorWithAtExit(const VarDecl &D, llvm::FunctionCallee fn, 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 @@ -337,14 +337,20 @@ /// Determine whether we will definitely emit this variable with a constant /// initializer, either because the language semantics demand it or because /// we know that the initializer is a constant. - bool isEmittedWithConstantInitializer(const VarDecl *VD) const { + // For weak definitions, any initializer available in the current translation + // is not necessarily reflective of the initializer used; such initializers + // are ignored unless if InspectInitForWeakDef is true. + bool + isEmittedWithConstantInitializer(const VarDecl *VD, + bool InspectInitForWeakDef = false) const { VD = VD->getMostRecentDecl(); if (VD->hasAttr()) return true; // All later checks examine the initializer specified on the variable. If // the variable is weak, such examination would not be correct. - if (VD->isWeak() || VD->hasAttr()) + if (!InspectInitForWeakDef && + (VD->isWeak() || VD->hasAttr())) return false; const VarDecl *InitDecl = VD->getInitializingDeclaration(); @@ -2559,6 +2565,8 @@ static void emitGlobalDtorWithCXAAtExit(CodeGenFunction &CGF, llvm::FunctionCallee dtor, llvm::Constant *addr, bool TLS) { + assert(!CGF.getTarget().getTriple().isOSAIX() && + "unexpected call to emitGlobalDtorWithCXAAtExit"); assert((TLS || CGF.getTypes().getCodeGenOpts().CXAAtExit) && "__cxa_atexit is disabled"); const char *Name = "__cxa_atexit"; @@ -2952,6 +2960,33 @@ } llvm::LLVMContext &Context = CGM.getModule().getContext(); + + // The linker on AIX is not happy with missing weak symbols. However, + // other TUs will not know whether the initialization routine exists + // so create an empty, init function to satisfy the linker. + // This is needed whenever a thread wrapper function is not used, and + // also when the symbol is weak. + if (CGM.getTriple().isOSAIX() && VD->hasDefinition() && + isEmittedWithConstantInitializer(VD, true) && + !VD->needsDestruction(getContext())) { + // Init should be null. If it were non-null, then the logic above would + // either be defining the function to be an alias or declaring the + // function with the expectation that the definition of the variable + // is elsewhere. + assert(Init == nullptr && "Expected Init to be null."); + + llvm::Function *Func = llvm::Function::Create( + InitFnTy, Var->getLinkage(), InitFnName.str(), &CGM.getModule()); + const CGFunctionInfo &FI = CGM.getTypes().arrangeNullaryFunction(); + CGM.SetLLVMFunctionAttributes(GlobalDecl(), FI, + cast(Func), + /*IsThunk=*/false); + // Create a function body that just returns + llvm::BasicBlock *Entry = llvm::BasicBlock::Create(Context, "", Func); + CGBuilderTy Builder(CGM, Entry); + Builder.CreateRetVoid(); + } + llvm::BasicBlock *Entry = llvm::BasicBlock::Create(Context, "", Wrapper); CGBuilderTy Builder(CGM, Entry); if (HasConstantInitialization) { @@ -2966,6 +3001,15 @@ Fn->setCallingConv(llvm::CallingConv::CXX_FAST_TLS); } } + } else if (CGM.getTriple().isOSAIX()) { + // On AIX, except if constinit and also neither of class type or of + // (possibly multi-dimensional) array of class type, thread_local vars + // will have init routines regardless of whether they are + // const-initialized. Since the routine is guaranteed to exist, we can + // unconditionally call it without testing for its existance. This + // avoids potentially unresolved weak symbols which the AIX linker + // isn't happy with. + Builder.CreateCall(InitFnTy, Init); } else { // Don't know whether we have an init function. Call it if it exists. llvm::Value *Have = Builder.CreateIsNotNull(Init); @@ -4728,20 +4772,43 @@ /// Register a global destructor as best as we know how. void XLCXXABI::registerGlobalDtor(CodeGenFunction &CGF, const VarDecl &D, - llvm::FunctionCallee dtor, - llvm::Constant *addr) { - if (D.getTLSKind() != VarDecl::TLS_None) - llvm::report_fatal_error("thread local storage not yet implemented on AIX"); + llvm::FunctionCallee Dtor, + llvm::Constant *Addr) { + if (D.getTLSKind() != VarDecl::TLS_None) { + // atexit routine expects "int(*)(int,...)" + llvm::FunctionType *FTy = + llvm::FunctionType::get(CGM.IntTy, CGM.IntTy, true); + llvm::PointerType *FpTy = FTy->getPointerTo(); + + // extern "C" int __pt_atexit_np(int flags, int(*)(int,...), ...); + llvm::FunctionType *AtExitTy = + llvm::FunctionType::get(CGM.IntTy, {CGM.IntTy, FpTy}, true); + + // Fetch the actual function. + llvm::FunctionCallee AtExit = + CGM.CreateRuntimeFunction(AtExitTy, "__pt_atexit_np"); + + // Create __dtor function for the var decl. + llvm::Function *DtorStub = CGF.createTLSAtExitStub(D, Dtor, Addr, AtExit); + + // Register above __dtor with atexit(). + // First param is flags and must be 0, second param is function ptr + llvm::Value *NV = llvm::Constant::getNullValue(CGM.IntTy); + CGF.EmitNounwindRuntimeCall(AtExit, {NV, DtorStub}); + + // Cannot unregister TLS __dtor so done + return; + } // Create __dtor function for the var decl. - llvm::Function *dtorStub = CGF.createAtExitStub(D, dtor, addr); + llvm::Function *DtorStub = CGF.createAtExitStub(D, Dtor, Addr); // Register above __dtor with atexit(). - CGF.registerGlobalDtorWithAtExit(dtorStub); + CGF.registerGlobalDtorWithAtExit(DtorStub); // Emit __finalize function to unregister __dtor and (as appropriate) call // __dtor. - emitCXXStermFinalizer(D, dtorStub, addr); + emitCXXStermFinalizer(D, DtorStub, Addr); } void XLCXXABI::emitCXXStermFinalizer(const VarDecl &D, llvm::Function *dtorStub, diff --git a/clang/test/CodeGenCXX/cxx11-thread-local-reference.cpp b/clang/test/CodeGenCXX/cxx11-thread-local-reference.cpp --- a/clang/test/CodeGenCXX/cxx11-thread-local-reference.cpp +++ b/clang/test/CodeGenCXX/cxx11-thread-local-reference.cpp @@ -1,13 +1,14 @@ -// RUN: %clang_cc1 -std=c++11 -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck --check-prefix=CHECK --check-prefix=LINUX %s +// RUN: %clang_cc1 -std=c++11 -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck --check-prefixes=CHECK,LINUX_AIX %s +// RUN: %clang_cc1 -std=c++11 -emit-llvm %s -o - -triple powerpc64-unknown-aix-xcoff | FileCheck --check-prefixes=CHECK,LINUX_AIX %s // RUN: %clang_cc1 -std=c++11 -emit-llvm %s -o - -triple x86_64-apple-darwin12 | FileCheck --check-prefix=CHECK --check-prefix=DARWIN %s int &f(); -// LINUX: @r ={{.*}} thread_local global i32* null +// LINUX_AIX: @r ={{.*}} thread_local global i32* null // DARWIN: @r = internal thread_local global i32* null thread_local int &r = f(); -// LINUX: @_ZTH1r ={{.*}} alias void (), void ()* @__tls_init +// LINUX_AIX: @_ZTH1r ={{.*}} alias void (), void ()* @__tls_init // DARWIN: @_ZTH1r = internal alias void (), void ()* @__tls_init int &g() { return r; } @@ -17,20 +18,20 @@ // CHECK: store i32* %{{.*}}, i32** @r, align 8 // CHECK-LABEL: define{{.*}} nonnull align {{[0-9]+}} dereferenceable({{[0-9]+}}) i32* @_Z1gv() -// LINUX: call i32* @_ZTW1r() +// LINUX_AIX: call i32* @_ZTW1r() // DARWIN: call cxx_fast_tlscc i32* @_ZTW1r() // CHECK: ret i32* %{{.*}} -// LINUX: define weak_odr hidden i32* @_ZTW1r() [[ATTR0:#[0-9]+]] comdat { +// LINUX_AIX: define weak_odr hidden i32* @_ZTW1r() [[ATTR0:#[0-9]+]]{{( comdat)?}} { // DARWIN: define cxx_fast_tlscc i32* @_ZTW1r() [[ATTR1:#[0-9]+]] { -// LINUX: call void @_ZTH1r() +// LINUX_AIX: call void @_ZTH1r() // DARWIN: call cxx_fast_tlscc void @_ZTH1r() // CHECK: load i32*, i32** @r, align 8 // CHECK: ret i32* %{{.*}} -// LINUX-LABEL: define internal void @__tls_init() +// LINUX_AIX-LABEL: define internal void @__tls_init() // DARWIN-LABEL: define internal cxx_fast_tlscc void @__tls_init() // CHECK: call void @[[R_INIT]]() -// LINUX: attributes [[ATTR0]] = { {{.*}}"target-features"{{.*}} } +// LINUX_AIX: attributes [[ATTR0]] = { {{.*}}"target-features"{{.*}} } // DARWIN: attributes [[ATTR1]] = { {{.*}}nounwind{{.*}}"target-features"{{.*}} } diff --git a/clang/test/CodeGenCXX/cxx11-thread-local-visibility.cpp b/clang/test/CodeGenCXX/cxx11-thread-local-visibility.cpp --- a/clang/test/CodeGenCXX/cxx11-thread-local-visibility.cpp +++ b/clang/test/CodeGenCXX/cxx11-thread-local-visibility.cpp @@ -1,12 +1,13 @@ -// RUN: %clang_cc1 -std=c++11 -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck --check-prefix=LINUX %s +// RUN: %clang_cc1 -std=c++11 -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck --check-prefix=LINUX_AIX %s +// RUN: %clang_cc1 -std=c++11 -emit-llvm %s -o - -triple powerpc64-unknown-aix-xcoff | FileCheck --check-prefix=LINUX_AIX %s // RUN: %clang_cc1 -std=c++11 -emit-llvm %s -o - -triple x86_64-apple-darwin12 | FileCheck --check-prefix=DARWIN %s // Regression test for PR40327 -// LINUX: @default_tls ={{.*}} thread_local global i32 -// LINUX: @hidden_tls = hidden thread_local global i32 -// LINUX: define weak_odr hidden i32* @_ZTW11default_tls() -// LINUX: define weak_odr hidden i32* @_ZTW10hidden_tls() +// LINUX_AIX: @default_tls ={{.*}} thread_local global i32 +// LINUX_AIX: @hidden_tls ={{( hidden)?}} thread_local global i32 +// LINUX_AIX: define weak_odr hidden i32* @_ZTW11default_tls() +// LINUX_AIX: define weak_odr hidden i32* @_ZTW10hidden_tls() // // DARWIN: @default_tls = internal thread_local global i32 // DARWIN: @hidden_tls = internal thread_local global i32 diff --git a/clang/test/CodeGenCXX/cxx11-thread-local.cpp b/clang/test/CodeGenCXX/cxx11-thread-local.cpp --- a/clang/test/CodeGenCXX/cxx11-thread-local.cpp +++ b/clang/test/CodeGenCXX/cxx11-thread-local.cpp @@ -1,19 +1,20 @@ -// RUN: %clang_cc1 -std=c++11 -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck --check-prefix=CHECK --check-prefix=LINUX %s -// RUN: %clang_cc1 -std=c++11 -emit-llvm %s -O2 -disable-llvm-passes -o - -triple x86_64-linux-gnu | FileCheck --check-prefix=CHECK --check-prefix=LINUX --check-prefix=CHECK-OPT %s +// RUN: %clang_cc1 -std=c++11 -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck --check-prefixes=CHECK,LINUX,LINUX_AIX %s +// RUN: %clang_cc1 -std=c++11 -emit-llvm %s -O2 -disable-llvm-passes -o - -triple x86_64-linux-gnu | FileCheck --check-prefixes=CHECK,LINUX,LINUX_AIX,CHECK-OPT %s // RUN: %clang_cc1 -std=c++11 -femulated-tls -emit-llvm %s -o - \ -// RUN: -triple x86_64-linux-gnu 2>&1 | FileCheck --check-prefix=CHECK --check-prefix=LINUX %s +// RUN: -triple x86_64-linux-gnu 2>&1 | FileCheck --check-prefixes=CHECK,LINUX,LINUX_AIX %s // RUN: %clang_cc1 -std=c++11 -emit-llvm %s -o - -triple x86_64-apple-darwin12 | FileCheck --check-prefix=CHECK --check-prefix=DARWIN %s +// RUN: %clang_cc1 -std=c++11 -emit-llvm %s -o - -triple powerpc64-unknown-aix-xcoff | FileCheck --check-prefixes=CHECK,AIX,LINUX_AIX %s -// RUN: %clang_cc1 -std=c++11 -fno-use-cxa-atexit -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck --check-prefix=CHECK --check-prefix=LINUX %s -// RUN: %clang_cc1 -std=c++11 -fno-use-cxa-atexit -emit-llvm %s -O2 -disable-llvm-passes -o - -triple x86_64-linux-gnu | FileCheck --check-prefix=CHECK --check-prefix=LINUX --check-prefix=CHECK-OPT %s +// RUN: %clang_cc1 -std=c++11 -fno-use-cxa-atexit -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck --check-prefixes=CHECK,LINUX,LINUX_AIX %s +// RUN: %clang_cc1 -std=c++11 -fno-use-cxa-atexit -emit-llvm %s -O2 -disable-llvm-passes -o - -triple x86_64-linux-gnu | FileCheck --check-prefixes=CHECK,LINUX,LINUX_AIX,CHECK-OPT %s // RUN: %clang_cc1 -std=c++11 -fno-use-cxa-atexit -femulated-tls -emit-llvm %s -o - \ -// RUN: -triple x86_64-linux-gnu 2>&1 | FileCheck --check-prefix=CHECK --check-prefix=LINUX %s +// RUN: -triple x86_64-linux-gnu 2>&1 | FileCheck --check-prefixes=CHECK,LINUX,LINUX_AIX %s // RUN: %clang_cc1 -std=c++11 -fno-use-cxa-atexit -emit-llvm %s -o - -triple x86_64-apple-darwin12 | FileCheck --check-prefix=CHECK --check-prefix=DARWIN %s int f(); int g(); -// LINUX-DAG: @a ={{.*}} thread_local global i32 0 +// LINUX_AIX-DAG: @a ={{.*}} thread_local global i32 0 // DARWIN-DAG: @a = internal thread_local global i32 0 thread_local int a = f(); extern thread_local int b; @@ -23,7 +24,7 @@ static thread_local int d = g(); struct U { static thread_local int m; }; -// LINUX-DAG: @_ZN1U1mE ={{.*}} thread_local global i32 0 +// LINUX_AIX-DAG: @_ZN1U1mE ={{.*}} thread_local global i32 0 // DARWIN-DAG: @_ZN1U1mE = internal thread_local global i32 0 thread_local int U::m = f(); @@ -89,9 +90,9 @@ // CHECK-DAG: @llvm.global_ctors = appending global {{.*}} @[[GLOBAL_INIT:[^ ]*]] -// LINUX-DAG: @_ZTH1a ={{.*}} alias void (), void ()* @__tls_init +// LINUX_AIX-DAG: @_ZTH1a ={{.*}} alias void (), void ()* @__tls_init // DARWIN-DAG: @_ZTH1a = internal alias void (), void ()* @__tls_init -// LINUX-DAG: @_ZTHN1U1mE ={{.*}} alias void (), void ()* @__tls_init +// LINUX_AIX-DAG: @_ZTHN1U1mE ={{.*}} alias void (), void ()* @__tls_init // DARWIN-DAG: @_ZTHN1U1mE = internal alias void (), void ()* @__tls_init // CHECK-DAG: @_ZTHN1VIiE1mE = linkonce_odr alias void (), void ()* @[[V_M_INIT:[^, ]*]] // CHECK-DAG: @_ZTHN1XIiE1mE = linkonce_odr alias void (), void ()* @[[X_M_INIT:[^, ]*]] @@ -106,16 +107,16 @@ // Individual variable initialization functions: // CHECK: define {{.*}} @[[A_INIT:.*]]() -// CHECK: call i32 @_Z1fv() +// CHECK: call{{.*}} i32 @_Z1fv() // CHECK-NEXT: store i32 {{.*}}, i32* @a, align 4 // CHECK-LABEL: define{{.*}} i32 @_Z1fv() int f() { // CHECK: %[[GUARD:.*]] = load i8, i8* @_ZGVZ1fvE1n, align 1 // CHECK: %[[NEED_INIT:.*]] = icmp eq i8 %[[GUARD]], 0 - // CHECK: br i1 %[[NEED_INIT]] + // CHECK: br i1 %[[NEED_INIT]]{{.*}} - // CHECK: %[[CALL:.*]] = call i32 @_Z1gv() + // CHECK: %[[CALL:.*]] = call{{.*}} i32 @_Z1gv() // CHECK: store i32 %[[CALL]], i32* @_ZZ1fvE1n, align 4 // CHECK: store i8 1, i8* @_ZGVZ1fvE1n // CHECK: br label @@ -126,55 +127,57 @@ } // CHECK: define {{.*}} @[[C_INIT:.*]]() -// LINUX: call i32* @_ZTW1b() +// LINUX_AIX: call i32* @_ZTW1b() // DARWIN: call cxx_fast_tlscc i32* @_ZTW1b() // CHECK-NEXT: load i32, i32* %{{.*}}, align 4 // CHECK-NEXT: store i32 %{{.*}}, i32* @c, align 4 -// LINUX-LABEL: define linkonce_odr hidden i32* @_ZTW1b() +// LINUX_AIX-LABEL: define linkonce_odr hidden i32* @_ZTW1b() // LINUX: br i1 icmp ne (void ()* @_ZTH1b, void ()* null), +// AIX-NOT: br i1 icmp ne (void ()* @_ZTH1b, void ()* null), // not null: -// LINUX: call void @_ZTH1b() +// LINUX_AIX: call void @_ZTH1b() // LINUX: br label +// AIX-NOT: br label // finally: -// LINUX: ret i32* @b +// LINUX_AIX: ret i32* @b // DARWIN-LABEL: declare cxx_fast_tlscc i32* @_ZTW1b() // There is no definition of the thread wrapper on Darwin for external TLV. // CHECK: define {{.*}} @[[D_INIT:.*]]() -// CHECK: call i32 @_Z1gv() +// CHECK: call{{.*}} i32 @_Z1gv() // CHECK-NEXT: store i32 %{{.*}}, i32* @_ZL1d, align 4 // CHECK: define {{.*}} @[[U_M_INIT:.*]]() -// CHECK: call i32 @_Z1fv() +// CHECK: call{{.*}} i32 @_Z1fv() // CHECK-NEXT: store i32 %{{.*}}, i32* @_ZN1U1mE, align 4 // CHECK: define {{.*}} @[[E_INIT:.*]]() -// LINUX: call i32* @_ZTWN1VIiE1mE() +// LINUX_AIX: call i32* @_ZTWN1VIiE1mE() // DARWIN: call cxx_fast_tlscc i32* @_ZTWN1VIiE1mE() // CHECK-NEXT: load i32, i32* %{{.*}}, align 4 -// LINUX: call {{.*}}* @_ZTWN1XIiE1mE() +// LINUX_AIX: call {{.*}}* @_ZTWN1XIiE1mE() // DARWIN: call cxx_fast_tlscc {{.*}}* @_ZTWN1XIiE1mE() // CHECK: store {{.*}} @e -// LINUX-LABEL: define weak_odr hidden i32* @_ZTWN1VIiE1mE() +// LINUX_AIX-LABEL: define weak_odr hidden i32* @_ZTWN1VIiE1mE() // DARWIN-LABEL: define weak_odr hidden cxx_fast_tlscc i32* @_ZTWN1VIiE1mE() -// LINUX: call void @_ZTHN1VIiE1mE() +// LINUX_AIX: call void @_ZTHN1VIiE1mE() // DARWIN: call cxx_fast_tlscc void @_ZTHN1VIiE1mE() // CHECK: ret i32* @_ZN1VIiE1mE -// LINUX-LABEL: define weak_odr hidden i32* @_ZTWN1WIiE1mE() +// LINUX_AIX-LABEL: define weak_odr hidden i32* @_ZTWN1WIiE1mE() // DARWIN-LABEL: define weak_odr hidden cxx_fast_tlscc i32* @_ZTWN1WIiE1mE() // CHECK-NOT: call // CHECK: ret i32* @_ZN1WIiE1mE -// LINUX-LABEL: define weak_odr hidden {{.*}}* @_ZTWN1XIiE1mE() +// LINUX_AIX-LABEL: define weak_odr hidden {{.*}}* @_ZTWN1XIiE1mE() // DARWIN-LABEL: define weak_odr hidden cxx_fast_tlscc {{.*}}* @_ZTWN1XIiE1mE() -// LINUX: call void @_ZTHN1XIiE1mE() +// LINUX_AIX: call void @_ZTHN1XIiE1mE() // DARWIN: call cxx_fast_tlscc void @_ZTHN1XIiE1mE() // CHECK: ret {{.*}}* @_ZN1XIiE1mE -// LINUX: define internal void @[[VF_M_INIT]]() +// LINUX_AIX: define internal void @[[VF_M_INIT]]() // DARWIN: define internal cxx_fast_tlscc void @[[VF_M_INIT]]() // LINUX-SAME: comdat($_ZN1VIfE1mE) // DARWIN-NOT: comdat @@ -182,12 +185,12 @@ // CHECK: %[[VF_M_INITIALIZED:.*]] = icmp eq i8 %{{.*}}, 0 // CHECK: br i1 %[[VF_M_INITIALIZED]], // need init: -// CHECK: call i32 @_Z1gv() +// CHECK: call{{.*}} i32 @_Z1gv() // CHECK: store i32 %{{.*}}, i32* @_ZN1VIfE1mE, align 4 // CHECK: store i8 1, i8* bitcast (i64* @_ZGVN1VIfE1mE to i8*) // CHECK: br label -// LINUX: define internal void @[[XF_M_INIT]]() +// LINUX_AIX: define internal void @[[XF_M_INIT]]() // DARWIN: define internal cxx_fast_tlscc void @[[XF_M_INIT]]() // LINUX-SAME: comdat($_ZN1XIfE1mE) // DARWIN-NOT: comdat @@ -195,31 +198,37 @@ // CHECK: %[[XF_M_INITIALIZED:.*]] = icmp eq i8 %{{.*}}, 0 // CHECK: br i1 %[[XF_M_INITIALIZED]], // need init: +// AIX-NOT: br // LINUX: call {{.*}}__cxa_thread_atexit +// AIX: call {{.*}}__pt_atexit_np // DARWIN: call {{.*}}_tlv_atexit // CHECK: store i8 1, i8* bitcast (i64* @_ZGVN1XIfE1mE to i8*) // CHECK: br label // LINUX: declare i32 @__cxa_thread_atexit(void (i8*)*, i8*, i8*) +// AIX: declare i32 @__pt_atexit_np(i32, i32 (i32, ...)*, ...) // DARWIN: declare i32 @_tlv_atexit(void (i8*)*, i8*, i8*) // DARWIN: declare cxx_fast_tlscc i32* @_ZTWN1VIcE1mE() -// LINUX: define linkonce_odr hidden i32* @_ZTWN1VIcE1mE() {{#[0-9]+}} comdat { +// LINUX_AIX: define linkonce_odr hidden i32* @_ZTWN1VIcE1mE() {{#[0-9]+}}{{( comdat)?}} { // LINUX: br i1 icmp ne (void ()* @_ZTHN1VIcE1mE, -// LINUX: call void @_ZTHN1VIcE1mE() -// LINUX: ret i32* @_ZN1VIcE1mE +// AIX-NOT: br i1 icmp ne (void ()* @_ZTHN1VIcE1mE +// LINUX_AIX: call void @_ZTHN1VIcE1mE() +// LINUX_AIX: ret i32* @_ZN1VIcE1mE // DARWIN: declare cxx_fast_tlscc i32* @_ZTWN1WIcE1mE() -// LINUX: define linkonce_odr hidden i32* @_ZTWN1WIcE1mE() {{#[0-9]+}} comdat { +// LINUX_AIX: define linkonce_odr hidden i32* @_ZTWN1WIcE1mE() {{#[0-9]+}}{{( comdat)?}} { // LINUX: br i1 icmp ne (void ()* @_ZTHN1WIcE1mE, -// LINUX: call void @_ZTHN1WIcE1mE() -// LINUX: ret i32* @_ZN1WIcE1mE +// AIX-NOT: br i1 icmp ne (void ()* @_ZTHN1WIcE1mE, +// LINUX_AIX: call void @_ZTHN1WIcE1mE() +// LINUX_AIX: ret i32* @_ZN1WIcE1mE // DARWIN: declare cxx_fast_tlscc {{.*}}* @_ZTWN1XIcE1mE() -// LINUX: define linkonce_odr hidden {{.*}}* @_ZTWN1XIcE1mE() {{#[0-9]+}} comdat { +// LINUX_AIX: define linkonce_odr hidden {{.*}}* @_ZTWN1XIcE1mE() {{#[0-9]+}}{{( comdat)?}} { // LINUX: br i1 icmp ne (void ()* @_ZTHN1XIcE1mE, -// LINUX: call void @_ZTHN1XIcE1mE() -// LINUX: ret {{.*}}* @_ZN1XIcE1mE +// AIX-NOT: br i1 icmp ne (void ()* @_ZTHN1XIcE1mE, +// LINUX_AIX: call void @_ZTHN1XIcE1mE() +// LINUX_AIX: ret {{.*}}* @_ZN1XIcE1mE struct S { S(); ~S(); }; struct T { ~T(); }; @@ -229,6 +238,7 @@ // CHECK: load i8, i8* @_ZGVZ8tls_dtorvE1s // CHECK: call void @_ZN1SC1Ev(%struct.S* {{[^,]*}} @_ZZ8tls_dtorvE1s) // LINUX: call i32 @__cxa_thread_atexit({{.*}}@_ZN1SD1Ev {{.*}} @_ZZ8tls_dtorvE1s{{.*}} @__dso_handle + // AIX: call i32 (i32, i32 (i32, ...)*, ...) @__pt_atexit_np(i32 0, {{.*}}@__dtor__ZZ8tls_dtorvE1s){{.*}} // DARWIN: call i32 @_tlv_atexit({{.*}}@_ZN1SD1Ev {{.*}} @_ZZ8tls_dtorvE1s{{.*}} @__dso_handle // CHECK: store i8 1, i8* @_ZGVZ8tls_dtorvE1s static thread_local S s; @@ -236,6 +246,7 @@ // CHECK: load i8, i8* @_ZGVZ8tls_dtorvE1t // CHECK-NOT: _ZN1T // LINUX: call i32 @__cxa_thread_atexit({{.*}}@_ZN1TD1Ev {{.*}}@_ZZ8tls_dtorvE1t{{.*}} @__dso_handle + // AIX: call i32 (i32, i32 (i32, ...)*, ...) @__pt_atexit_np(i32 0, {{.*}}@__dtor__ZZ8tls_dtorvE1t){{.*}} // DARWIN: call i32 @_tlv_atexit({{.*}}@_ZN1TD1Ev {{.*}}@_ZZ8tls_dtorvE1t{{.*}} @__dso_handle // CHECK: store i8 1, i8* @_ZGVZ8tls_dtorvE1t static thread_local T t; @@ -243,11 +254,36 @@ // CHECK: load i8, i8* @_ZGVZ8tls_dtorvE1u // CHECK: call void @_ZN1SC1Ev(%struct.S* {{[^,]*}} @_ZGRZ8tls_dtorvE1u_) // LINUX: call i32 @__cxa_thread_atexit({{.*}}@_ZN1SD1Ev {{.*}} @_ZGRZ8tls_dtorvE1u_{{.*}} @__dso_handle + // AIX: call i32 (i32, i32 (i32, ...)*, ...) @__pt_atexit_np(i32 0, {{.*}}__dtor__ZZ8tls_dtorvE1u){{.*}} // DARWIN: call i32 @_tlv_atexit({{.*}}@_ZN1SD1Ev {{.*}} @_ZGRZ8tls_dtorvE1u_{{.*}} @__dso_handle // CHECK: store i8 1, i8* @_ZGVZ8tls_dtorvE1u static thread_local const S &u = S(); } +// AIX: define {{.*}}@__dtor__ZZ8tls_dtorvE1s(i32 signext %0, ...){{.*}}{ +// AIX: entry: +// AIX: %.addr = alloca i32, align 4 +// AIX: store i32 %0, i32* %.addr, align 4 +// AIX: call void @_ZN1SD1Ev(%struct.S* @_ZZ8tls_dtorvE1s) +// AIX: ret i32 0 +// AIX: } + +// AIX: define {{.*}}@__dtor__ZZ8tls_dtorvE1t(i32 signext %0, ...){{.*}}{ +// AIX: entry: +// AIX: %.addr = alloca i32, align 4 +// AIX: store i32 %0, i32* %.addr, align 4 +// AIX: call void @_ZN1TD1Ev(%struct.T* @_ZZ8tls_dtorvE1t) +// AIX: ret i32 0 +// AIX: } + +// AIX: define {{.*}}@__dtor__ZZ8tls_dtorvE1u(i32 signext %0, ...){{.*}}{ +// AIX: entry: +// AIX: %.addr = alloca i32, align 4 +// AIX: store i32 %0, i32* %.addr, align 4 +// AIX: call void @_ZN1SD1Ev(%struct.S* @_ZGRZ8tls_dtorvE1u_) +// AIX: ret i32 0 +// AIX: } + // CHECK: define {{.*}} @_Z7PR15991v( int PR15991() { thread_local int n; @@ -261,7 +297,7 @@ }; // CHECK: define {{.*}} @_ZN7PR192541fEv( int PR19254::f() { - // LINUX: call void @_ZTHN7PR192541nE( + // LINUX_AIX: call void @_ZTHN7PR192541nE( // DARWIN: call cxx_fast_tlscc i32* @_ZTWN7PR192541nE( return this->n; } @@ -272,10 +308,10 @@ void set_anon_i() { anon_i = 2; } -// LINUX-LABEL: define internal i32* @_ZTWN12_GLOBAL__N_16anon_iE() +// LINUX_AIX-LABEL: define internal i32* @_ZTWN12_GLOBAL__N_16anon_iE() // DARWIN-LABEL: define internal cxx_fast_tlscc i32* @_ZTWN12_GLOBAL__N_16anon_iE() -// LINUX: define internal void @[[V_M_INIT]]() +// LINUX_AIX: define internal void @[[V_M_INIT]]() // DARWIN: define internal cxx_fast_tlscc void @[[V_M_INIT]]() // LINUX-SAME: comdat($_ZN1VIiE1mE) // DARWIN-NOT: comdat @@ -283,12 +319,12 @@ // CHECK: %[[V_M_INITIALIZED:.*]] = icmp eq i8 %{{.*}}, 0 // CHECK: br i1 %[[V_M_INITIALIZED]], // need init: -// CHECK: call i32 @_Z1gv() +// CHECK: call{{.*}} i32 @_Z1gv() // CHECK: store i32 %{{.*}}, i32* @_ZN1VIiE1mE, align 4 // CHECK: store i8 1, i8* bitcast (i64* @_ZGVN1VIiE1mE to i8*) // CHECK: br label -// LINUX: define internal void @[[X_M_INIT]]() +// LINUX_AIX: define internal void @[[X_M_INIT]]() // DARWIN: define internal cxx_fast_tlscc void @[[X_M_INIT]]() // LINUX-SAME: comdat($_ZN1XIiE1mE) // DARWIN-NOT: comdat @@ -297,6 +333,7 @@ // CHECK: br i1 %[[X_M_INITIALIZED]], // need init: // LINUX: call {{.*}}__cxa_thread_atexit +// AIX: call {{.*}}__pt_atexit_np // DARWIN: call {{.*}}_tlv_atexit // CHECK: store i8 1, i8* bitcast (i64* @_ZGVN1XIiE1mE to i8*) // CHECK: br label @@ -322,9 +359,9 @@ // CHECK-NOT: call void @[[V_M_INIT]]() -// LINUX: define weak_odr hidden i32* @_ZTW1a() +// LINUX_AIX: define weak_odr hidden i32* @_ZTW1a() // DARWIN: define cxx_fast_tlscc i32* @_ZTW1a() -// LINUX: call void @_ZTH1a() +// LINUX_AIX: call void @_ZTH1a() // DARWIN: call cxx_fast_tlscc void @_ZTH1a() // CHECK: ret i32* @a // CHECK: } @@ -335,12 +372,20 @@ // thread-local variables in this TU. // CHECK-NOT: define {{.*}} @_ZTWL1d() -// LINUX-LABEL: define weak_odr hidden i32* @_ZTWN1U1mE() +// LINUX_AIX-LABEL: define weak_odr hidden i32* @_ZTWN1U1mE() // DARWIN-LABEL: define cxx_fast_tlscc i32* @_ZTWN1U1mE() -// LINUX: call void @_ZTHN1U1mE() +// LINUX_AIX: call void @_ZTHN1U1mE() // DARWIN: call cxx_fast_tlscc void @_ZTHN1U1mE() // CHECK: ret i32* @_ZN1U1mE -// LINUX: declare extern_weak void @_ZTH1b() [[ATTR:#[0-9]+]] +// LINUX_AIX: declare extern_weak void @_ZTH1b() [[ATTR:#[0-9]+]] + +// AIX: define linkonce_odr void @_ZTHN1WIiE1mE(){{.*}} { +// AIX-NEXT: ret void +// AIX-NEXT: } +// CHECK-NOT: @_ZTHN1WIfE1mE = +// AIX: define weak_odr void @_ZTHN1WIfE1mE(){{.*}} { +// AIX-NEXT: ret void +// AIX-NEXT: } -// LINUX: attributes [[ATTR]] = { {{.+}} } +// LINUX_AIX: attributes [[ATTR]] = { {{.+}} }