Author: Xiangling Liao
Date: 2020-11-19T09:24:01-05:00
New Revision: 17497ec514f7a87e0ac39a803534b3a324a19324

URL: 
https://github.com/llvm/llvm-project/commit/17497ec514f7a87e0ac39a803534b3a324a19324
DIFF: 
https://github.com/llvm/llvm-project/commit/17497ec514f7a87e0ac39a803534b3a324a19324.diff

LOG: [AIX][FE] Support constructor/destructor attribute

Support attribute((constructor)) and attribute((destructor)) on AIX

Differential Revision: https://reviews.llvm.org/D90892

Added: 
    clang/test/CodeGen/aix-constructor-attribute.c
    clang/test/CodeGen/aix-destructor-attribute.c
    clang/test/CodeGenCXX/aix-constructor-attribute.cpp
    clang/test/CodeGenCXX/aix-destructor-attribute.cpp

Modified: 
    clang/lib/CodeGen/CGDeclCXX.cpp
    clang/lib/CodeGen/CodeGenFunction.h
    clang/lib/CodeGen/CodeGenModule.cpp
    clang/lib/CodeGen/CodeGenModule.h
    clang/lib/CodeGen/ItaniumCXXABI.cpp
    clang/lib/Sema/SemaDeclAttr.cpp

Removed: 
    clang/test/CodeGen/aix-constructor-attribute.cpp
    clang/test/CodeGen/aix-destructor-attribute.cpp
    clang/test/CodeGenCXX/aix-sinit-register-global-dtors-with-atexit.cpp


################################################################################
diff  --git a/clang/lib/CodeGen/CGDeclCXX.cpp b/clang/lib/CodeGen/CGDeclCXX.cpp
index b9ff561aa641..3dbf4cc7cb97 100644
--- a/clang/lib/CodeGen/CGDeclCXX.cpp
+++ b/clang/lib/CodeGen/CGDeclCXX.cpp
@@ -273,8 +273,10 @@ void CodeGenFunction::registerGlobalDtorWithAtExit(const 
VarDecl &VD,
 
 void CodeGenFunction::registerGlobalDtorWithAtExit(llvm::Constant *dtorStub) {
   // extern "C" int atexit(void (*f)(void));
-  assert(cast<llvm::Function>(dtorStub)->getFunctionType() ==
-             llvm::FunctionType::get(CGM.VoidTy, false) &&
+  assert(dtorStub->getType() ==
+             llvm::PointerType::get(
+                 llvm::FunctionType::get(CGM.VoidTy, false),
+                 dtorStub->getType()->getPointerAddressSpace()) &&
          "Argument to atexit has a wrong type.");
 
   llvm::FunctionType *atexitTy =
@@ -290,7 +292,7 @@ void 
CodeGenFunction::registerGlobalDtorWithAtExit(llvm::Constant *dtorStub) {
 }
 
 llvm::Value *
-CodeGenFunction::unregisterGlobalDtorWithUnAtExit(llvm::Function *dtorStub) {
+CodeGenFunction::unregisterGlobalDtorWithUnAtExit(llvm::Constant *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
@@ -298,8 +300,10 @@ 
CodeGenFunction::unregisterGlobalDtorWithUnAtExit(llvm::Function *dtorStub) {
   // value is returned.
   //
   // extern "C" int unatexit(void (*f)(void));
-  assert(dtorStub->getFunctionType() ==
-             llvm::FunctionType::get(CGM.VoidTy, false) &&
+  assert(dtorStub->getType() ==
+             llvm::PointerType::get(
+                 llvm::FunctionType::get(CGM.VoidTy, false),
+                 dtorStub->getType()->getPointerAddressSpace()) &&
          "Argument to unatexit has a wrong type.");
 
   llvm::FunctionType *unatexitTy =

diff  --git a/clang/lib/CodeGen/CodeGenFunction.h 
b/clang/lib/CodeGen/CodeGenFunction.h
index 8a1e47db33ff..40efa6dbc5ff 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -4304,7 +4304,7 @@ class CodeGenFunction : public CodeGenTypeCache {
   void registerGlobalDtorWithAtExit(llvm::Constant *dtorStub);
 
   /// Call unatexit() with function dtorStub.
-  llvm::Value *unregisterGlobalDtorWithUnAtExit(llvm::Function *dtorStub);
+  llvm::Value *unregisterGlobalDtorWithUnAtExit(llvm::Constant *dtorStub);
 
   /// Emit code in this function to perform a guarded variable
   /// initialization.  Guarded initializations are used when it's not

diff  --git a/clang/lib/CodeGen/CodeGenModule.cpp 
b/clang/lib/CodeGen/CodeGenModule.cpp
index 1f81faaa2c6f..f56b7374082f 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -1278,11 +1278,10 @@ void CodeGenModule::AddGlobalCtor(llvm::Function *Ctor, 
int Priority,
 
 /// AddGlobalDtor - Add a function to the list that will be called
 /// when the module is unloaded.
-void CodeGenModule::AddGlobalDtor(llvm::Function *Dtor, int Priority) {
-  if (CodeGenOpts.RegisterGlobalDtorsWithAtExit) {
-    if (getCXXABI().useSinitAndSterm())
-      llvm::report_fatal_error(
-          "register global dtors with atexit() is not supported yet");
+void CodeGenModule::AddGlobalDtor(llvm::Function *Dtor, int Priority,
+                                  bool IsDtorAttrFunc) {
+  if (CodeGenOpts.RegisterGlobalDtorsWithAtExit &&
+      (!getContext().getTargetInfo().getTriple().isOSAIX() || IsDtorAttrFunc)) 
{
     DtorsUsingAtExit[Priority].push_back(Dtor);
     return;
   }
@@ -4716,7 +4715,7 @@ void 
CodeGenModule::EmitGlobalFunctionDefinition(GlobalDecl GD,
   if (const ConstructorAttr *CA = D->getAttr<ConstructorAttr>())
     AddGlobalCtor(Fn, CA->getPriority());
   if (const DestructorAttr *DA = D->getAttr<DestructorAttr>())
-    AddGlobalDtor(Fn, DA->getPriority());
+    AddGlobalDtor(Fn, DA->getPriority(), true);
   if (D->hasAttr<AnnotateAttr>())
     AddGlobalAnnotations(D, Fn);
 }

diff  --git a/clang/lib/CodeGen/CodeGenModule.h 
b/clang/lib/CodeGen/CodeGenModule.h
index 8996805145fd..7d812b6658dc 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -1478,7 +1478,8 @@ class CodeGenModule : public CodeGenTypeCache {
   // FIXME: Hardcoding priority here is gross.
   void AddGlobalCtor(llvm::Function *Ctor, int Priority = 65535,
                      llvm::Constant *AssociatedData = nullptr);
-  void AddGlobalDtor(llvm::Function *Dtor, int Priority = 65535);
+  void AddGlobalDtor(llvm::Function *Dtor, int Priority = 65535,
+                     bool IsDtorAttrFunc = false);
 
   /// EmitCtorList - Generates a global array of functions and priorities using
   /// the given list and name. This array will have appending linkage and is
@@ -1508,6 +1509,11 @@ class CodeGenModule : public CodeGenTypeCache {
   /// __cxa_atexit, if it is available, or atexit otherwise.
   void registerGlobalDtorsWithAtExit();
 
+  // When using sinit and sterm functions, unregister
+  // __attribute__((destructor)) annotated functions which were previously
+  // registered by the atexit subroutine using unatexit.
+  void unregisterGlobalDtorsWithUnAtExit();
+
   void emitMultiVersionFunctions();
 
   /// Emit any vtables which we deferred and still have a use for.

diff  --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp 
b/clang/lib/CodeGen/ItaniumCXXABI.cpp
index 98ef357b76a0..ac2cd72a213b 100644
--- a/clang/lib/CodeGen/ItaniumCXXABI.cpp
+++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp
@@ -2530,48 +2530,132 @@ static void 
emitGlobalDtorWithCXAAtExit(CodeGenFunction &CGF,
   CGF.EmitNounwindRuntimeCall(atexit, args);
 }
 
-void CodeGenModule::registerGlobalDtorsWithAtExit() {
+static llvm::Function *createGlobalInitOrCleanupFn(CodeGen::CodeGenModule &CGM,
+                                                   StringRef FnName) {
+  // Create a function that registers/unregisters destructors that have the 
same
+  // priority.
+  llvm::FunctionType *FTy = llvm::FunctionType::get(CGM.VoidTy, false);
+  llvm::Function *GlobalInitOrCleanupFn = 
CGM.CreateGlobalInitOrCleanUpFunction(
+      FTy, FnName, CGM.getTypes().arrangeNullaryFunction(), SourceLocation());
+
+  return GlobalInitOrCleanupFn;
+}
+
+static FunctionDecl *
+createGlobalInitOrCleanupFnDecl(CodeGen::CodeGenModule &CGM, StringRef FnName) 
{
+  ASTContext &Ctx = CGM.getContext();
+  QualType FunctionTy = Ctx.getFunctionType(Ctx.VoidTy, llvm::None, {});
+  return FunctionDecl::Create(
+      Ctx, Ctx.getTranslationUnitDecl(), SourceLocation(), SourceLocation(),
+      &Ctx.Idents.get(FnName), FunctionTy, nullptr, SC_Static, false, false);
+}
+
+void CodeGenModule::unregisterGlobalDtorsWithUnAtExit() {
   for (const auto &I : DtorsUsingAtExit) {
     int Priority = I.first;
+    std::string GlobalCleanupFnName =
+        std::string("__GLOBAL_cleanup_") + llvm::to_string(Priority);
+
+    llvm::Function *GlobalCleanupFn =
+        createGlobalInitOrCleanupFn(*this, GlobalCleanupFnName);
+
+    FunctionDecl *GlobalCleanupFD =
+        createGlobalInitOrCleanupFnDecl(*this, GlobalCleanupFnName);
+
+    CodeGenFunction CGF(*this);
+    CGF.StartFunction(GlobalDecl(GlobalCleanupFD), getContext().VoidTy,
+                      GlobalCleanupFn, getTypes().arrangeNullaryFunction(),
+                      FunctionArgList(), SourceLocation(), SourceLocation());
+
+    // Get the destructor function type, void(*)(void).
+    llvm::FunctionType *dtorFuncTy = llvm::FunctionType::get(CGF.VoidTy, 
false);
+    llvm::Type *dtorTy = dtorFuncTy->getPointerTo();
+
+    // Destructor functions are run/unregistered in non-ascending
+    // order of their priorities.
     const llvm::TinyPtrVector<llvm::Function *> &Dtors = I.second;
+    auto itv = Dtors.rbegin();
+    while (itv != Dtors.rend()) {
+      llvm::Function *Dtor = *itv;
+
+      // We're assuming that the destructor function is something we can
+      // reasonably call with the correct CC.  Go ahead and cast it to the
+      // right prototype.
+      llvm::Constant *dtor = llvm::ConstantExpr::getBitCast(Dtor, dtorTy);
+      llvm::Value *V = CGF.unregisterGlobalDtorWithUnAtExit(dtor);
+      llvm::Value *NeedsDestruct =
+          CGF.Builder.CreateIsNull(V, "needs_destruct");
+
+      llvm::BasicBlock *DestructCallBlock =
+          CGF.createBasicBlock("destruct.call");
+      llvm::BasicBlock *EndBlock = CGF.createBasicBlock(
+          (itv + 1) != Dtors.rend() ? "unatexit.call" : "destruct.end");
+      // Check if unatexit returns a value of 0. If it does, jump to
+      // DestructCallBlock, otherwise jump to EndBlock directly.
+      CGF.Builder.CreateCondBr(NeedsDestruct, DestructCallBlock, EndBlock);
+
+      CGF.EmitBlock(DestructCallBlock);
+
+      // Emit the call to casted Dtor.
+      llvm::CallInst *CI = CGF.Builder.CreateCall(dtorFuncTy, dtor);
+      // Make sure the call and the callee agree on calling convention.
+      CI->setCallingConv(Dtor->getCallingConv());
+
+      CGF.EmitBlock(EndBlock);
+
+      itv++;
+    }
+
+    CGF.FinishFunction();
+    AddGlobalDtor(GlobalCleanupFn, Priority);
+  }
+}
+
+void CodeGenModule::registerGlobalDtorsWithAtExit() {
+  for (const auto &I : DtorsUsingAtExit) {
+    int Priority = I.first;
+    std::string GlobalInitFnName =
+        std::string("__GLOBAL_init_") + llvm::to_string(Priority);
+    llvm::Function *GlobalInitFn =
+        createGlobalInitOrCleanupFn(*this, GlobalInitFnName);
+    FunctionDecl *GlobalInitFD =
+        createGlobalInitOrCleanupFnDecl(*this, GlobalInitFnName);
+
+    CodeGenFunction CGF(*this);
+    CGF.StartFunction(GlobalDecl(GlobalInitFD), getContext().VoidTy,
+                      GlobalInitFn, getTypes().arrangeNullaryFunction(),
+                      FunctionArgList(), SourceLocation(), SourceLocation());
 
-    // Create a function that registers destructors that have the same 
priority.
-    //
     // Since constructor functions are run in non-descending order of their
     // priorities, destructors are registered in non-descending order of their
     // priorities, and since destructor functions are run in the reverse order
     // of their registration, destructor functions are run in non-ascending
     // order of their priorities.
-    CodeGenFunction CGF(*this);
-    std::string GlobalInitFnName =
-        std::string("__GLOBAL_init_") + llvm::to_string(Priority);
-    llvm::FunctionType *FTy = llvm::FunctionType::get(VoidTy, false);
-    llvm::Function *GlobalInitFn = CreateGlobalInitOrCleanUpFunction(
-        FTy, GlobalInitFnName, getTypes().arrangeNullaryFunction(),
-        SourceLocation());
-    ASTContext &Ctx = getContext();
-    QualType ReturnTy = Ctx.VoidTy;
-    QualType FunctionTy = Ctx.getFunctionType(ReturnTy, llvm::None, {});
-    FunctionDecl *FD = FunctionDecl::Create(
-        Ctx, Ctx.getTranslationUnitDecl(), SourceLocation(), SourceLocation(),
-        &Ctx.Idents.get(GlobalInitFnName), FunctionTy, nullptr, SC_Static,
-        false, false);
-    CGF.StartFunction(GlobalDecl(FD), ReturnTy, GlobalInitFn,
-                      getTypes().arrangeNullaryFunction(), FunctionArgList(),
-                      SourceLocation(), SourceLocation());
-
+    const llvm::TinyPtrVector<llvm::Function *> &Dtors = I.second;
     for (auto *Dtor : Dtors) {
       // Register the destructor function calling __cxa_atexit if it is
       // available. Otherwise fall back on calling atexit.
-      if (getCodeGenOpts().CXAAtExit)
+      if (getCodeGenOpts().CXAAtExit) {
         emitGlobalDtorWithCXAAtExit(CGF, Dtor, nullptr, false);
-      else
-        CGF.registerGlobalDtorWithAtExit(Dtor);
+      } else {
+        // Get the destructor function type, void(*)(void).
+        llvm::Type *dtorTy =
+            llvm::FunctionType::get(CGF.VoidTy, false)->getPointerTo();
+
+        // We're assuming that the destructor function is something we can
+        // reasonably call with the correct CC.  Go ahead and cast it to the
+        // right prototype.
+        CGF.registerGlobalDtorWithAtExit(
+            llvm::ConstantExpr::getBitCast(Dtor, dtorTy));
+      }
     }
 
     CGF.FinishFunction();
     AddGlobalCtor(GlobalInitFn, Priority, nullptr);
   }
+
+  if (getCXXABI().useSinitAndSterm())
+    unregisterGlobalDtorsWithUnAtExit();
 }
 
 /// Register a global destructor as best as we know how.

diff  --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 6a414e257447..a14c16229419 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -7487,19 +7487,12 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, 
Decl *D,
     handlePassObjectSizeAttr(S, D, AL);
     break;
   case ParsedAttr::AT_Constructor:
-    if (S.Context.getTargetInfo().getTriple().isOSAIX())
-      llvm::report_fatal_error(
-          "'constructor' attribute is not yet supported on AIX");
-    else
       handleConstructorAttr(S, D, AL);
     break;
   case ParsedAttr::AT_Deprecated:
     handleDeprecatedAttr(S, D, AL);
     break;
   case ParsedAttr::AT_Destructor:
-    if (S.Context.getTargetInfo().getTriple().isOSAIX())
-      llvm::report_fatal_error("'destructor' attribute is not yet supported on 
AIX");
-    else
       handleDestructorAttr(S, D, AL);
     break;
   case ParsedAttr::AT_EnableIf:

diff  --git a/clang/test/CodeGen/aix-constructor-attribute.c 
b/clang/test/CodeGen/aix-constructor-attribute.c
new file mode 100644
index 000000000000..8759b02b3aa7
--- /dev/null
+++ b/clang/test/CodeGen/aix-constructor-attribute.c
@@ -0,0 +1,24 @@
+// RUN: %clang_cc1 -triple powerpc-ibm-aix-xcoff -emit-llvm \
+// RUN:     -fno-use-cxa-atexit < %s |\
+// RUN:   FileCheck %s
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix-xcoff -emit-llvm \
+// RUN:     -fno-use-cxa-atexit < %s | \
+// RUN:   FileCheck %s
+
+// CHECK: @llvm.global_ctors = appending global [3 x { i32, void ()*, i8* }] 
[{ i32, void ()*, i8* } { i32 65535, void ()* bitcast (i32 ()* @foo3 to void 
()*), i8* null }, { i32, void ()*, i8* } { i32 180, void ()* bitcast (i32 ()* 
@foo2 to void ()*), i8* null }, { i32, void ()*, i8* } { i32 180, void ()* 
bitcast (i32 ()* @foo to void ()*), i8* null }]
+
+int foo() __attribute__((constructor(180)));
+int foo2() __attribute__((constructor(180)));
+int foo3() __attribute__((constructor(65535)));
+
+int foo3() {
+  return 3;
+}
+
+int foo2() {
+  return 2;
+}
+
+int foo() {
+  return 1;
+}

diff  --git a/clang/test/CodeGen/aix-constructor-attribute.cpp 
b/clang/test/CodeGen/aix-constructor-attribute.cpp
deleted file mode 100644
index 008a92483361..000000000000
--- a/clang/test/CodeGen/aix-constructor-attribute.cpp
+++ /dev/null
@@ -1,20 +0,0 @@
-// 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 is not yet 
supported on AIX

diff  --git a/clang/test/CodeGen/aix-destructor-attribute.c 
b/clang/test/CodeGen/aix-destructor-attribute.c
new file mode 100644
index 000000000000..fccd0b73f62e
--- /dev/null
+++ b/clang/test/CodeGen/aix-destructor-attribute.c
@@ -0,0 +1,84 @@
+// RUN: %clang_cc1 -triple powerpc-ibm-aix-xcoff -emit-llvm \
+// RUN:     -fno-use-cxa-atexit < %s | \
+// RUN:   FileCheck --check-prefix=NO-REGISTER %s
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix-xcoff -emit-llvm \
+// RUN:     -fno-use-cxa-atexit < %s | \
+// RUN:   FileCheck --check-prefix=NO-REGISTER %s
+
+// RUN: %clang_cc1 -triple powerpc-ibm-aix-xcoff -emit-llvm \
+// RUN:     -fno-use-cxa-atexit -fregister-global-dtors-with-atexit < %s | \
+// RUN:   FileCheck --check-prefix=REGISTER %s
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix-xcoff -emit-llvm \
+// RUN:     -fno-use-cxa-atexit -fregister-global-dtors-with-atexit < %s | \
+// RUN:   FileCheck --check-prefix=REGISTER %s
+
+int bar() __attribute__((destructor(100)));
+int bar2() __attribute__((destructor(65535)));
+int bar3(int) __attribute__((destructor(65535)));
+
+int bar() {
+  return 1;
+}
+
+int bar2() {
+  return 2;
+}
+
+int bar3(int a) {
+  return a;
+}
+
+// NO-REGISTER: @llvm.global_dtors = appending global [3 x { i32, void ()*, 
i8* }] [{ i32, void ()*, i8* } { i32 100, void ()* bitcast (i32 ()* @bar to 
void ()*), i8* null }, { i32, void ()*, i8* } { i32 65535, void ()* bitcast 
(i32 ()* @bar2 to void ()*), i8* null }, { i32, void ()*, i8* } { i32 65535, 
void ()* bitcast (i32 (i32)* @bar3 to void ()*), i8* null }]
+
+// REGISTER: @llvm.global_ctors = appending global [2 x { i32, void ()*, i8* 
}] [{ i32, void ()*, i8* } { i32 100, void ()* @__GLOBAL_init_100, i8* null }, 
{ i32, void ()*, i8* } { i32 65535, void ()* @__GLOBAL_init_65535, i8* null }]
+// REGISTER: @llvm.global_dtors = appending global [2 x { i32, void ()*, i8* 
}] [{ i32, void ()*, i8* } { i32 100, void ()* @__GLOBAL_cleanup_100, i8* null 
}, { i32, void ()*, i8* } { i32 65535, void ()* @__GLOBAL_cleanup_65535, i8* 
null }]
+
+// REGISTER: define internal void @__GLOBAL_init_100() [[ATTR:#[0-9]+]] {
+// REGISTER: entry:
+// REGISTER:   %0 = call i32 @atexit(void ()* bitcast (i32 ()* @bar to void 
()*))
+// REGISTER:   ret void
+// REGISTER: }
+
+// REGISTER: define internal void @__GLOBAL_init_65535() [[ATTR:#[0-9]+]] {
+// REGISTER: entry:
+// REGISTER:   %0 = call i32 @atexit(void ()* bitcast (i32 ()* @bar2 to void 
()*))
+// REGISTER:   %1 = call i32 @atexit(void ()* bitcast (i32 (i32)* @bar3 to 
void ()*))
+// REGISTER:   ret void
+// REGISTER: }
+
+// REGISTER: define internal void @__GLOBAL_cleanup_100() [[ATTR:#[0-9]+]] {
+// REGISTER: entry:
+// REGISTER:   %0 = call i32 @unatexit(void ()* bitcast (i32 ()* @bar to void 
()*))
+// REGISTER:   %needs_destruct = icmp eq i32 %0, 0
+// REGISTER:   br i1 %needs_destruct, label %destruct.call, label %destruct.end
+
+// REGISTER: destruct.call:
+// REGISTER:   call void bitcast (i32 ()* @bar to void ()*)()
+// REGISTER:   br label %destruct.end
+
+// REGISTER: destruct.end:
+// REGISTER:   ret void
+// REGISTER: }
+
+// REGISTER: define internal void @__GLOBAL_cleanup_65535() [[ATTR:#[0-9]+]] {
+// REGISTER: entry:
+// REGISTER:   %0 = call i32 @unatexit(void ()* bitcast (i32 (i32)* @bar3 to 
void ()*))
+// REGISTER:   %needs_destruct = icmp eq i32 %0, 0
+// REGISTER:   br i1 %needs_destruct, label %destruct.call, label 
%unatexit.call
+
+// REGISTER: destruct.call:
+// REGISTER:   call void bitcast (i32 (i32)* @bar3 to void ()*)()
+// REGISTER:   br label %unatexit.call
+
+// REGISTER: unatexit.call:
+// REGISTER:   %1 = call i32 @unatexit(void ()* bitcast (i32 ()* @bar2 to void 
()*))
+// REGISTER:   %needs_destruct1 = icmp eq i32 %1, 0
+// REGISTER:   br i1 %needs_destruct1, label %destruct.call2, label 
%destruct.end
+
+// REGISTER: destruct.call2:
+// REGISTER:   call void bitcast (i32 ()* @bar2 to void ()*)()
+// REGISTER:   br label %destruct.end
+
+// REGISTER: destruct.end:
+// REGISTER:   ret void
+// REGISTER: }

diff  --git a/clang/test/CodeGen/aix-destructor-attribute.cpp 
b/clang/test/CodeGen/aix-destructor-attribute.cpp
deleted file mode 100644
index 27200d392b87..000000000000
--- a/clang/test/CodeGen/aix-destructor-attribute.cpp
+++ /dev/null
@@ -1,20 +0,0 @@
-// 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 is not yet 
supported on AIX

diff  --git a/clang/test/CodeGenCXX/aix-constructor-attribute.cpp 
b/clang/test/CodeGenCXX/aix-constructor-attribute.cpp
new file mode 100644
index 000000000000..91c8d16158be
--- /dev/null
+++ b/clang/test/CodeGenCXX/aix-constructor-attribute.cpp
@@ -0,0 +1,31 @@
+// RUN: %clang_cc1 -triple powerpc-ibm-aix-xcoff -x c++ -emit-llvm \
+// RUN:     -fno-use-cxa-atexit < %s |\
+// RUN:   FileCheck %s
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix-xcoff -x c++ -emit-llvm \
+// RUN:     -fno-use-cxa-atexit < %s | \
+// RUN:   FileCheck %s
+
+// CHECK: @llvm.global_ctors = appending global [4 x { i32, void ()*, i8* }] 
[{ i32, void ()*, i8* } { i32 65535, void ()* bitcast (i32 ()* @_Z4foo3v to 
void ()*), i8* null }, { i32, void ()*, i8* } { i32 180, void ()* bitcast (i32 
()* @_Z4foo2v to void ()*), i8* null }, { i32, void ()*, i8* } { i32 180, void 
()* bitcast (i32 ()* @_Z3foov to void ()*), i8* null }, { i32, void ()*, i8* } 
{ i32 65535, void ()* @_GLOBAL__sub_I__, i8* null }]
+// CHECK: @llvm.global_dtors = appending global [1 x { i32, void ()*, i8* }] 
[{ i32, void ()*, i8* } { i32 65535, void ()* @_GLOBAL__D_a, i8* null }]
+
+int foo() __attribute__((constructor(180)));
+int foo2() __attribute__((constructor(180)));
+int foo3() __attribute__((constructor(65535)));
+
+struct Test {
+public:
+  Test() {}
+  ~Test() {}
+} t;
+
+int foo3() {
+  return 3;
+}
+
+int foo2() {
+  return 2;
+}
+
+int foo() {
+  return 1;
+}

diff  --git a/clang/test/CodeGenCXX/aix-destructor-attribute.cpp 
b/clang/test/CodeGenCXX/aix-destructor-attribute.cpp
new file mode 100644
index 000000000000..56ee3094552c
--- /dev/null
+++ b/clang/test/CodeGenCXX/aix-destructor-attribute.cpp
@@ -0,0 +1,90 @@
+// RUN: %clang_cc1 -triple powerpc-ibm-aix-xcoff -x c++ -emit-llvm \
+// RUN:     -fno-use-cxa-atexit < %s | \
+// RUN:   FileCheck --check-prefix=NO-REGISTER %s
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix-xcoff -x c++ -emit-llvm \
+// RUN:     -fno-use-cxa-atexit < %s | \
+// RUN:   FileCheck --check-prefix=NO-REGISTER %s
+
+// RUN: %clang_cc1 -triple powerpc-ibm-aix-xcoff -x c++ -emit-llvm \
+// RUN:     -fno-use-cxa-atexit -fregister-global-dtors-with-atexit < %s | \
+// RUN:   FileCheck --check-prefix=REGISTER %s
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix-xcoff -x c++ -emit-llvm \
+// RUN:     -fno-use-cxa-atexit -fregister-global-dtors-with-atexit < %s | \
+// RUN:   FileCheck --check-prefix=REGISTER %s
+
+struct test {
+  test();
+  ~test();
+} t;
+
+int bar() __attribute__((destructor(100)));
+int bar2() __attribute__((destructor(65535)));
+int bar3(int) __attribute__((destructor(65535)));
+
+int bar() {
+  return 1;
+}
+
+int bar2() {
+  return 2;
+}
+
+int bar3(int a) {
+  return a;
+}
+
+// NO-REGISTER: @llvm.global_ctors = appending global [1 x { i32, void ()*, 
i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* @_GLOBAL__sub_I__, i8* 
null }]
+// NO-REGISTER: @llvm.global_dtors = appending global [4 x { i32, void ()*, 
i8* }] [{ i32, void ()*, i8* } { i32 100, void ()* bitcast (i32 ()* @_Z3barv to 
void ()*), i8* null }, { i32, void ()*, i8* } { i32 65535, void ()* bitcast 
(i32 ()* @_Z4bar2v to void ()*), i8* null }, { i32, void ()*, i8* } { i32 
65535, void ()* bitcast (i32 (i32)* @_Z4bar3i to void ()*), i8* null }, { i32, 
void ()*, i8* } { i32 65535, void ()* @_GLOBAL__D_a, i8* null }]
+
+// REGISTER: @llvm.global_ctors = appending global [3 x { i32, void ()*, i8* 
}] [{ i32, void ()*, i8* } { i32 65535, void ()* @_GLOBAL__sub_I__, i8* null }, 
{ i32, void ()*, i8* } { i32 100, void ()* @__GLOBAL_init_100, i8* null }, { 
i32, void ()*, i8* } { i32 65535, void ()* @__GLOBAL_init_65535, i8* null }]
+// REGISTER: @llvm.global_dtors = appending global [3 x { i32, void ()*, i8* 
}] [{ i32, void ()*, i8* } { i32 65535, void ()* @_GLOBAL__D_a, i8* null }, { 
i32, void ()*, i8* } { i32 100, void ()* @__GLOBAL_cleanup_100, i8* null }, { 
i32, void ()*, i8* } { i32 65535, void ()* @__GLOBAL_cleanup_65535, i8* null }]
+
+// REGISTER: define internal void @__GLOBAL_init_100() [[ATTR:#[0-9]+]] {
+// REGISTER: entry:
+// REGISTER:   %0 = call i32 @atexit(void ()* bitcast (i32 ()* @_Z3barv to 
void ()*))
+// REGISTER:   ret void
+// REGISTER: }
+
+// REGISTER: define internal void @__GLOBAL_init_65535() [[ATTR:#[0-9]+]] {
+// REGISTER: entry:
+// REGISTER:   %0 = call i32 @atexit(void ()* bitcast (i32 ()* @_Z4bar2v to 
void ()*))
+// REGISTER:   %1 = call i32 @atexit(void ()* bitcast (i32 (i32)* @_Z4bar3i to 
void ()*))
+// REGISTER:   ret void
+// REGISTER: }
+
+// REGISTER: define internal void @__GLOBAL_cleanup_100() [[ATTR:#[0-9]+]] {
+// REGISTER: entry:
+// REGISTER:   %0 = call i32 @unatexit(void ()* bitcast (i32 ()* @_Z3barv to 
void ()*))
+// REGISTER:   %needs_destruct = icmp eq i32 %0, 0
+// REGISTER:   br i1 %needs_destruct, label %destruct.call, label %destruct.end
+
+// REGISTER: destruct.call:
+// REGISTER:   call void bitcast (i32 ()* @_Z3barv to void ()*)()
+// REGISTER:   br label %destruct.end
+
+// REGISTER: destruct.end:
+// REGISTER:   ret void
+// REGISTER: }
+
+// REGISTER: define internal void @__GLOBAL_cleanup_65535() [[ATTR:#[0-9]+]] {
+// REGISTER: entry:
+// REGISTER:   %0 = call i32 @unatexit(void ()* bitcast (i32 (i32)* @_Z4bar3i 
to void ()*))
+// REGISTER:   %needs_destruct = icmp eq i32 %0, 0
+// REGISTER:   br i1 %needs_destruct, label %destruct.call, label 
%unatexit.call
+
+// REGISTER: destruct.call:
+// REGISTER:   call void bitcast (i32 (i32)* @_Z4bar3i to void ()*)()
+// REGISTER:   br label %unatexit.call
+
+// REGISTER: unatexit.call:
+// REGISTER:   %1 = call i32 @unatexit(void ()* bitcast (i32 ()* @_Z4bar2v to 
void ()*))
+// REGISTER:   %needs_destruct1 = icmp eq i32 %1, 0
+// REGISTER:   br i1 %needs_destruct1, label %destruct.call2, label 
%destruct.end
+
+// REGISTER: destruct.call2:
+// REGISTER:   call void bitcast (i32 ()* @_Z4bar2v to void ()*)()
+// REGISTER:   br label %destruct.end
+
+// REGISTER: destruct.end:
+// REGISTER:   ret void
+// REGISTER: }

diff  --git 
a/clang/test/CodeGenCXX/aix-sinit-register-global-dtors-with-atexit.cpp 
b/clang/test/CodeGenCXX/aix-sinit-register-global-dtors-with-atexit.cpp
deleted file mode 100644
index 4cec83d461ad..000000000000
--- a/clang/test/CodeGenCXX/aix-sinit-register-global-dtors-with-atexit.cpp
+++ /dev/null
@@ -1,14 +0,0 @@
-// RUN: not %clang_cc1 -triple powerpc-ibm-aix-xcoff -S -emit-llvm -x c++ \
-// RUN:     -fregister-global-dtors-with-atexit < %s 2>&1 | \
-// RUN:   FileCheck %s
-
-// RUN: not %clang_cc1 -triple powerpc64-ibm-aix-xcoff -S -emit-llvm -x c++ \
-// RUN:     -fregister-global-dtors-with-atexit < %s 2>&1 | \
-// RUN:   FileCheck %s
-
-struct T {
-  T();
-  ~T();
-} t;
-
-// CHECK: error in backend: register global dtors with atexit() is not 
supported yet


        
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to