diff options
author | John McCall <rjmccall@apple.com> | 2011-07-11 08:38:19 +0000 |
---|---|---|
committer | John McCall <rjmccall@apple.com> | 2011-07-11 08:38:19 +0000 |
commit | 2673c68aa58e277ebc755b71d81aca618cdedbf9 (patch) | |
tree | bac33d78d7e725e30c528d0ac7503b7b6bf0cdaf | |
parent | 00d40eae546219786a74564fc0d95eab1978b82b (diff) |
Fix a lot of problems with the partial destruction of arrays:
- an off-by-one error in emission of irregular array limits for
InitListExprs
- use an EH partial-destruction cleanup within the normal
array-destruction cleanup
- get the branch destinations right for the empty check
Also some refactoring which unfortunately obscures these changes.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@134890 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | lib/CodeGen/CGDecl.cpp | 235 | ||||
-rw-r--r-- | lib/CodeGen/CGExprAgg.cpp | 27 | ||||
-rw-r--r-- | lib/CodeGen/CodeGenFunction.h | 20 | ||||
-rw-r--r-- | test/CodeGenCXX/destructors.cpp | 15 | ||||
-rw-r--r-- | test/CodeGenCXX/partial-destruction.cpp | 90 |
5 files changed, 299 insertions, 88 deletions
diff --git a/lib/CodeGen/CGDecl.cpp b/lib/CodeGen/CGDecl.cpp index ae9753b03d..6eaaca0b72 100644 --- a/lib/CodeGen/CGDecl.cpp +++ b/lib/CodeGen/CGDecl.cpp @@ -306,15 +306,21 @@ void CodeGenFunction::EmitStaticVarDecl(const VarDecl &D, namespace { struct DestroyObject : EHScopeStack::Cleanup { DestroyObject(llvm::Value *addr, QualType type, - CodeGenFunction::Destroyer *destroyer) - : addr(addr), type(type), destroyer(*destroyer) {} + CodeGenFunction::Destroyer *destroyer, + bool useEHCleanupForArray) + : addr(addr), type(type), destroyer(*destroyer), + useEHCleanupForArray(useEHCleanupForArray) {} llvm::Value *addr; QualType type; CodeGenFunction::Destroyer &destroyer; + bool useEHCleanupForArray; - void Emit(CodeGenFunction &CGF, bool IsForEH) { - CGF.emitDestroy(addr, type, destroyer); + void Emit(CodeGenFunction &CGF, bool isForEH) { + // Don't use an EH cleanup recursively from an EH cleanup. + bool useEHCleanupForArray = !isForEH && this->useEHCleanupForArray; + + CGF.emitDestroy(addr, type, destroyer, useEHCleanupForArray); } }; @@ -1059,7 +1065,12 @@ void CodeGenFunction::emitAutoVarTypeCleanup( // If we haven't chosen a more specific destroyer, use the default. if (!destroyer) destroyer = &getDestroyer(dtorKind); - EHStack.pushCleanup<DestroyObject>(cleanupKind, addr, type, destroyer); + + // Use an EH cleanup in array destructors iff the destructor itself + // is being pushed as an EH cleanup. + bool useEHCleanup = (cleanupKind & EHCleanup); + EHStack.pushCleanup<DestroyObject>(cleanupKind, addr, type, destroyer, + useEHCleanup); } void CodeGenFunction::EmitAutoVarCleanups(const AutoVarEmission &emission) { @@ -1119,12 +1130,26 @@ CodeGenFunction::getDestroyer(QualType::DestructionKind kind) { } void CodeGenFunction::pushDestroy(CleanupKind cleanupKind, llvm::Value *addr, - QualType type, Destroyer &destroyer) { - EHStack.pushCleanup<DestroyObject>(cleanupKind, addr, type, destroyer); + QualType type, Destroyer &destroyer, + bool useEHCleanupForArray) { + EHStack.pushCleanup<DestroyObject>(cleanupKind, addr, type, destroyer, + useEHCleanupForArray); } +/// emitDestroy - Immediately perform the destruction of the given +/// object. +/// +/// \param addr - the address of the object; a type* +/// \param type - the type of the object; if an array type, all +/// objects are destroyed in reverse order +/// \param destroyer - the function to call to destroy individual +/// elements +/// \param useEHCleanupForArray - whether an EH cleanup should be +/// used when destroying array elements, in case one of the +/// destructions throws an exception void CodeGenFunction::emitDestroy(llvm::Value *addr, QualType type, - Destroyer &destroyer) { + Destroyer &destroyer, + bool useEHCleanupForArray) { const ArrayType *arrayType = getContext().getAsArrayType(type); if (!arrayType) return destroyer(*this, addr, type); @@ -1132,13 +1157,24 @@ void CodeGenFunction::emitDestroy(llvm::Value *addr, QualType type, llvm::Value *begin = addr; llvm::Value *length = emitArrayLength(arrayType, type, begin); llvm::Value *end = Builder.CreateInBoundsGEP(begin, length); - emitArrayDestroy(begin, end, type, destroyer); + emitArrayDestroy(begin, end, type, destroyer, useEHCleanupForArray); } +/// emitArrayDestroy - Destroys all the elements of the given array, +/// beginning from last to first. The array cannot be zero-length. +/// +/// \param begin - a type* denoting the first element of the array +/// \param end - a type* denoting one past the end of the array +/// \param type - the element type of the array +/// \param destroyer - the function to call to destroy elements +/// \param useEHCleanup - whether to push an EH cleanup to destroy +/// the remaining elements in case the destruction of a single +/// element throws void CodeGenFunction::emitArrayDestroy(llvm::Value *begin, llvm::Value *end, QualType type, - Destroyer &destroyer) { + Destroyer &destroyer, + bool useEHCleanup) { assert(!type->isArrayType()); // The basic structure here is a do-while loop, because we don't @@ -1158,9 +1194,15 @@ void CodeGenFunction::emitArrayDestroy(llvm::Value *begin, llvm::Value *element = Builder.CreateInBoundsGEP(elementPast, negativeOne, "arraydestroy.element"); + if (useEHCleanup) + pushRegularPartialArrayCleanup(begin, element, type, destroyer); + // Perform the actual destruction there. destroyer(*this, element, type); + if (useEHCleanup) + PopCleanupBlock(); + // Check whether we've reached the end. llvm::Value *done = Builder.CreateICmpEQ(element, begin, "arraydestroy.done"); Builder.CreateCondBr(done, doneBB, bodyBB); @@ -1170,73 +1212,131 @@ void CodeGenFunction::emitArrayDestroy(llvm::Value *begin, EmitBlock(doneBB); } +/// Perform partial array destruction as if in an EH cleanup. Unlike +/// emitArrayDestroy, the element type here may still be an array type. +/// +/// Essentially does an emitArrayDestroy, but checking for the +/// possibility of a zero-length array (in case the initializer for +/// the first element throws). +static void emitPartialArrayDestroy(CodeGenFunction &CGF, + llvm::Value *begin, llvm::Value *end, + QualType type, + CodeGenFunction::Destroyer &destroyer) { + // Check whether the array is empty. For the sake of prettier IR, + // we want to jump to the end of the array destroy loop instead of + // jumping to yet another block. We can do this with some modest + // assumptions about how emitArrayDestroy works. + + llvm::Value *earlyTest = + CGF.Builder.CreateICmpEQ(begin, end, "pad.isempty"); + + llvm::BasicBlock *nextBB = CGF.createBasicBlock("pad.arraydestroy"); + + // Temporarily, build the conditional branch with identical + // successors. We'll patch this in a bit. + llvm::BranchInst *br = + CGF.Builder.CreateCondBr(earlyTest, nextBB, nextBB); + CGF.EmitBlock(nextBB); + + llvm::Value *zero = llvm::ConstantInt::get(CGF.SizeTy, 0); + + // If the element type is itself an array, drill down. + llvm::SmallVector<llvm::Value*,4> gepIndices; + gepIndices.push_back(zero); + while (const ArrayType *arrayType = CGF.getContext().getAsArrayType(type)) { + // VLAs don't require a GEP index to walk into. + if (!isa<VariableArrayType>(arrayType)) + gepIndices.push_back(zero); + type = arrayType->getElementType(); + } + if (gepIndices.size() != 1) { + begin = CGF.Builder.CreateInBoundsGEP(begin, gepIndices.begin(), + gepIndices.end(), "pad.arraybegin"); + end = CGF.Builder.CreateInBoundsGEP(end, gepIndices.begin(), + gepIndices.end(), "pad.arrayend"); + } + + // Now that we know that the array isn't empty, destroy it. We + // don't ever need an EH cleanup because we assume that we're in an + // EH cleanup ourselves, so a throwing destructor causes an + // immediate terminate. + CGF.emitArrayDestroy(begin, end, type, destroyer, /*useEHCleanup*/ false); + + // Set the conditional branch's 'false' successor to doneBB. + llvm::BasicBlock *doneBB = CGF.Builder.GetInsertBlock(); + assert(CGF.Builder.GetInsertPoint() == doneBB->begin()); + br->setSuccessor(0, doneBB); +} + namespace { - class PartialArrayDestroy : public EHScopeStack::Cleanup { + /// RegularPartialArrayDestroy - a cleanup which performs a partial + /// array destroy where the end pointer is regularly determined and + /// does not need to be loaded from a local. + class RegularPartialArrayDestroy : public EHScopeStack::Cleanup { + llvm::Value *ArrayBegin; + llvm::Value *ArrayEnd; + QualType ElementType; + CodeGenFunction::Destroyer &Destroyer; + public: + RegularPartialArrayDestroy(llvm::Value *arrayBegin, llvm::Value *arrayEnd, + QualType elementType, + CodeGenFunction::Destroyer *destroyer) + : ArrayBegin(arrayBegin), ArrayEnd(arrayEnd), + ElementType(elementType), Destroyer(*destroyer) {} + + void Emit(CodeGenFunction &CGF, bool isForEH) { + emitPartialArrayDestroy(CGF, ArrayBegin, ArrayEnd, + ElementType, Destroyer); + } + }; + + /// IrregularPartialArrayDestroy - a cleanup which performs a + /// partial array destroy where the end pointer is irregularly + /// determined and must be loaded from a local. + class IrregularPartialArrayDestroy : public EHScopeStack::Cleanup { llvm::Value *ArrayBegin; llvm::Value *ArrayEndPointer; QualType ElementType; CodeGenFunction::Destroyer &Destroyer; public: - PartialArrayDestroy(llvm::Value *arrayBegin, llvm::Value *arrayEndPointer, - QualType elementType, - CodeGenFunction::Destroyer *destroyer) + IrregularPartialArrayDestroy(llvm::Value *arrayBegin, + llvm::Value *arrayEndPointer, + QualType elementType, + CodeGenFunction::Destroyer *destroyer) : ArrayBegin(arrayBegin), ArrayEndPointer(arrayEndPointer), ElementType(elementType), Destroyer(*destroyer) {} void Emit(CodeGenFunction &CGF, bool isForEH) { - llvm::Value *arrayBegin = ArrayBegin; llvm::Value *arrayEnd = CGF.Builder.CreateLoad(ArrayEndPointer); - - // It's possible for the count to be zero here, so we're going - // to need a check. For the sake of prettier IR, we just want - // to jump to the end of the array destroy loop. This assumes - // the structure of the IR generated by emitArrayDestroy, but - // that assumption is pretty reliable. - llvm::Value *earlyTest = - CGF.Builder.CreateICmpEQ(arrayBegin, arrayEnd, "pad.isempty"); - - llvm::BasicBlock *nextBB = CGF.createBasicBlock("pad.arraydestroy"); - - // For now, use a conditional branch with both successors the - // same. We'll patch this later. - llvm::BranchInst *br = - CGF.Builder.CreateCondBr(earlyTest, nextBB, nextBB); - CGF.EmitBlock(nextBB); - - llvm::Value *zero = llvm::ConstantInt::get(CGF.SizeTy, 0); - - // If the element type is itself an array, drill down. - QualType type = ElementType; - llvm::SmallVector<llvm::Value*,4> gepIndices; - gepIndices.push_back(zero); - while (const ArrayType *arrayType = CGF.getContext().getAsArrayType(type)) { - // VLAs don't require a GEP index to walk into. - if (!isa<VariableArrayType>(arrayType)) - gepIndices.push_back(zero); - type = arrayType->getElementType(); - } - if (gepIndices.size() != 1) { - arrayBegin = - CGF.Builder.CreateInBoundsGEP(arrayBegin, gepIndices.begin(), - gepIndices.end(), "pad.arraybegin"); - arrayEnd = - CGF.Builder.CreateInBoundsGEP(arrayEnd, gepIndices.begin(), - gepIndices.end(), "pad.arrayend"); - } - - CGF.emitArrayDestroy(arrayBegin, arrayEnd, type, Destroyer); - - // Set the conditional branch's 'false' successor to doneBB. - llvm::BasicBlock *doneBB = CGF.Builder.GetInsertBlock(); - assert(CGF.Builder.GetInsertPoint() == doneBB->begin()); - br->setSuccessor(1, doneBB); + emitPartialArrayDestroy(CGF, ArrayBegin, arrayEnd, + ElementType, Destroyer); } }; } -/// pushPartialArrayCleanup - Push a cleanup to destroy +/// pushIrregularPartialArrayCleanup - Push an EH cleanup to destroy +/// already-constructed elements of the given array. The cleanup +/// may be popped with DeactivateCleanupBlock or PopCleanupBlock. +/// +/// \param elementType - the immediate element type of the array; +/// possibly still an array type +/// \param array - a value of type elementType* +/// \param destructionKind - the kind of destruction required +/// \param initializedElementCount - a value of type size_t* holding +/// the number of successfully-constructed elements +void CodeGenFunction::pushIrregularPartialArrayCleanup(llvm::Value *array, + llvm::Value *arrayEndPointer, + QualType elementType, + Destroyer &destroyer) { + // FIXME: can this be in a conditional expression? + EHStack.pushCleanup<IrregularPartialArrayDestroy>(EHCleanup, array, + arrayEndPointer, + elementType, &destroyer); +} + +/// pushRegularPartialArrayCleanup - Push an EH cleanup to destroy /// already-constructed elements of the given array. The cleanup -/// may be popped with DeactivateCleanupBlock. +/// may be popped with DeactivateCleanupBlock or PopCleanupBlock. /// /// \param elementType - the immediate element type of the array; /// possibly still an array type @@ -1244,13 +1344,14 @@ namespace { /// \param destructionKind - the kind of destruction required /// \param initializedElementCount - a value of type size_t* holding /// the number of successfully-constructed elements -void CodeGenFunction::pushPartialArrayCleanup(llvm::Value *array, - QualType elementType, - Destroyer &destroyer, - llvm::Value *arrayEndPointer) { +void CodeGenFunction::pushRegularPartialArrayCleanup(llvm::Value *arrayBegin, + llvm::Value *arrayEnd, + QualType elementType, + Destroyer &destroyer) { // FIXME: can this be in a conditional expression? - EHStack.pushCleanup<PartialArrayDestroy>(EHCleanup, array, arrayEndPointer, - elementType, &destroyer); + EHStack.pushCleanup<RegularPartialArrayDestroy>(EHCleanup, + arrayBegin, arrayEnd, + elementType, &destroyer); } namespace { diff --git a/lib/CodeGen/CGExprAgg.cpp b/lib/CodeGen/CGExprAgg.cpp index 1d473f6012..062e13d2b6 100644 --- a/lib/CodeGen/CGExprAgg.cpp +++ b/lib/CodeGen/CGExprAgg.cpp @@ -689,8 +689,8 @@ void AggExprEmitter::VisitInitListExpr(InitListExpr *E) { endOfInit = CGF.CreateTempAlloca(begin->getType(), "arrayinit.endOfInit"); Builder.CreateStore(begin, endOfInit); - CGF.pushPartialArrayCleanup(begin, elementType, - CGF.getDestroyer(dtorKind), endOfInit); + CGF.pushIrregularPartialArrayCleanup(begin, endOfInit, elementType, + CGF.getDestroyer(dtorKind)); cleanup = CGF.EHStack.stable_begin(); // Otherwise, remember that we didn't need a cleanup. @@ -710,16 +710,17 @@ void AggExprEmitter::VisitInitListExpr(InitListExpr *E) { // Emit the explicit initializers. for (uint64_t i = 0; i != NumInitElements; ++i) { // Advance to the next element. - if (i > 0) + if (i > 0) { element = Builder.CreateInBoundsGEP(element, one, "arrayinit.element"); + // Tell the cleanup that it needs to destroy up to this + // element. TODO: some of these stores can be trivially + // observed to be unnecessary. + if (endOfInit) Builder.CreateStore(element, endOfInit); + } + LValue elementLV = CGF.MakeAddrLValue(element, elementType); EmitInitializationToLValue(E->getInit(i), elementLV); - - // Tell the cleanup that it needs to destroy this element. - // TODO: some of these stores can be trivially observed to be - // unnecessary. - if (endOfInit) Builder.CreateStore(element, endOfInit); } // Check whether there's a non-trivial array-fill expression. @@ -743,8 +744,10 @@ void AggExprEmitter::VisitInitListExpr(InitListExpr *E) { // do { *array++ = filler; } while (array != end); // Advance to the start of the rest of the array. - if (NumInitElements) + if (NumInitElements) { element = Builder.CreateInBoundsGEP(element, one, "arrayinit.start"); + if (endOfInit) Builder.CreateStore(element, endOfInit); + } // Compute the end of the array. llvm::Value *end = Builder.CreateInBoundsGEP(begin, @@ -767,13 +770,13 @@ void AggExprEmitter::VisitInitListExpr(InitListExpr *E) { else EmitNullInitializationToLValue(elementLV); - // Tell the EH cleanup that we finished with that element. - if (endOfInit) Builder.CreateStore(element, endOfInit); - // Move on to the next element. llvm::Value *nextElement = Builder.CreateInBoundsGEP(currentElement, one, "arrayinit.next"); + // Tell the EH cleanup that we finished with the last element. + if (endOfInit) Builder.CreateStore(nextElement, endOfInit); + // Leave the loop if we're done. llvm::Value *done = Builder.CreateICmpEQ(nextElement, end, "arrayinit.done"); diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index 7f2d8b9ed0..968222e334 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/lib/CodeGen/CodeGenFunction.h @@ -1156,17 +1156,23 @@ public: typedef void Destroyer(CodeGenFunction &CGF, llvm::Value *addr, QualType ty); - void pushPartialArrayCleanup(llvm::Value *arrayBegin, - QualType elementType, - Destroyer &destroyer, - llvm::Value *arrayEndPointer); + void pushIrregularPartialArrayCleanup(llvm::Value *arrayBegin, + llvm::Value *arrayEndPointer, + QualType elementType, + Destroyer &destroyer); + void pushRegularPartialArrayCleanup(llvm::Value *arrayBegin, + llvm::Value *arrayEnd, + QualType elementType, + Destroyer &destroyer); Destroyer &getDestroyer(QualType::DestructionKind destructionKind); void pushDestroy(CleanupKind kind, llvm::Value *addr, QualType type, - Destroyer &destroyer); - void emitDestroy(llvm::Value *addr, QualType type, Destroyer &destroyer); + Destroyer &destroyer, bool useEHCleanupForArray); + void emitDestroy(llvm::Value *addr, QualType type, Destroyer &destroyer, + bool useEHCleanupForArray); void emitArrayDestroy(llvm::Value *begin, llvm::Value *end, - QualType type, Destroyer &destroyer); + QualType type, Destroyer &destroyer, + bool useEHCleanup); /// Determines whether an EH cleanup is required to destroy a type /// with the given destruction kind. diff --git a/test/CodeGenCXX/destructors.cpp b/test/CodeGenCXX/destructors.cpp index 5e14cbef89..3381985814 100644 --- a/test/CodeGenCXX/destructors.cpp +++ b/test/CodeGenCXX/destructors.cpp @@ -235,15 +235,26 @@ namespace test5 { // CHECK: define void @_ZN5test53fooEv() // CHECK: [[ELEMS:%.*]] = alloca [5 x [[A:%.*]]], align + // CHECK-NEXT: [[EXN:%.*]] = alloca i8* + // CHECK-NEXT: [[SEL:%.*]] = alloca i32 + // CHECK-NEXT: [[EHCLEANUP:%.*]] = alloca i32 // CHECK-NEXT: [[BEGIN:%.*]] = getelementptr inbounds [5 x [[A]]]* [[ELEMS]], i32 0, i32 0 // CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[A]]* [[BEGIN]], i64 5 // CHECK-NEXT: br label // CHECK: [[POST:%.*]] = phi [[A]]* [ [[END]], {{%.*}} ], [ [[ELT:%.*]], {{%.*}} ] // CHECK-NEXT: [[ELT]] = getelementptr inbounds [[A]]* [[POST]], i64 -1 - // CHECK-NEXT: call void @_ZN5test51AD1Ev([[A]]* [[ELT]]) - // CHECK-NEXT: [[T0:%.*]] = icmp eq [[A]]* [[ELT]], [[BEGIN]] + // CHECK-NEXT: invoke void @_ZN5test51AD1Ev([[A]]* [[ELT]]) + // CHECK: [[T0:%.*]] = icmp eq [[A]]* [[ELT]], [[BEGIN]] // CHECK-NEXT: br i1 [[T0]], // CHECK: ret void + // lpad + // CHECK: [[EMPTY:%.*]] = icmp eq [[A]]* [[BEGIN]], [[ELT]] + // CHECK-NEXT: br i1 [[EMPTY]] + // CHECK: [[AFTER:%.*]] = phi [[A]]* [ [[ELT]], {{%.*}} ], [ [[CUR:%.*]], {{%.*}} ] + // CHECK-NEXT: [[CUR:%.*]] = getelementptr inbounds [[A]]* [[AFTER]], i64 -1 + // CHECK-NEXT: invoke void @_ZN5test51AD1Ev([[A]]* [[CUR]]) + // CHECK: [[DONE:%.*]] = icmp eq [[A]]* [[CUR]], [[BEGIN]] + // CHECK-NEXT: br i1 [[DONE]], void foo() { A elems[5]; } diff --git a/test/CodeGenCXX/partial-destruction.cpp b/test/CodeGenCXX/partial-destruction.cpp new file mode 100644 index 0000000000..53b34d06bf --- /dev/null +++ b/test/CodeGenCXX/partial-destruction.cpp @@ -0,0 +1,90 @@ +// RUN: %clang_cc1 %s -triple=x86_64-apple-darwin10 -emit-llvm -o - -fcxx-exceptions -fexceptions | FileCheck %s + +// Test IR generation for partial destruction of aggregates. + +void opaque(); + +// Initializer lists. +namespace test0 { + struct A { A(int); A(); ~A(); void *v; }; + void test() { + A as[10] = { 5, 7 }; + opaque(); + } + // CHECK: define void @_ZN5test04testEv() + // CHECK: [[AS:%.*]] = alloca [10 x [[A:%.*]]], align + // CHECK-NEXT: [[ENDVAR:%.*]] = alloca [[A]]* + // CHECK-NEXT: [[EXN:%.*]] = alloca i8* + // CHECK-NEXT: [[SEL:%.*]] = alloca i32 + // CHECK-NEXT: [[CLEANUP:%.*]] = alloca i32 + + // Initialize. + // CHECK-NEXT: [[E_BEGIN:%.*]] = getelementptr inbounds [10 x [[A]]]* [[AS]], i64 0, i64 0 + // CHECK-NEXT: store [[A]]* [[E_BEGIN]], [[A]]** [[ENDVAR]] + // CHECK-NEXT: invoke void @_ZN5test01AC1Ei([[A]]* [[E_BEGIN]], i32 5) + // CHECK: [[E1:%.*]] = getelementptr inbounds [[A]]* [[E_BEGIN]], i64 1 + // CHECK-NEXT: store [[A]]* [[E1]], [[A]]** [[ENDVAR]] + // CHECK-NEXT: invoke void @_ZN5test01AC1Ei([[A]]* [[E1]], i32 7) + // CHECK: [[E2:%.*]] = getelementptr inbounds [[A]]* [[E1]], i64 1 + // CHECK-NEXT: store [[A]]* [[E2]], [[A]]** [[ENDVAR]] + // CHECK-NEXT: [[E_END:%.*]] = getelementptr inbounds [[A]]* [[E_BEGIN]], i64 10 + // CHECK-NEXT: br label + // CHECK: [[E_CUR:%.*]] = phi [[A]]* [ [[E2]], {{%.*}} ], [ [[E_NEXT:%.*]], {{%.*}} ] + // CHECK-NEXT: invoke void @_ZN5test01AC1Ev([[A]]* [[E_CUR]]) + // CHECK: [[E_NEXT]] = getelementptr inbounds [[A]]* [[E_CUR]], i64 1 + // CHECK-NEXT: store [[A]]* [[E_NEXT]], [[A]]** [[ENDVAR]] + // CHECK-NEXT: [[T0:%.*]] = icmp eq [[A]]* [[E_NEXT]], [[E_END]] + // CHECK-NEXT: br i1 [[T0]], + + // Run. + // CHECK: invoke void @_Z6opaquev() + + // Normal destroy. + // CHECK: [[ED_BEGIN:%.*]] = getelementptr inbounds [10 x [[A]]]* [[AS]], i32 0, i32 0 + // CHECK-NEXT: [[ED_END:%.*]] = getelementptr inbounds [[A]]* [[ED_BEGIN]], i64 10 + // CHECK-NEXT: br label + // CHECK: [[ED_AFTER:%.*]] = phi [[A]]* [ [[ED_END]], {{%.*}} ], [ [[ED_CUR:%.*]], {{%.*}} ] + // CHECK-NEXT: [[ED_CUR]] = getelementptr inbounds [[A]]* [[ED_AFTER]], i64 -1 + // CHECK-NEXT: invoke void @_ZN5test01AD1Ev([[A]]* [[ED_CUR]]) + // CHECK: [[T0:%.*]] = icmp eq [[A]]* [[ED_CUR]], [[ED_BEGIN]] + // CHECK-NEXT: br i1 [[T0]], + // CHECK: ret void + + // Partial destroy for initialization. + // CHECK: llvm.eh.selector({{.*}}, i32 0) + // CHECK: [[PARTIAL_END:%.*]] = load [[A]]** [[ENDVAR]] + // CHECK-NEXT: [[T0:%.*]] = icmp eq [[A]]* [[E_BEGIN]], [[PARTIAL_END]] + // CHECK-NEXT: br i1 [[T0]], + // CHECK: [[E_AFTER:%.*]] = phi [[A]]* [ [[PARTIAL_END]], {{%.*}} ], [ [[E_CUR:%.*]], {{%.*}} ] + // CHECK-NEXT: [[E_CUR]] = getelementptr inbounds [[A]]* [[E_AFTER]], i64 -1 + // CHECK-NEXT: invoke void @_ZN5test01AD1Ev([[A]]* [[E_CUR]]) + // CHECK: [[T0:%.*]] = icmp eq [[A]]* [[E_CUR]], [[E_BEGIN]] + // CHECK-NEXT: br i1 [[T0]], + + // Primary EH destructor. + // CHECK: llvm.eh.selector({{.*}}, i32 0) + // CHECK: [[E0:%.*]] = getelementptr inbounds [10 x [[A]]]* [[AS]], i32 0, i32 0 + // CHECK-NEXT: [[E_END:%.*]] = getelementptr inbounds [[A]]* [[E0]], i64 10 + // CHECK-NEXT: br label + + // Partial destructor for primary normal destructor. + // FIXME: There's some really bad block ordering here which causes + // the partial destroy for the primary normal destructor to fall + // within the primary EH destructor. + // CHECK: llvm.eh.selector({{.*}}, i32 0) + // CHECK: [[T0:%.*]] = icmp eq [[A]]* [[ED_BEGIN]], [[ED_CUR]] + // CHECK-NEXT: br i1 [[T0]] + // CHECK: [[EDD_AFTER:%.*]] = phi [[A]]* [ [[ED_CUR]], {{%.*}} ], [ [[EDD_CUR:%.*]], {{%.*}} ] + // CHECK-NEXT: [[EDD_CUR]] = getelementptr inbounds [[A]]* [[EDD_AFTER]], i64 -1 + // CHECK-NEXT: invoke void @_ZN5test01AD1Ev([[A]]* [[EDD_CUR]]) + // CHECK: [[T0:%.*]] = icmp eq [[A]]* [[EDD_CUR]], [[ED_BEGIN]] + // CHECK-NEXT: br i1 [[T0]] + + // Back to the primary EH destructor. + // CHECK: [[E_AFTER:%.*]] = phi [[A]]* [ [[E_END]], {{%.*}} ], [ [[E_CUR:%.*]], {{%.*}} ] + // CHECK-NEXT: [[E_CUR]] = getelementptr inbounds [[A]]* [[E_AFTER]], i64 -1 + // CHECK-NEXT: invoke void @_ZN5test01AD1Ev([[A]]* [[E_CUR]]) + // CHECK: [[T0:%.*]] = icmp eq [[A]]* [[E_CUR]], [[E0]] + // CHECK-NEXT: br i1 [[T0]], + +} |