forked from OSchip/llvm-project
[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:
parent
9e39a5d9a6
commit
17497ec514
|
@ -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 =
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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: }
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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: }
|
|
@ -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
|
Loading…
Reference in New Issue