/// 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<ConstInitAttr>())
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<SelectAnyAttr>())
+ if (!InspectInitForWeakDef &&
+ (VD->isWeak() || VD->hasAttr<SelectAnyAttr>()))
return false;
const VarDecl *InitDecl = VD->getInitializingDeclaration();
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";
}
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<llvm::Function>(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) {
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);
/// 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,
-// 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;
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();
// 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:[^, ]*]]
// 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
}
// 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
// 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
// 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(); };
// 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;
// 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;
// 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;
};
// 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;
}
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
// 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
// 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
// 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: }
// 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]] = { {{.+}} }