forked from OSchip/llvm-project
Use atexit when __cxa_atexit isn't available instead of adding a
global destructor entry. For some reason this isn't enabled for apple-kexts; it'd be good to have documentation for that. Based on a patch by Nakamura Takumi! llvm-svn: 154191
This commit is contained in:
parent
ee08c53478
commit
76cc43a2a4
|
@ -145,37 +145,109 @@ void CodeGenFunction::EmitCXXGlobalVarDeclInit(const VarDecl &D,
|
|||
EmitStoreOfScalar(RV.getScalarVal(), DeclPtr, false, Alignment, T);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenFunction::EmitCXXGlobalDtorRegistration(llvm::Constant *DtorFn,
|
||||
llvm::Constant *DeclPtr) {
|
||||
// Generate a global destructor entry if not using __cxa_atexit.
|
||||
if (!CGM.getCodeGenOpts().CXAAtExit) {
|
||||
CGM.AddCXXDtorEntry(DtorFn, DeclPtr);
|
||||
/// Register a global destructor using __cxa_atexit.
|
||||
static void emitGlobalDtorWithCXAAtExit(CodeGenFunction &CGF,
|
||||
llvm::Constant *dtor,
|
||||
llvm::Constant *addr) {
|
||||
// We're assuming that the destructor function is something we can
|
||||
// reasonably call with the default CC. Go ahead and cast it to the
|
||||
// right prototype.
|
||||
llvm::Type *dtorTy =
|
||||
llvm::FunctionType::get(CGF.VoidTy, CGF.Int8PtrTy, false)->getPointerTo();
|
||||
|
||||
// extern "C" int __cxa_atexit(void (*f)(void *), void *p, void *d);
|
||||
llvm::Type *paramTys[] = { dtorTy, CGF.Int8PtrTy, CGF.Int8PtrTy };
|
||||
llvm::FunctionType *atexitTy =
|
||||
llvm::FunctionType::get(CGF.IntTy, paramTys, false);
|
||||
|
||||
// Fetch the actual function.
|
||||
llvm::Constant *atexit =
|
||||
CGF.CGM.CreateRuntimeFunction(atexitTy, "__cxa_atexit");
|
||||
if (llvm::Function *fn = dyn_cast<llvm::Function>(atexit))
|
||||
fn->setDoesNotThrow();
|
||||
|
||||
// Create a variable that binds the atexit to this shared object.
|
||||
llvm::Constant *handle =
|
||||
CGF.CGM.CreateRuntimeVariable(CGF.Int8Ty, "__dso_handle");
|
||||
|
||||
llvm::Value *args[] = {
|
||||
llvm::ConstantExpr::getBitCast(dtor, dtorTy),
|
||||
llvm::ConstantExpr::getBitCast(addr, CGF.Int8PtrTy),
|
||||
handle
|
||||
};
|
||||
CGF.Builder.CreateCall(atexit, args);
|
||||
}
|
||||
|
||||
static llvm::Function *
|
||||
CreateGlobalInitOrDestructFunction(CodeGenModule &CGM,
|
||||
llvm::FunctionType *ty,
|
||||
const Twine &name);
|
||||
|
||||
/// Create a stub function, suitable for being passed to atexit,
|
||||
/// which passes the given address to the given destructor function.
|
||||
static llvm::Constant *createAtExitStub(CodeGenModule &CGM,
|
||||
llvm::Constant *dtor,
|
||||
llvm::Constant *addr) {
|
||||
// Get the destructor function type, void(*)(void).
|
||||
llvm::FunctionType *ty = llvm::FunctionType::get(CGM.VoidTy, false);
|
||||
llvm::Function *fn =
|
||||
CreateGlobalInitOrDestructFunction(CGM, ty,
|
||||
Twine("__dtor_", addr->getName()));
|
||||
|
||||
CodeGenFunction CGF(CGM);
|
||||
|
||||
CGF.StartFunction(GlobalDecl(), CGM.getContext().VoidTy, fn,
|
||||
CGM.getTypes().arrangeNullaryFunction(),
|
||||
FunctionArgList(), SourceLocation());
|
||||
|
||||
llvm::CallInst *call = CGF.Builder.CreateCall(dtor, addr);
|
||||
|
||||
// Make sure the call and the callee agree on calling convention.
|
||||
if (llvm::Function *dtorFn =
|
||||
dyn_cast<llvm::Function>(dtor->stripPointerCasts()))
|
||||
call->setCallingConv(dtorFn->getCallingConv());
|
||||
|
||||
CGF.FinishFunction();
|
||||
|
||||
return fn;
|
||||
}
|
||||
|
||||
/// Register a global destructor using atexit.
|
||||
static void emitGlobalDtorWithAtExit(CodeGenFunction &CGF,
|
||||
llvm::Constant *dtor,
|
||||
llvm::Constant *addr) {
|
||||
// Create a function which calls the destructor.
|
||||
llvm::Constant *dtorStub = createAtExitStub(CGF.CGM, dtor, addr);
|
||||
|
||||
// extern "C" int atexit(void (*f)(void));
|
||||
llvm::FunctionType *atexitTy =
|
||||
llvm::FunctionType::get(CGF.IntTy, dtorStub->getType(), false);
|
||||
|
||||
llvm::Constant *atexit =
|
||||
CGF.CGM.CreateRuntimeFunction(atexitTy, "atexit");
|
||||
if (llvm::Function *atexitFn = dyn_cast<llvm::Function>(atexit))
|
||||
atexitFn->setDoesNotThrow();
|
||||
|
||||
CGF.Builder.CreateCall(atexit, dtorStub);
|
||||
}
|
||||
|
||||
void CodeGenFunction::EmitCXXGlobalDtorRegistration(llvm::Constant *dtor,
|
||||
llvm::Constant *addr) {
|
||||
// Use __cxa_atexit if available.
|
||||
if (CGM.getCodeGenOpts().CXAAtExit) {
|
||||
emitGlobalDtorWithCXAAtExit(*this, dtor, addr);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the destructor function type
|
||||
llvm::Type *DtorFnTy = llvm::FunctionType::get(VoidTy, Int8PtrTy, false);
|
||||
DtorFnTy = llvm::PointerType::getUnqual(DtorFnTy);
|
||||
// In Apple kexts, we want to add a global destructor entry.
|
||||
// FIXME: shouldn't this be guarded by some variable?
|
||||
if (CGM.getContext().getLangOpts().AppleKext) {
|
||||
// Generate a global destructor entry.
|
||||
CGM.AddCXXDtorEntry(dtor, addr);
|
||||
}
|
||||
|
||||
llvm::Type *Params[] = { DtorFnTy, Int8PtrTy, Int8PtrTy };
|
||||
|
||||
// Get the __cxa_atexit function type
|
||||
// extern "C" int __cxa_atexit ( void (*f)(void *), void *p, void *d );
|
||||
llvm::FunctionType *AtExitFnTy =
|
||||
llvm::FunctionType::get(ConvertType(getContext().IntTy), Params, false);
|
||||
|
||||
llvm::Constant *AtExitFn = CGM.CreateRuntimeFunction(AtExitFnTy,
|
||||
"__cxa_atexit");
|
||||
if (llvm::Function *Fn = dyn_cast<llvm::Function>(AtExitFn))
|
||||
Fn->setDoesNotThrow();
|
||||
|
||||
llvm::Constant *Handle = CGM.CreateRuntimeVariable(Int8PtrTy,
|
||||
"__dso_handle");
|
||||
llvm::Value *Args[3] = { llvm::ConstantExpr::getBitCast(DtorFn, DtorFnTy),
|
||||
llvm::ConstantExpr::getBitCast(DeclPtr, Int8PtrTy),
|
||||
llvm::ConstantExpr::getBitCast(Handle, Int8PtrTy) };
|
||||
Builder.CreateCall(AtExitFn, Args);
|
||||
// Otherwise, we just use atexit.
|
||||
emitGlobalDtorWithAtExit(*this, dtor, addr);
|
||||
}
|
||||
|
||||
void CodeGenFunction::EmitCXXGuardedInit(const VarDecl &D,
|
||||
|
|
|
@ -20,9 +20,17 @@ public:
|
|||
|
||||
// The global dtor needs the right calling conv with -fno-use-cxa-atexit
|
||||
// rdar://7817590
|
||||
// Checked at end of file.
|
||||
bar baz;
|
||||
|
||||
// PR9593
|
||||
// Make sure atexit(3) is used for global dtors.
|
||||
|
||||
// CHECK: call [[BAR:%.*]]* @_ZN3barC1Ev(
|
||||
// CHECK-NEXT: call i32 @atexit(void ()* @__dtor_baz)
|
||||
|
||||
// CHECK: define internal void @__dtor_baz()
|
||||
// CHECK: call [[BAR]]* @_ZN3barD1Ev([[BAR]]* @baz)
|
||||
|
||||
// Destructors and constructors must return this.
|
||||
namespace test1 {
|
||||
void foo();
|
||||
|
@ -357,5 +365,5 @@ namespace test8 {
|
|||
// CHECK: call void @_ZN5test21CD0Ev(
|
||||
// CHECK: ret void
|
||||
|
||||
// CHECK: @_GLOBAL__D_a()
|
||||
// CHECK: call %class.bar* @_ZN3barD1Ev(%class.bar* @baz)
|
||||
// CH_ECK: @_GLOBAL__D_a()
|
||||
// CH_ECK: call %class.bar* @_ZN3barD1Ev(%class.bar* @baz)
|
||||
|
|
|
@ -3,10 +3,15 @@
|
|||
// PR7097
|
||||
// RUN: %clang_cc1 -triple x86_64 %s -fno-use-cxa-atexit -mconstructor-aliases -emit-llvm -o - | FileCheck %s
|
||||
|
||||
// CHECK: define internal void @_GLOBAL__D_a()
|
||||
// CHECK: call void @_ZN1AD1Ev(%class.A* @b)
|
||||
// CHECK: call void @_ZN1AD1Ev(%class.A* @a)
|
||||
// CHECK: }
|
||||
// CHECK: call void @_ZN1AC1Ev([[A:%.*]]* @a)
|
||||
// CHECK-NEXT: call i32 @atexit(void ()* @__dtor_a)
|
||||
// CHECK: define internal void @__dtor_a() nounwind
|
||||
// CHECK: call void @_ZN1AD1Ev([[A]]* @a)
|
||||
|
||||
// CHECK: call void @_ZN1AC1Ev([[A]]* @b)
|
||||
// CHECK-NEXT: call i32 @atexit(void ()* @__dtor_b)
|
||||
// CHECK: define internal void @__dtor_b() nounwind
|
||||
// CHECK: call void @_ZN1AD1Ev([[A]]* @b)
|
||||
|
||||
class A {
|
||||
public:
|
||||
|
@ -15,3 +20,25 @@ public:
|
|||
};
|
||||
|
||||
A a, b;
|
||||
|
||||
// PR9593
|
||||
// CHECK: define void @_Z4funcv()
|
||||
// CHECK: call i32 @__cxa_guard_acquire(i64* @_ZGVZ4funcvE2a1)
|
||||
// CHECK: call void @_ZN1AC1Ev([[A]]* @_ZZ4funcvE2a1)
|
||||
// CHECK-NEXT: call i32 @atexit(void ()* @__dtor__ZZ4funcvE2a1)
|
||||
// CHECK-NEXT: call void @__cxa_guard_release(i64* @_ZGVZ4funcvE2a1)
|
||||
|
||||
// CHECK: call i32 @__cxa_guard_acquire(i64* @_ZGVZ4funcvE2a2)
|
||||
// CHECK: call void @_ZN1AC1Ev([[A]]* @_ZZ4funcvE2a2)
|
||||
// CHECK-NEXT: call i32 @atexit(void ()* @__dtor__ZZ4funcvE2a2)
|
||||
// CHECK-NEXT: call void @__cxa_guard_release(i64* @_ZGVZ4funcvE2a2)
|
||||
|
||||
// CHECK: define internal void @__dtor__ZZ4funcvE2a1() nounwind
|
||||
// CHECK: call void @_ZN1AD1Ev([[A]]* @_ZZ4funcvE2a1)
|
||||
|
||||
// CHECK: define internal void @__dtor__ZZ4funcvE2a2() nounwind
|
||||
// CHECK: call void @_ZN1AD1Ev([[A]]* @_ZZ4funcvE2a2)
|
||||
|
||||
void func() {
|
||||
static A a1, a2;
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ struct C { void *field; };
|
|||
|
||||
struct D { ~D(); };
|
||||
|
||||
// CHECK: @__dso_handle = external unnamed_addr global i8*
|
||||
// CHECK: @__dso_handle = external unnamed_addr global i8
|
||||
// CHECK: @c = global %struct.C zeroinitializer, align 8
|
||||
|
||||
// It's okay if we ever implement the IR-generation optimization to remove this.
|
||||
|
@ -24,18 +24,18 @@ struct D { ~D(); };
|
|||
// CHECK: @_ZN6PR59741bE = global %"struct.PR5974::B"* bitcast (i8* getelementptr (i8* bitcast (%"struct.PR5974::C"* @_ZN6PR59741cE to i8*), i64 4) to %"struct.PR5974::B"*), align 8
|
||||
|
||||
// CHECK: call void @_ZN1AC1Ev(%struct.A* @a)
|
||||
// CHECK: call i32 @__cxa_atexit(void (i8*)* bitcast (void (%struct.A*)* @_ZN1AD1Ev to void (i8*)*), i8* getelementptr inbounds (%struct.A* @a, i32 0, i32 0), i8* bitcast (i8** @__dso_handle to i8*))
|
||||
// CHECK: call i32 @__cxa_atexit(void (i8*)* bitcast (void (%struct.A*)* @_ZN1AD1Ev to void (i8*)*), i8* getelementptr inbounds (%struct.A* @a, i32 0, i32 0), i8* @__dso_handle)
|
||||
A a;
|
||||
|
||||
// CHECK: call void @_ZN1BC1Ev(%struct.B* @b)
|
||||
// CHECK: call i32 @__cxa_atexit(void (i8*)* bitcast (void (%struct.B*)* @_ZN1BD1Ev to void (i8*)*), i8* getelementptr inbounds (%struct.B* @b, i32 0, i32 0), i8* bitcast (i8** @__dso_handle to i8*))
|
||||
// CHECK: call i32 @__cxa_atexit(void (i8*)* bitcast (void (%struct.B*)* @_ZN1BD1Ev to void (i8*)*), i8* getelementptr inbounds (%struct.B* @b, i32 0, i32 0), i8* @__dso_handle)
|
||||
B b;
|
||||
|
||||
// PR6205: this should not require a global initializer
|
||||
// CHECK-NOT: call void @_ZN1CC1Ev(%struct.C* @c)
|
||||
C c;
|
||||
|
||||
// CHECK: call i32 @__cxa_atexit(void (i8*)* bitcast (void (%struct.D*)* @_ZN1DD1Ev to void (i8*)*), i8* getelementptr inbounds (%struct.D* @d, i32 0, i32 0), i8* bitcast (i8** @__dso_handle to i8*))
|
||||
// CHECK: call i32 @__cxa_atexit(void (i8*)* bitcast (void (%struct.D*)* @_ZN1DD1Ev to void (i8*)*), i8* getelementptr inbounds (%struct.D* @d, i32 0, i32 0), i8* @__dso_handle)
|
||||
D d;
|
||||
|
||||
// <rdar://problem/7458115>
|
||||
|
|
|
@ -17,7 +17,7 @@ void f() {
|
|||
// CHECK: load atomic i8* bitcast (i64* @_ZGVZ1fvE1a to i8*) acquire, align 1
|
||||
// CHECK: call i32 @__cxa_guard_acquire
|
||||
// CHECK: call void @_ZN1AC1Ev
|
||||
// CHECK: call i32 @__cxa_atexit(void (i8*)* bitcast (void (%struct.A*)* @_ZN1AD1Ev to void (i8*)*), i8* getelementptr inbounds (%struct.A* @_ZZ1fvE1a, i32 0, i32 0), i8* bitcast (i8** @__dso_handle to i8*))
|
||||
// CHECK: call i32 @__cxa_atexit(void (i8*)* bitcast (void (%struct.A*)* @_ZN1AD1Ev to void (i8*)*), i8* getelementptr inbounds (%struct.A* @_ZZ1fvE1a, i32 0, i32 0), i8* @__dso_handle)
|
||||
// CHECK: call void @__cxa_guard_release
|
||||
static A a;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue