[clang] 17497ec - [AIX][FE] Support constructor/destructor attribute
Xiangling Liao via cfe-commits
cfe-commits at lists.llvm.org
Thu Nov 19 06:24:45 PST 2020
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
More information about the cfe-commits
mailing list