[clang] 73840f9 - thread_local support for AIX

Jamie Schmeiser via cfe-commits cfe-commits at lists.llvm.org
Mon Jul 19 07:03:58 PDT 2021


Author: Jamie Schmeiser
Date: 2021-07-19T10:03:22-04:00
New Revision: 73840f9f81413f21d3c8fab0ed85917aca2b5d34

URL: https://github.com/llvm/llvm-project/commit/73840f9f81413f21d3c8fab0ed85917aca2b5d34
DIFF: https://github.com/llvm/llvm-project/commit/73840f9f81413f21d3c8fab0ed85917aca2b5d34.diff

LOG: thread_local support for AIX

Summary:
The AIX linker will produce errors on unresolved weak symbols.  Change the
generated code to not check for the initialization function but just call
it and ensure that it always exists.  Also, the AIX atexit routine has a
different name (and signature) so call it correctly.  Update the lit tests
to test on AIX appropriately.

Author: Jamie Schmeiser <schmeise at ca.ibm.com>
Reviewed By: hubert.reinterpretcast (Hubert Tong)
Differential Revision: https://reviews.llvm.org/D104420

Added: 
    

Modified: 
    clang/lib/CodeGen/CGDeclCXX.cpp
    clang/lib/CodeGen/CodeGenFunction.h
    clang/lib/CodeGen/ItaniumCXXABI.cpp
    clang/test/CodeGenCXX/cxx11-thread-local-reference.cpp
    clang/test/CodeGenCXX/cxx11-thread-local-visibility.cpp
    clang/test/CodeGenCXX/cxx11-thread-local.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/CodeGen/CGDeclCXX.cpp b/clang/lib/CodeGen/CGDeclCXX.cpp
index 27a93c243eca0..d43fb99550a85 100644
--- a/clang/lib/CodeGen/CGDeclCXX.cpp
+++ b/clang/lib/CodeGen/CGDeclCXX.cpp
@@ -262,6 +262,58 @@ llvm::Function *CodeGenFunction::createAtExitStub(const VarDecl &VD,
   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<llvm::Function>(
+          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
index 75b6bf197c553..e026937f91fe1 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -4369,6 +4369,11 @@ class CodeGenFunction : public CodeGenTypeCache {
   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
index 148986c585c2c..d3dc0e6212b8c 100644
--- a/clang/lib/CodeGen/ItaniumCXXABI.cpp
+++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp
@@ -337,14 +337,20 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI {
   /// 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();
@@ -2559,6 +2565,8 @@ void ItaniumCXXABI::EmitGuardedInit(CodeGenFunction &CGF,
 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 @@ void ItaniumCXXABI::EmitThreadLocalInitFuncs(
     }
 
     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) {
@@ -2966,6 +3001,15 @@ void ItaniumCXXABI::EmitThreadLocalInitFuncs(
           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 @@ WebAssemblyCXXABI::emitTerminateForUnexpectedException(CodeGenFunction &CGF,
 
 /// 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
index d3b3e9e9fa631..b1e0cdb0d91ff 100644
--- 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 @@ int &g() { return r; }
 // 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
index a03d7e661f32e..992a25dadc31b 100644
--- 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
index da200c61c5ea3..f3146715b9214 100644
--- 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 @@ int c = 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();
 
@@ -89,9 +90,9 @@ void *e2 = V<char>::m + W<char>::m + &X<char>::m;
 
 // 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 @@ void *e2 = V<char>::m + W<char>::m + &X<char>::m;
 // 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 @@ int f() {
 }
 
 // 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 @@ int f() {
 // 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 @@ int f() {
 // 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 @@ void tls_dtor() {
   // 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 @@ void tls_dtor() {
   // 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 @@ void tls_dtor() {
   // 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 @@ struct PR19254 {
 };
 // 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 @@ thread_local int anon_i{f()};
 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 @@ void set_anon_i() {
 // 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 @@ void set_anon_i() {
 // 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 @@ void set_anon_i() {
 // 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 @@ void set_anon_i() {
 // 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]] = { {{.+}} }


        


More information about the cfe-commits mailing list