From: John McCall Date: Wed, 13 Jul 2011 01:41:37 +0000 (+0000) Subject: Switch delete[] IR-generation over to the destroy framework, X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=7cfd76cb5cec2aa7aaa4176339c806de6ec85a79;p=clang Switch delete[] IR-generation over to the destroy framework, which implicitly makes it EH-safe as well. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@135025 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/CodeGen/CGExprCXX.cpp b/lib/CodeGen/CGExprCXX.cpp index 6b1b88924a..07997e4391 100644 --- a/lib/CodeGen/CGExprCXX.cpp +++ b/lib/CodeGen/CGExprCXX.cpp @@ -1351,90 +1351,50 @@ namespace { /// Emit the code for deleting an array of objects. static void EmitArrayDelete(CodeGenFunction &CGF, const CXXDeleteExpr *E, - llvm::Value *Ptr, - QualType ElementType) { - llvm::Value *NumElements = 0; - llvm::Value *AllocatedPtr = 0; - CharUnits CookieSize; - CGF.CGM.getCXXABI().ReadArrayCookie(CGF, Ptr, E, ElementType, - NumElements, AllocatedPtr, CookieSize); + llvm::Value *deletedPtr, + QualType elementType) { + llvm::Value *numElements = 0; + llvm::Value *allocatedPtr = 0; + CharUnits cookieSize; + CGF.CGM.getCXXABI().ReadArrayCookie(CGF, deletedPtr, E, elementType, + numElements, allocatedPtr, cookieSize); - assert(AllocatedPtr && "ReadArrayCookie didn't set AllocatedPtr"); + assert(allocatedPtr && "ReadArrayCookie didn't set allocated pointer"); // Make sure that we call delete even if one of the dtors throws. - const FunctionDecl *OperatorDelete = E->getOperatorDelete(); + const FunctionDecl *operatorDelete = E->getOperatorDelete(); CGF.EHStack.pushCleanup(NormalAndEHCleanup, - AllocatedPtr, OperatorDelete, - NumElements, ElementType, - CookieSize); - - if (const CXXRecordDecl *RD = ElementType->getAsCXXRecordDecl()) { - if (!RD->hasTrivialDestructor()) { - assert(NumElements && "ReadArrayCookie didn't find element count" - " for a class with destructor"); - CGF.EmitCXXAggrDestructorCall(RD->getDestructor(), NumElements, Ptr); - } - } else if (CGF.getLangOptions().ObjCAutoRefCount && - ElementType->isObjCLifetimeType() && - (ElementType.getObjCLifetime() == Qualifiers::OCL_Strong || - ElementType.getObjCLifetime() == Qualifiers::OCL_Weak)) { - bool IsStrong = ElementType.getObjCLifetime() == Qualifiers::OCL_Strong; - const llvm::Type *SizeLTy = CGF.ConvertType(CGF.getContext().getSizeType()); - llvm::Value *One = llvm::ConstantInt::get(SizeLTy, 1); - - // Create a temporary for the loop index and initialize it with count of - // array elements. - llvm::Value *IndexPtr = CGF.CreateTempAlloca(SizeLTy, "loop.index"); - - // Store the number of elements in the index pointer. - CGF.Builder.CreateStore(NumElements, IndexPtr); - - // Start the loop with a block that tests the condition. - llvm::BasicBlock *CondBlock = CGF.createBasicBlock("for.cond"); - llvm::BasicBlock *AfterFor = CGF.createBasicBlock("for.end"); - - CGF.EmitBlock(CondBlock); - - llvm::BasicBlock *ForBody = CGF.createBasicBlock("for.body"); - - // Generate: if (loop-index != 0 fall to the loop body, - // otherwise, go to the block after the for-loop. - llvm::Value* zeroConstant = llvm::Constant::getNullValue(SizeLTy); - llvm::Value *Counter = CGF.Builder.CreateLoad(IndexPtr); - llvm::Value *IsNE = CGF.Builder.CreateICmpNE(Counter, zeroConstant, - "isne"); - // If the condition is true, execute the body. - CGF.Builder.CreateCondBr(IsNE, ForBody, AfterFor); - - CGF.EmitBlock(ForBody); - - llvm::BasicBlock *ContinueBlock = CGF.createBasicBlock("for.inc"); - // Inside the loop body, emit the constructor call on the array element. - Counter = CGF.Builder.CreateLoad(IndexPtr); - Counter = CGF.Builder.CreateSub(Counter, One); - llvm::Value *Address = CGF.Builder.CreateInBoundsGEP(Ptr, Counter, - "arrayidx"); - if (IsStrong) - CGF.EmitARCRelease(CGF.Builder.CreateLoad(Address, - ElementType.isVolatileQualified()), - /*precise*/ true); - else - CGF.EmitARCDestroyWeak(Address); - - CGF.EmitBlock(ContinueBlock); - - // Emit the decrement of the loop counter. - Counter = CGF.Builder.CreateLoad(IndexPtr); - Counter = CGF.Builder.CreateSub(Counter, One, "dec"); - CGF.Builder.CreateStore(Counter, IndexPtr); - - // Finally, branch back up to the condition for the next iteration. - CGF.EmitBranch(CondBlock); - - // Emit the fall-through block. - CGF.EmitBlock(AfterFor, true); + allocatedPtr, operatorDelete, + numElements, elementType, + cookieSize); + + // Destroy the elements. + if (QualType::DestructionKind dtorKind = elementType.isDestructedType()) { + assert(numElements && "no element count for a type with a destructor!"); + + // It's legal to allocate a zero-length array, but emitArrayDestroy + // won't handle that correctly, so we need to check that here. + llvm::Value *iszero = + CGF.Builder.CreateIsNull(numElements, "delete.isempty"); + + // We'll patch the 'true' successor of this to lead to the end of + // the emitArrayDestroy loop. + llvm::BasicBlock *destroyBB = CGF.createBasicBlock("delete.destroy"); + llvm::BranchInst *br = + CGF.Builder.CreateCondBr(iszero, destroyBB, destroyBB); + CGF.EmitBlock(destroyBB); + + llvm::Value *arrayEnd = + CGF.Builder.CreateInBoundsGEP(deletedPtr, numElements, "delete.end"); + CGF.emitArrayDestroy(deletedPtr, arrayEnd, elementType, + CGF.getDestroyer(dtorKind), + CGF.needsEHCleanup(dtorKind)); + + assert(CGF.Builder.GetInsertBlock()->empty()); + br->setSuccessor(0, CGF.Builder.GetInsertBlock()); } + // Pop the cleanup block. CGF.PopCleanupBlock(); } diff --git a/test/CodeGenCXX/delete.cpp b/test/CodeGenCXX/delete.cpp index f3586d4674..ae5e29bf06 100644 --- a/test/CodeGenCXX/delete.cpp +++ b/test/CodeGenCXX/delete.cpp @@ -67,31 +67,23 @@ namespace test1 { // CHECK: define void @_ZN5test14testEPA10_A20_NS_1AE( void test(A (*arr)[10][20]) { delete [] arr; - // CHECK: icmp eq [10 x [20 x [[S:%.*]]]]* [[PTR:%.*]], null + // CHECK: icmp eq [10 x [20 x [[A:%.*]]]]* [[PTR:%.*]], null // CHECK-NEXT: br i1 - // CHECK: [[ARR:%.*]] = getelementptr inbounds [10 x [20 x [[S]]]]* [[PTR]], i32 0, i32 0, i32 0 - // CHECK-NEXT: bitcast {{.*}} to i8* - // CHECK-NEXT: [[ALLOC:%.*]] = getelementptr inbounds {{.*}}, i64 -8 - // CHECK-NEXT: bitcast i8* [[ALLOC]] to i64* - // CHECK-NEXT: load - // CHECK-NEXT: store i64 {{.*}}, i64* [[IDX:%.*]] - - // CHECK: load i64* [[IDX]] - // CHECK-NEXT: icmp ne {{.*}}, 0 - // CHECK-NEXT: br i1 - - // CHECK: load i64* [[IDX]] - // CHECK-NEXT: [[I:%.*]] = sub i64 {{.*}}, 1 - // CHECK-NEXT: getelementptr inbounds [[S]]* [[ARR]], i64 [[I]] - // CHECK-NEXT: call void @_ZN5test11AD1Ev( + // CHECK: [[BEGIN:%.*]] = getelementptr inbounds [10 x [20 x [[A]]]]* [[PTR]], i32 0, i32 0, i32 0 + // CHECK-NEXT: [[T0:%.*]] = bitcast [[A]]* [[BEGIN]] to i8* + // CHECK-NEXT: [[ALLOC:%.*]] = getelementptr inbounds i8* [[T0]], i64 -8 + // CHECK-NEXT: [[T1:%.*]] = bitcast i8* [[ALLOC]] to i64* + // CHECK-NEXT: [[COUNT:%.*]] = load i64* [[T1]] + // CHECK-NEXT: [[ISZERO:%.*]] = icmp eq i64 [[COUNT]], 0 + // CHECK-NEXT: br i1 [[ISZERO]], + // CHECK: [[END:%.*]] = getelementptr inbounds [[A]]* [[BEGIN]], i64 [[COUNT]] // CHECK-NEXT: br label - - // CHECK: load i64* [[IDX]] - // CHECK-NEXT: sub - // CHECK-NEXT: store {{.*}}, i64* [[IDX]] - // CHECK-NEXT: br label - + // CHECK: [[PAST:%.*]] = phi [[A]]* [ [[END]], {{%.*}} ], [ [[CUR:%.*]], {{%.*}} ] + // CHECK-NEXT: [[CUR:%.*]] = getelementptr inbounds [[A]]* [[PAST]], i64 -1 + // CHECK-NEXT: call void @_ZN5test11AD1Ev([[A]]* [[CUR]]) + // CHECK-NEXT: [[ISDONE:%.*]] = icmp eq [[A]]* [[CUR]], [[BEGIN]] + // CHECK-NEXT: br i1 [[ISDONE]] // CHECK: call void @_ZdaPv(i8* [[ALLOC]]) } } diff --git a/test/CodeGenObjCXX/arc-new-delete.mm b/test/CodeGenObjCXX/arc-new-delete.mm index be92da5176..82747f3b33 100644 --- a/test/CodeGenObjCXX/arc-new-delete.mm +++ b/test/CodeGenObjCXX/arc-new-delete.mm @@ -70,17 +70,26 @@ void test_delete(__strong id *sptr, __weak id *wptr) { // CHECK: define void @_Z17test_array_deletePU8__strongP11objc_objectPU6__weakS0_ void test_array_delete(__strong id *sptr, __weak id *wptr) { - // CHECK: load i64* - // CHECK: {{icmp ne i64.*, 0}} - // CHECK: call void @objc_release - // CHECK: br label + // CHECK: icmp eq i8** [[BEGIN:%.*]], null + // CHECK: [[LEN:%.*]] = load i64* {{%.*}} + // CHECK: icmp eq i64 [[LEN]], 0 + // CHECK: [[END:%.*]] = getelementptr inbounds i8** [[BEGIN]], i64 [[LEN]] + // CHECK: [[PAST:%.*]] = phi i8** [ [[END]], {{%.*}} ], [ [[CUR:%.*]], + // CHECK-NEXT: [[CUR]] = getelementptr inbounds i8** [[PAST]], i64 -1 + // CHECK-NEXT: [[T0:%.*]] = load i8** [[CUR]] + // CHECK-NEXT: call void @objc_release(i8* [[T0]]) + // CHECK-NEXT: icmp eq i8** [[CUR]], [[BEGIN]] // CHECK: call void @_ZdaPv delete [] sptr; - // CHECK: load i64* - // CHECK: {{icmp ne i64.*, 0}} - // CHECK: call void @objc_destroyWeak - // CHECK: br label + // CHECK: icmp eq i8** [[BEGIN:%.*]], null + // CHECK: [[LEN:%.*]] = load i64* {{%.*}} + // CHECK: icmp eq i64 [[LEN]], 0 + // CHECK: [[END:%.*]] = getelementptr inbounds i8** [[BEGIN]], i64 [[LEN]] + // CHECK: [[PAST:%.*]] = phi i8** [ [[END]], {{%.*}} ], [ [[CUR:%.*]], + // CHECK-NEXT: [[CUR]] = getelementptr inbounds i8** [[PAST]], i64 -1 + // CHECK-NEXT: call void @objc_destroyWeak(i8** [[CUR]]) + // CHECK-NEXT: icmp eq i8** [[CUR]], [[BEGIN]] // CHECK: call void @_ZdaPv delete [] wptr; }