From 5cd91b513455fd7753e8815b54f0a49bbca6602d Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 8 Sep 2010 01:44:27 +0000 Subject: [PATCH] Implement ARM static local initialization guards, which are more compact than Itanium guards and use a slightly different compiled-in API. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@113330 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/CGCXX.cpp | 8 +- lib/CodeGen/CGCXXABI.h | 12 +- lib/CodeGen/CGDecl.cpp | 6 +- lib/CodeGen/CGDeclCXX.cpp | 144 +--------------- lib/CodeGen/CodeGenFunction.h | 7 +- lib/CodeGen/ItaniumCXXABI.cpp | 159 ++++++++++++++++++ lib/CodeGen/Mangle.cpp | 4 +- lib/CodeGen/Mangle.h | 6 +- lib/CodeGen/MicrosoftCXXABI.cpp | 6 - test/CodeGenCXX/arm.cpp | 75 +++++++++ test/CodeGenCXX/static-init.cpp | 2 +- .../threadsafe-statics-exceptions.cpp | 4 +- 12 files changed, 273 insertions(+), 160 deletions(-) diff --git a/lib/CodeGen/CGCXX.cpp b/lib/CodeGen/CGCXX.cpp index 179716f631..3dd5a75856 100644 --- a/lib/CodeGen/CGCXX.cpp +++ b/lib/CodeGen/CGCXX.cpp @@ -321,7 +321,7 @@ CodeGenFunction::BuildVirtualCall(const CXXDestructorDecl *DD, CXXDtorType Type, /// Implementation for CGCXXABI. Possibly this should be moved into /// the incomplete ABI implementations? -CGCXXABI::~CGCXXABI() {} +void CGCXXABI::_anchor() {} static void ErrorUnsupportedABI(CodeGenFunction &CGF, llvm::StringRef S) { @@ -469,3 +469,9 @@ void CGCXXABI::ReadArrayCookie(CodeGenFunction &CGF, llvm::Value *Ptr, AllocPtr = llvm::Constant::getNullValue(CGF.Builder.getInt8PtrTy()); CookieSize = CharUnits::Zero(); } + +void CGCXXABI::EmitStaticLocalInit(CodeGenFunction &CGF, + const VarDecl &D, + llvm::GlobalVariable *GV) { + ErrorUnsupportedABI(CGF, "static local variable initialization"); +} diff --git a/lib/CodeGen/CGCXXABI.h b/lib/CodeGen/CGCXXABI.h index 367e345918..b99216b786 100644 --- a/lib/CodeGen/CGCXXABI.h +++ b/lib/CodeGen/CGCXXABI.h @@ -69,9 +69,11 @@ protected: ASTContext &getContext() const { return CGM.getContext(); } + virtual void _anchor(); + public: - virtual ~CGCXXABI(); + virtual ~CGCXXABI() {} /// Gets the mangle context. virtual MangleContext &getMangleContext() = 0; @@ -217,6 +219,14 @@ public: QualType ElementType, llvm::Value *&NumElements, llvm::Value *&AllocPtr, CharUnits &CookieSize); + /*************************** Static local guards ****************************/ + + /// Emits the initializer and destructor setup for the given static + /// local variable, given that it's reachable and couldn't be + /// emitted as a constant. + virtual void EmitStaticLocalInit(CodeGenFunction &CGF, const VarDecl &D, + llvm::GlobalVariable *DeclPtr); + }; /// Creates an instance of a C++ ABI class. diff --git a/lib/CodeGen/CGDecl.cpp b/lib/CodeGen/CGDecl.cpp index 97c0341087..965400135e 100644 --- a/lib/CodeGen/CGDecl.cpp +++ b/lib/CodeGen/CGDecl.cpp @@ -189,12 +189,12 @@ CodeGenFunction::AddInitializerToGlobalBlockVarDecl(const VarDecl &D, if (!Init) { if (!getContext().getLangOptions().CPlusPlus) CGM.ErrorUnsupported(D.getInit(), "constant l-value expression"); - else { + else if (Builder.GetInsertBlock()) { // Since we have a static initializer, this global variable can't // be constant. GV->setConstant(false); - - EmitStaticCXXBlockVarDeclInit(D, GV); + + EmitCXXStaticLocalInit(D, GV); } return GV; } diff --git a/lib/CodeGen/CGDeclCXX.cpp b/lib/CodeGen/CGDeclCXX.cpp index e2f197522b..565c865c34 100644 --- a/lib/CodeGen/CGDeclCXX.cpp +++ b/lib/CodeGen/CGDeclCXX.cpp @@ -42,6 +42,8 @@ static void EmitDeclInit(CodeGenFunction &CGF, const VarDecl &D, } } +/// Emit code to cause the destruction of the given variable with +/// static storage duration. static void EmitDeclDestroy(CodeGenFunction &CGF, const VarDecl &D, llvm::Constant *DeclPtr) { CodeGenModule &CGM = CGF.CGM; @@ -138,6 +140,11 @@ CodeGenFunction::EmitCXXGlobalDtorRegistration(llvm::Constant *DtorFn, Builder.CreateCall(AtExitFn, &Args[0], llvm::array_endof(Args)); } +void CodeGenFunction::EmitCXXStaticLocalInit(const VarDecl &D, + llvm::GlobalVariable *DeclPtr) { + CGM.getCXXABI().EmitStaticLocalInit(*this, D, DeclPtr); +} + static llvm::Function * CreateGlobalInitOrDestructFunction(CodeGenModule &CGM, const llvm::FunctionType *FTy, @@ -283,143 +290,6 @@ void CodeGenFunction::GenerateCXXGlobalDtorFunc(llvm::Function *Fn, FinishFunction(); } -static llvm::Constant *getGuardAcquireFn(CodeGenFunction &CGF) { - // int __cxa_guard_acquire(__int64_t *guard_object); - - const llvm::Type *Int64PtrTy = - llvm::Type::getInt64PtrTy(CGF.getLLVMContext()); - - std::vector Args(1, Int64PtrTy); - - const llvm::FunctionType *FTy = - llvm::FunctionType::get(CGF.ConvertType(CGF.getContext().IntTy), - Args, /*isVarArg=*/false); - - return CGF.CGM.CreateRuntimeFunction(FTy, "__cxa_guard_acquire"); -} - -static llvm::Constant *getGuardReleaseFn(CodeGenFunction &CGF) { - // void __cxa_guard_release(__int64_t *guard_object); - - const llvm::Type *Int64PtrTy = - llvm::Type::getInt64PtrTy(CGF.getLLVMContext()); - - std::vector Args(1, Int64PtrTy); - - const llvm::FunctionType *FTy = - llvm::FunctionType::get(llvm::Type::getVoidTy(CGF.getLLVMContext()), - Args, /*isVarArg=*/false); - - return CGF.CGM.CreateRuntimeFunction(FTy, "__cxa_guard_release"); -} - -static llvm::Constant *getGuardAbortFn(CodeGenFunction &CGF) { - // void __cxa_guard_abort(__int64_t *guard_object); - - const llvm::Type *Int64PtrTy = - llvm::Type::getInt64PtrTy(CGF.getLLVMContext()); - - std::vector Args(1, Int64PtrTy); - - const llvm::FunctionType *FTy = - llvm::FunctionType::get(llvm::Type::getVoidTy(CGF.getLLVMContext()), - Args, /*isVarArg=*/false); - - return CGF.CGM.CreateRuntimeFunction(FTy, "__cxa_guard_abort"); -} - -namespace { - struct CallGuardAbort : EHScopeStack::Cleanup { - llvm::GlobalVariable *Guard; - CallGuardAbort(llvm::GlobalVariable *Guard) : Guard(Guard) {} - - void Emit(CodeGenFunction &CGF, bool IsForEH) { - // It shouldn't be possible for this to throw, but if it can, - // this should allow for the possibility of an invoke. - CGF.Builder.CreateCall(getGuardAbortFn(CGF), Guard) - ->setDoesNotThrow(); - } - }; -} - -void -CodeGenFunction::EmitStaticCXXBlockVarDeclInit(const VarDecl &D, - llvm::GlobalVariable *GV) { - // Bail out early if this initializer isn't reachable. - if (!Builder.GetInsertBlock()) return; - - bool ThreadsafeStatics = getContext().getLangOptions().ThreadsafeStatics; - - llvm::SmallString<256> GuardVName; - CGM.getCXXABI().getMangleContext().mangleGuardVariable(&D, GuardVName); - - // Create the guard variable. - llvm::GlobalVariable *GuardVariable = - new llvm::GlobalVariable(CGM.getModule(), Int64Ty, - false, GV->getLinkage(), - llvm::Constant::getNullValue(Int64Ty), - GuardVName.str()); - - // Load the first byte of the guard variable. - const llvm::Type *PtrTy - = llvm::PointerType::get(llvm::Type::getInt8Ty(VMContext), 0); - llvm::Value *V = - Builder.CreateLoad(Builder.CreateBitCast(GuardVariable, PtrTy), "tmp"); - - llvm::BasicBlock *InitCheckBlock = createBasicBlock("init.check"); - llvm::BasicBlock *EndBlock = createBasicBlock("init.end"); - - // Check if the first byte of the guard variable is zero. - Builder.CreateCondBr(Builder.CreateIsNull(V, "tobool"), - InitCheckBlock, EndBlock); - - EmitBlock(InitCheckBlock); - - // Variables used when coping with thread-safe statics and exceptions. - if (ThreadsafeStatics) { - // Call __cxa_guard_acquire. - V = Builder.CreateCall(getGuardAcquireFn(*this), GuardVariable); - - llvm::BasicBlock *InitBlock = createBasicBlock("init"); - - Builder.CreateCondBr(Builder.CreateIsNotNull(V, "tobool"), - InitBlock, EndBlock); - - // Call __cxa_guard_abort along the exceptional edge. - if (Exceptions) - EHStack.pushCleanup(EHCleanup, GuardVariable); - - EmitBlock(InitBlock); - } - - if (D.getType()->isReferenceType()) { - unsigned Alignment = getContext().getDeclAlign(&D).getQuantity(); - QualType T = D.getType(); - RValue RV = EmitReferenceBindingToExpr(D.getInit(), &D); - EmitStoreOfScalar(RV.getScalarVal(), GV, /*Volatile=*/false, Alignment, T); - } else - EmitDeclInit(*this, D, GV); - - if (ThreadsafeStatics) { - // Pop the guard-abort cleanup if we pushed one. - if (Exceptions) - PopCleanupBlock(); - - // Call __cxa_guard_release. This cannot throw. - Builder.CreateCall(getGuardReleaseFn(*this), GuardVariable); - } else { - llvm::Value *One = - llvm::ConstantInt::get(llvm::Type::getInt8Ty(VMContext), 1); - Builder.CreateStore(One, Builder.CreateBitCast(GuardVariable, PtrTy)); - } - - // Register the call to the destructor. - if (!D.getType()->isReferenceType()) - EmitDeclDestroy(*this, D, GV); - - EmitBlock(EndBlock); -} - /// GenerateCXXAggrDestructorHelper - Generates a helper function which when /// invoked, calls the default destructor on array elements in reverse order of /// construction. diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index 4f0420536a..37795ed5db 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/lib/CodeGen/CodeGenFunction.h @@ -1565,11 +1565,6 @@ public: llvm::GlobalVariable *GV); - /// EmitStaticCXXBlockVarDeclInit - Create the initializer for a C++ runtime - /// initialized static block var decl. - void EmitStaticCXXBlockVarDeclInit(const VarDecl &D, - llvm::GlobalVariable *GV); - /// EmitCXXGlobalVarDeclInit - Create the initializer for a C++ /// variable with global storage. void EmitCXXGlobalVarDeclInit(const VarDecl &D, llvm::Constant *DeclPtr); @@ -1579,6 +1574,8 @@ public: void EmitCXXGlobalDtorRegistration(llvm::Constant *DtorFn, llvm::Constant *DeclPtr); + void EmitCXXStaticLocalInit(const VarDecl &D, llvm::GlobalVariable *DeclPtr); + /// GenerateCXXGlobalInitFunc - Generates code for initializing global /// variables. void GenerateCXXGlobalInitFunc(llvm::Function *Fn, diff --git a/lib/CodeGen/ItaniumCXXABI.cpp b/lib/CodeGen/ItaniumCXXABI.cpp index eefc530ccf..4d0e05da4a 100644 --- a/lib/CodeGen/ItaniumCXXABI.cpp +++ b/lib/CodeGen/ItaniumCXXABI.cpp @@ -119,6 +119,9 @@ public: void ReadArrayCookie(CodeGenFunction &CGF, llvm::Value *Ptr, QualType ElementType, llvm::Value *&NumElements, llvm::Value *&AllocPtr, CharUnits &CookieSize); + + void EmitStaticLocalInit(CodeGenFunction &CGF, const VarDecl &D, + llvm::GlobalVariable *DeclPtr); }; class ARMCXXABI : public ItaniumCXXABI { @@ -1021,3 +1024,159 @@ void ARMCXXABI::ReadArrayCookie(CodeGenFunction &CGF, NumElements = CGF.Builder.CreateLoad(NumElementsPtr); } +/*********************** Static local initialization **************************/ + +static llvm::Constant *getGuardAcquireFn(CodeGenModule &CGM, + const llvm::PointerType *GuardPtrTy) { + // int __cxa_guard_acquire(__guard *guard_object); + + std::vector Args(1, GuardPtrTy); + const llvm::FunctionType *FTy = + llvm::FunctionType::get(CGM.getTypes().ConvertType(CGM.getContext().IntTy), + Args, /*isVarArg=*/false); + + return CGM.CreateRuntimeFunction(FTy, "__cxa_guard_acquire"); +} + +static llvm::Constant *getGuardReleaseFn(CodeGenModule &CGM, + const llvm::PointerType *GuardPtrTy) { + // void __cxa_guard_release(__guard *guard_object); + + std::vector Args(1, GuardPtrTy); + + const llvm::FunctionType *FTy = + llvm::FunctionType::get(llvm::Type::getVoidTy(CGM.getLLVMContext()), + Args, /*isVarArg=*/false); + + return CGM.CreateRuntimeFunction(FTy, "__cxa_guard_release"); +} + +static llvm::Constant *getGuardAbortFn(CodeGenModule &CGM, + const llvm::PointerType *GuardPtrTy) { + // void __cxa_guard_abort(__guard *guard_object); + + std::vector Args(1, GuardPtrTy); + + const llvm::FunctionType *FTy = + llvm::FunctionType::get(llvm::Type::getVoidTy(CGM.getLLVMContext()), + Args, /*isVarArg=*/false); + + return CGM.CreateRuntimeFunction(FTy, "__cxa_guard_abort"); +} + +namespace { + struct CallGuardAbort : EHScopeStack::Cleanup { + llvm::GlobalVariable *Guard; + CallGuardAbort(llvm::GlobalVariable *Guard) : Guard(Guard) {} + + void Emit(CodeGenFunction &CGF, bool IsForEH) { + CGF.Builder.CreateCall(getGuardAbortFn(CGF.CGM, Guard->getType()), Guard) + ->setDoesNotThrow(); + } + }; +} + +/// The ARM code here follows the Itanium code closely enough that we +/// just special-case it at particular places. +void ItaniumCXXABI::EmitStaticLocalInit(CodeGenFunction &CGF, + const VarDecl &D, + llvm::GlobalVariable *GV) { + CGBuilderTy &Builder = CGF.Builder; + bool ThreadsafeStatics = getContext().getLangOptions().ThreadsafeStatics; + + // Guard variables are 64 bits in the generic ABI and 32 bits on ARM. + const llvm::IntegerType *GuardTy + = (IsARM ? Builder.getInt32Ty() : Builder.getInt64Ty()); + const llvm::PointerType *GuardPtrTy = GuardTy->getPointerTo(); + + // Create the guard variable. + llvm::SmallString<256> GuardVName; + getMangleContext().mangleItaniumGuardVariable(&D, GuardVName); + llvm::GlobalVariable *GuardVariable = + new llvm::GlobalVariable(CGM.getModule(), GuardTy, + false, GV->getLinkage(), + llvm::ConstantInt::get(GuardTy, 0), + GuardVName.str()); + + // Test whether the variable has completed initialization. + llvm::Value *IsInitialized; + + // ARM C++ ABI 3.2.3.1: + // To support the potential use of initialization guard variables + // as semaphores that are the target of ARM SWP and LDREX/STREX + // synchronizing instructions we define a static initialization + // guard variable to be a 4-byte aligned, 4- byte word with the + // following inline access protocol. + // #define INITIALIZED 1 + // if ((obj_guard & INITIALIZED) != INITIALIZED) { + // if (__cxa_guard_acquire(&obj_guard)) + // ... + // } + if (IsARM) { + llvm::Value *V = Builder.CreateLoad(GuardVariable); + V = Builder.CreateAnd(V, Builder.getInt32(1)); + IsInitialized = Builder.CreateIsNull(V, "guard.uninitialized"); + + // Itanium C++ ABI 3.3.2: + // The following is pseudo-code showing how these functions can be used: + // if (obj_guard.first_byte == 0) { + // if ( __cxa_guard_acquire (&obj_guard) ) { + // try { + // ... initialize the object ...; + // } catch (...) { + // __cxa_guard_abort (&obj_guard); + // throw; + // } + // ... queue object destructor with __cxa_atexit() ...; + // __cxa_guard_release (&obj_guard); + // } + // } + } else { + // Load the first byte of the guard variable. + const llvm::Type *PtrTy = Builder.getInt8PtrTy(); + llvm::Value *V = + Builder.CreateLoad(Builder.CreateBitCast(GuardVariable, PtrTy), "tmp"); + + IsInitialized = Builder.CreateIsNull(V, "guard.uninitialized"); + } + + llvm::BasicBlock *InitCheckBlock = CGF.createBasicBlock("init.check"); + llvm::BasicBlock *EndBlock = CGF.createBasicBlock("init.end"); + + // Check if the first byte of the guard variable is zero. + Builder.CreateCondBr(IsInitialized, InitCheckBlock, EndBlock); + + CGF.EmitBlock(InitCheckBlock); + + // Variables used when coping with thread-safe statics and exceptions. + if (ThreadsafeStatics) { + // Call __cxa_guard_acquire. + llvm::Value *V + = Builder.CreateCall(getGuardAcquireFn(CGM, GuardPtrTy), GuardVariable); + + llvm::BasicBlock *InitBlock = CGF.createBasicBlock("init"); + + Builder.CreateCondBr(Builder.CreateIsNotNull(V, "tobool"), + InitBlock, EndBlock); + + // Call __cxa_guard_abort along the exceptional edge. + CGF.EHStack.pushCleanup(EHCleanup, GuardVariable); + + CGF.EmitBlock(InitBlock); + } + + // Emit the initializer and add a global destructor if appropriate. + CGF.EmitCXXGlobalVarDeclInit(D, GV); + + if (ThreadsafeStatics) { + // Pop the guard-abort cleanup if we pushed one. + CGF.PopCleanupBlock(); + + // Call __cxa_guard_release. This cannot throw. + Builder.CreateCall(getGuardReleaseFn(CGM, GuardPtrTy), GuardVariable); + } else { + Builder.CreateStore(llvm::ConstantInt::get(GuardTy, 1), GuardVariable); + } + + CGF.EmitBlock(EndBlock); +} diff --git a/lib/CodeGen/Mangle.cpp b/lib/CodeGen/Mangle.cpp index 75ce44d82b..b836f016ff 100644 --- a/lib/CodeGen/Mangle.cpp +++ b/lib/CodeGen/Mangle.cpp @@ -2451,8 +2451,8 @@ MangleContext::mangleCXXDtorThunk(const CXXDestructorDecl *DD, CXXDtorType Type, /// mangleGuardVariable - Returns the mangled name for a guard variable /// for the passed in VarDecl. -void MangleContext::mangleGuardVariable(const VarDecl *D, - llvm::SmallVectorImpl &Res) { +void MangleContext::mangleItaniumGuardVariable(const VarDecl *D, + llvm::SmallVectorImpl &Res) { // ::= GV # Guard variable for one-time // # initialization CXXNameMangler Mangler(*this, Res); diff --git a/lib/CodeGen/Mangle.h b/lib/CodeGen/Mangle.h index 139f6c0377..402a9b8cc3 100644 --- a/lib/CodeGen/Mangle.h +++ b/lib/CodeGen/Mangle.h @@ -119,8 +119,6 @@ public: virtual void mangleCXXDtorThunk(const CXXDestructorDecl *DD, CXXDtorType Type, const ThisAdjustment &ThisAdjustment, llvm::SmallVectorImpl &); - virtual void mangleGuardVariable(const VarDecl *D, - llvm::SmallVectorImpl &); virtual void mangleReferenceTemporary(const VarDecl *D, llvm::SmallVectorImpl &); virtual void mangleCXXVTable(const CXXRecordDecl *RD, @@ -139,6 +137,10 @@ public: void mangleBlock(GlobalDecl GD, const BlockDecl *BD, llvm::SmallVectorImpl &); + // This is pretty lame. + void mangleItaniumGuardVariable(const VarDecl *D, + llvm::SmallVectorImpl &); + void mangleInitDiscriminator() { Discriminator = 0; } diff --git a/lib/CodeGen/MicrosoftCXXABI.cpp b/lib/CodeGen/MicrosoftCXXABI.cpp index 9407335e32..baa6ed3f82 100644 --- a/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/lib/CodeGen/MicrosoftCXXABI.cpp @@ -91,8 +91,6 @@ public: virtual void mangleCXXDtorThunk(const CXXDestructorDecl *DD, CXXDtorType Type, const ThisAdjustment &ThisAdjustment, llvm::SmallVectorImpl &); - virtual void mangleGuardVariable(const VarDecl *D, - llvm::SmallVectorImpl &); virtual void mangleCXXVTable(const CXXRecordDecl *RD, llvm::SmallVectorImpl &); virtual void mangleCXXVTT(const CXXRecordDecl *RD, @@ -1175,10 +1173,6 @@ void MicrosoftMangleContext::mangleCXXDtorThunk(const CXXDestructorDecl *DD, llvm::SmallVectorImpl &) { assert(false && "Can't yet mangle destructor thunks!"); } -void MicrosoftMangleContext::mangleGuardVariable(const VarDecl *D, - llvm::SmallVectorImpl &) { - assert(false && "Can't yet mangle guard variables!"); -} void MicrosoftMangleContext::mangleCXXVTable(const CXXRecordDecl *RD, llvm::SmallVectorImpl &) { assert(false && "Can't yet mangle virtual tables!"); diff --git a/test/CodeGenCXX/arm.cpp b/test/CodeGenCXX/arm.cpp index 44c0affa5a..f73aa0dad5 100644 --- a/test/CodeGenCXX/arm.cpp +++ b/test/CodeGenCXX/arm.cpp @@ -1,5 +1,10 @@ // RUN: %clang_cc1 %s -triple=thumbv7-apple-darwin3.0.0-iphoneos -fno-use-cxa-atexit -target-abi apcs-gnu -emit-llvm -o - -fexceptions | FileCheck %s +// CHECK: @_ZZN5test74testEvE1x = internal global i32 0, align 4 +// CHECK: @_ZGVZN5test74testEvE1x = internal global i32 0 +// CHECK: @_ZZN5test84testEvE1x = internal global [[TEST8A:.*]] zeroinitializer, align 1 +// CHECK: @_ZGVZN5test84testEvE1x = internal global i32 0 + typedef typeof(sizeof(int)) size_t; class foo { @@ -277,6 +282,76 @@ namespace test6 { } } +namespace test7 { + int foo(); + + // Static and guard tested at top of file + + // CHECK: define void @_ZN5test74testEv() + void test() { + // CHECK: [[T0:%.*]] = load i32* @_ZGVZN5test74testEvE1x + // CHECK-NEXT: [[T1:%.*]] = and i32 [[T0]], 1 + // CHECK-NEXT: [[T2:%.*]] = icmp eq i32 [[T1]], 0 + // CHECK-NEXT: br i1 [[T2]] + // -> fallthrough, end + // CHECK: [[T3:%.*]] = call i32 @__cxa_guard_acquire(i32* @_ZGVZN5test74testEvE1x) + // CHECK-NEXT: [[T4:%.*]] = icmp ne i32 [[T3]], 0 + // CHECK-NEXT: br i1 [[T4]] + // -> fallthrough, end + // CHECK: [[INIT:%.*]] = invoke i32 @_ZN5test73fooEv() + // CHECK: store i32 [[INIT]], i32* @_ZZN5test74testEvE1x, align 4 + // CHECK-NEXT: call void @__cxa_guard_release(i32* @_ZGVZN5test74testEvE1x) + // CHECK-NEXT: br label + // -> end + // end: + // CHECK: ret void + static int x = foo(); + + // CHECK: call i8* @llvm.eh.exception() + // CHECK: call void @__cxa_guard_abort(i32* @_ZGVZN5test74testEvE1x) + // CHECK: call void @_Unwind_Resume_or_Rethrow + } +} + +namespace test8 { + struct A { + A(); + ~A(); + }; + + // Static and guard tested at top of file + + // CHECK: define void @_ZN5test84testEv() + void test() { + // CHECK: [[T0:%.*]] = load i32* @_ZGVZN5test84testEvE1x + // CHECK-NEXT: [[T1:%.*]] = and i32 [[T0]], 1 + // CHECK-NEXT: [[T2:%.*]] = icmp eq i32 [[T1]], 0 + // CHECK-NEXT: br i1 [[T2]] + // -> fallthrough, end + // CHECK: [[T3:%.*]] = call i32 @__cxa_guard_acquire(i32* @_ZGVZN5test84testEvE1x) + // CHECK-NEXT: [[T4:%.*]] = icmp ne i32 [[T3]], 0 + // CHECK-NEXT: br i1 [[T4]] + // -> fallthrough, end + // CHECK: [[INIT:%.*]] = invoke [[TEST8A]]* @_ZN5test81AC1Ev([[TEST8A]]* @_ZZN5test84testEvE1x) + + // FIXME: Here we register a global destructor that + // unconditionally calls the destructor. That's what we've always + // done for -fno-use-cxa-atexit here, but that's really not + // semantically correct at all. + + // CHECK: call void @__cxa_guard_release(i32* @_ZGVZN5test84testEvE1x) + // CHECK-NEXT: br label + // -> end + // end: + // CHECK: ret void + static A x; + + // CHECK: call i8* @llvm.eh.exception() + // CHECK: call void @__cxa_guard_abort(i32* @_ZGVZN5test84testEvE1x) + // CHECK: call void @_Unwind_Resume_or_Rethrow + } +} + // CHECK: define linkonce_odr [[C:%.*]]* @_ZTv0_n12_N5test21CD1Ev( // CHECK: call [[C]]* @_ZN5test21CD1Ev( // CHECK: ret [[C]]* undef diff --git a/test/CodeGenCXX/static-init.cpp b/test/CodeGenCXX/static-init.cpp index 09b398a530..dd9ed61434 100644 --- a/test/CodeGenCXX/static-init.cpp +++ b/test/CodeGenCXX/static-init.cpp @@ -14,8 +14,8 @@ struct A { void f() { // CHECK: call i32 @__cxa_guard_acquire // CHECK: call void @_ZN1AC1Ev - // CHECK: call void @__cxa_guard_release // 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 void @__cxa_guard_release static A a; } diff --git a/test/CodeGenCXX/threadsafe-statics-exceptions.cpp b/test/CodeGenCXX/threadsafe-statics-exceptions.cpp index c91590fd58..87be57295c 100644 --- a/test/CodeGenCXX/threadsafe-statics-exceptions.cpp +++ b/test/CodeGenCXX/threadsafe-statics-exceptions.cpp @@ -11,8 +11,8 @@ struct Y { }; void f() { // CHECK: call i32 @__cxa_guard_acquire(i64* @_ZGVZ1fvE1x) // CHECK: invoke void @_ZN1XC1Ev - // CHECK: call void @__cxa_guard_release(i64* @_ZGVZ1fvE1x) - // CHECK-NEXT: call i32 @__cxa_atexit + // CHECK: call i32 @__cxa_atexit + // CHECK-NEXT: call void @__cxa_guard_release(i64* @_ZGVZ1fvE1x) // CHECK: br static X x; -- 2.40.0