[AIX][FE] Support constructor/destructor attribute

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

Differential Revision: https://reviews.llvm.org/D90892
This commit is contained in:
Xiangling Liao 2020-11-19 08:58:38 -05:00
parent 9e39a5d9a6
commit 17497ec514
13 changed files with 359 additions and 98 deletions

View File

@ -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 =

View File

@ -4304,7 +4304,7 @@ public:
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

View File

@ -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);
}

View File

@ -1478,7 +1478,8 @@ private:
// 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 @@ private:
/// __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.

View File

@ -2530,48 +2530,132 @@ static void emitGlobalDtorWithCXAAtExit(CodeGenFunction &CGF,
CGF.EmitNounwindRuntimeCall(atexit, args);
}
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;
const llvm::TinyPtrVector<llvm::Function *> &Dtors = I.second;
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.

View File

@ -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:

View File

@ -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;
}

View File

@ -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

View File

@ -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: }

View File

@ -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

View File

@ -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;
}

View File

@ -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: }

View File

@ -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