Author: Andy Kaylor Date: 2026-03-12T16:55:48-07:00 New Revision: ec934142e4b470881ae0081ff505881fd5bb532c
URL: https://github.com/llvm/llvm-project/commit/ec934142e4b470881ae0081ff505881fd5bb532c DIFF: https://github.com/llvm/llvm-project/commit/ec934142e4b470881ae0081ff505881fd5bb532c.diff LOG: [CIR] Implement array delete for destructed types (#186248) This extends the cir.delete_array lowering code to introduce a loop that calls destructors when the array being deleted represents a destructed type. The lowering introduces the destructors by way of a cir.array.dtor operation, which is further expanded during LoweringPrepare. This also required updating the cir.array.dtor operation to accept a raw pointer to the element type and a value representing the number of elements to be destructed. This does not yet handle the possibility of destructors throwing exceptions. Added: Modified: clang/include/clang/CIR/Dialect/IR/CIROps.td clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp clang/test/CIR/CodeGen/delete-array.cpp Removed: ################################################################################ diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index d10bed40e75d4..a9b98b1f43b3f 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -4331,7 +4331,24 @@ def CIR_TrapOp : CIR_Op<"trap", [Terminator]> { // ArrayCtor & ArrayDtor //===----------------------------------------------------------------------===// -class CIR_ArrayInitDestroy<string mnemonic> : CIR_Op<mnemonic> { +def CIR_ArrayCtor : CIR_Op<"array.ctor"> { + let summary = "Initialize array elements with C++ constructors"; + let description = [{ + Initialize each array element using the same C++ constructor. This + operation has one region, with one single block. The block has an + incoming argument for the current array element to initialize. + + Example: + + ```mlir + cir.array.ctor(%0 : !cir.ptr<!cir.array<!rec_S x 42>>) { + ^bb0(%arg0: !cir.ptr<!rec_S>): + cir.call @some_ctor(%arg0) : (!cir.ptr<!rec_S>) -> () + cir.yield + } + ``` + }]; + let arguments = (ins Arg<CIR_PtrToArray, "array address", [MemWrite, MemRead]>:$addr ); @@ -4356,42 +4373,77 @@ class CIR_ArrayInitDestroy<string mnemonic> : CIR_Op<mnemonic> { let hasLLVMLowering = false; } -def CIR_ArrayCtor : CIR_ArrayInitDestroy<"array.ctor"> { - let summary = "Initialize array elements with C++ constructors"; +def CIR_ArrayDtor : CIR_Op<"array.dtor"> { + let summary = "Destroy array elements with C++ destructors"; let description = [{ - Initialize each array element using the same C++ constructor. This - operation has one region, with one single block. The block has an - incoming argument for the current array element to initialize. + Destroy each array element using the same C++ destructor. This operation + has one region with one block whose argument is a pointer to the current + array element. - Example: + When `num_elements` is absent, `addr` must be a pointer to a fixed-size + CIR array type and the element count is derived from that array type. + + When `num_elements` is present, `addr` is a pointer to the first element + and `num_elements` provides the runtime element count (e.g. from an array + cookie for `delete[]`). + + Elements are destroyed in reverse order. + + Examples: ```mlir - cir.array.ctor(%0 : !cir.ptr<!cir.array<!rec_S x 42>>) { + // Fixed-size (stack array, global): + cir.array.dtor %0 : !cir.ptr<!cir.array<!rec_S x 42>> { ^bb0(%arg0: !cir.ptr<!rec_S>): - cir.call @some_ctor(%arg0) : (!cir.ptr<!rec_S>) -> () + cir.call @_ZN1SD1Ev(%arg0) : (!cir.ptr<!rec_S>) -> () cir.yield } - ``` - }]; -} - -def CIR_ArrayDtor : CIR_ArrayInitDestroy<"array.dtor"> { - let summary = "Destroy array elements with C++ dtors"; - let description = [{ - Destroy each array element using the same C++ destructor. This - operation has one region, with one single block. The block has an - incoming argument for the current array element to destruct. - - Example: - ```mlir - cir.array.dtor(%0 : !cir.ptr<!cir.array<!rec_S x 42>>) { + // Dynamic count (delete[] with destructor): + cir.array.dtor %ptr, %n : !cir.ptr<!rec_S>, !u64i { ^bb0(%arg0: !cir.ptr<!rec_S>): - cir.call @some_dtor(%arg0) : (!cir.ptr<!rec_S>) -> () + cir.call @_ZN1SD1Ev(%arg0) : (!cir.ptr<!rec_S>) -> () cir.yield } ``` }]; + + let arguments = (ins + Arg<CIR_AnyPtrType, "array or element address", [MemWrite, MemRead]>:$addr, + Optional<CIR_AnyIntType>:$num_elements + ); + + let regions = (region SizedRegion<1>:$body); + + let assemblyFormat = [{ + $addr (`,` $num_elements^)? `:` qualified(type($addr)) + (`,` type($num_elements)^)? $body attr-dict + }]; + + let builders = [ + // Static form: addr is ptr<array<T x N>>, no num_elements. + OpBuilder<(ins "mlir::Value":$addr, + "llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)>":$regionBuilder), [{ + assert(regionBuilder && "builder callback expected"); + mlir::OpBuilder::InsertionGuard guard($_builder); + mlir::Region *r = $_state.addRegion(); + $_state.addOperands(ValueRange{addr}); + $_builder.createBlock(r); + regionBuilder($_builder, $_state.location); + }]>, + // Dynamic form: addr is ptr<T>, num_elements is the runtime count. + OpBuilder<(ins "mlir::Value":$addr, "mlir::Value":$num_elements, + "llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)>":$regionBuilder), [{ + assert(regionBuilder && "builder callback expected"); + mlir::OpBuilder::InsertionGuard guard($_builder); + mlir::Region *r = $_state.addRegion(); + $_state.addOperands({addr, num_elements}); + $_builder.createBlock(r); + regionBuilder($_builder, $_state.location); + }]> + ]; + + let hasLLVMLowering = false; } //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp index 0ba7384e307fb..50617a8d04f6d 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp @@ -1233,12 +1233,6 @@ void CIRGenFunction::emitCXXDeleteExpr(const CXXDeleteExpr *e) { } if (e->isArrayForm()) { - // This will be handled in CXXABILowering, but we can emit a better - // diagnostic here. - if (deleteTy.isDestructedType()) { - cgm.errorNYI(e->getSourceRange(), - "emitCXXDeleteExpr: array delete of destructed type"); - } const FunctionDecl *operatorDelete = e->getOperatorDelete(); cir::FuncOp operatorDeleteFn = cgm.getAddrOfFunction(operatorDelete); auto deleteFn = @@ -1247,8 +1241,24 @@ void CIRGenFunction::emitCXXDeleteExpr(const CXXDeleteExpr *e) { auto deleteParams = cir::UsualDeleteParamsAttr::get( builder.getContext(), udp.Size, isAlignedAllocation(udp.Alignment), isTypeAwareAllocation(udp.TypeAwareDelete), udp.DestroyingDelete); + + mlir::FlatSymbolRefAttr elementDtor; + if (const auto *rd = deleteTy->getAsCXXRecordDecl()) { + if (rd->hasDefinition() && !rd->hasTrivialDestructor()) { + const CXXDestructorDecl *dtor = rd->getDestructor(); + if (dtor->getType()->castAs<FunctionProtoType>()->canThrow()) + cgm.errorNYI(e->getSourceRange(), + "emitCXXDeleteExpr: throwing destructor"); + cir::FuncOp dtorFn = + cgm.getAddrOfCXXStructor(GlobalDecl(dtor, Dtor_Complete)); + elementDtor = mlir::FlatSymbolRefAttr::get(builder.getContext(), + dtorFn.getSymNameAttr()); + } + } + cir::DeleteArrayOp::create(builder, ptr.getPointer().getLoc(), - ptr.getPointer(), deleteFn, deleteParams); + ptr.getPointer(), deleteFn, deleteParams, + elementDtor); } else { emitObjectDelete(*this, e, ptr, deleteTy); } diff --git a/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp b/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp index f74474cbcfbff..d2c7ac37e8a96 100644 --- a/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp +++ b/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp @@ -15,6 +15,7 @@ #include "mlir/Transforms/DialectConversion.h" #include "clang/CIR/Dialect/Builder/CIRBaseBuilder.h" #include "clang/CIR/Dialect/IR/CIRAttrs.h" +#include "clang/CIR/Dialect/IR/CIRDataLayout.h" #include "clang/CIR/Dialect/IR/CIRDialect.h" #include "clang/CIR/Dialect/IR/CIROpsEnums.h" #include "clang/CIR/Dialect/Passes.h" @@ -327,7 +328,9 @@ mlir::LogicalResult CIRDeleteArrayOpABILowering::matchAndRewrite( mlir::Value loweredAddress = adaptor.getAddress(); cir::UsualDeleteParamsAttr deleteParams = op.getDeleteParams(); - bool sizeNeeded = deleteParams.getSize(); + bool cookieRequired = deleteParams.getSize(); + assert((deleteParams.getSize() || !op.getElementDtorAttr()) && + "Expected size parameter when dtor fn is provided!"); if (deleteParams.getTypeAwareDelete() || deleteParams.getDestroyingDelete() || deleteParams.getAlignment()) @@ -339,7 +342,7 @@ mlir::LogicalResult CIRDeleteArrayOpABILowering::matchAndRewrite( mlir::Value deletePtr; llvm::SmallVector<mlir::Value> callArgs; - if (sizeNeeded) { + if (cookieRequired) { mlir::Value numElements; clang::CharUnits cookieSize; auto ptrTy = mlir::cast<cir::PointerType>(loweredAddress.getType()); @@ -347,6 +350,23 @@ mlir::LogicalResult CIRDeleteArrayOpABILowering::matchAndRewrite( cxxABI.readArrayCookie(loc, loweredAddress, dl, cirBuilder, numElements, deletePtr, cookieSize); + + // If a dtor function is provided, create an array dtor operation. + // This will get expanded during LoweringPrepare. + mlir::FlatSymbolRefAttr dtorFn = op.getElementDtorAttr(); + if (dtorFn) { + auto eltPtrTy = cir::PointerType::get(ptrTy.getPointee()); + cir::ArrayDtor::create( + rewriter, loc, loweredAddress, numElements, + [&](mlir::OpBuilder &b, mlir::Location l) { + auto arg = b.getInsertionBlock()->addArgument(eltPtrTy, l); + cir::CallOp::create(b, l, dtorFn, cir::VoidType(), + mlir::ValueRange{arg}); + cir::YieldOp::create(b, l); + }); + } + + // Compute the total allocation size and add it to the call arguments. callArgs.push_back(deletePtr); uint64_t eltSizeBytes = dl.getTypeSizeInBits(ptrTy.getPointee()) / 8; unsigned ptrWidth = diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp index 232d320d71f37..82bf8dbccba97 100644 --- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp +++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp @@ -1337,32 +1337,57 @@ void LoweringPreparePass::buildCXXGlobalInitFunc() { static void lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder, clang::ASTContext *astCtx, mlir::Operation *op, mlir::Type eltTy, - mlir::Value arrayAddr, uint64_t arrayLen, - bool isCtor) { + mlir::Value addr, + mlir::Value numElements, + uint64_t arrayLen, bool isCtor) { // Generate loop to call into ctor/dtor for every element. mlir::Location loc = op->getLoc(); + bool isDynamic = numElements != nullptr; // TODO: instead of getting the size from the AST context, create alias for // PtrDiffTy and unify with CIRGen stuff. const unsigned sizeTypeSize = astCtx->getTypeSize(astCtx->getSignedSizeType()); - uint64_t endOffset = isCtor ? arrayLen : arrayLen - 1; - mlir::Value endOffsetVal = - builder.getUnsignedInt(loc, endOffset, sizeTypeSize); - - auto begin = cir::CastOp::create(builder, loc, eltTy, - cir::CastKind::array_to_ptrdecay, arrayAddr); - mlir::Value end = - cir::PtrStrideOp::create(builder, loc, eltTy, begin, endOffsetVal); + + mlir::Value begin, end; + if (isDynamic) { + assert(!isCtor && "Unexpected dynamic ctor loop"); + mlir::Value one = builder.getUnsignedInt(loc, 1, sizeTypeSize); + mlir::Value endOffsetVal = builder.createSub(loc, numElements, one); + begin = addr; + end = cir::PtrStrideOp::create(builder, loc, eltTy, begin, endOffsetVal); + } else { + // Static: emit endOffset const first, then array_to_ptrdecay, matching + // the expected IR ordering. + uint64_t endOffset = isCtor ? arrayLen : arrayLen - 1; + mlir::Value endOffsetVal = + builder.getUnsignedInt(loc, endOffset, sizeTypeSize); + begin = cir::CastOp::create(builder, loc, eltTy, + cir::CastKind::array_to_ptrdecay, addr); + end = cir::PtrStrideOp::create(builder, loc, eltTy, begin, endOffsetVal); + } + mlir::Value start = isCtor ? begin : end; mlir::Value stop = isCtor ? end : begin; + // For dynamic destructors, guard against zero elements. + // This places the destructor loop emitted below inside the if block. + cir::IfOp ifOp; + if (isDynamic) { + mlir::Value isEmpty = + cir::CmpOp::create(builder, loc, cir::CmpOpKind::ne, start, stop); + ifOp = cir::IfOp::create(builder, loc, isEmpty, + /*withElseRegion=*/false, + [&](mlir::OpBuilder &, mlir::Location) {}); + builder.setInsertionPointToStart(&ifOp.getThenRegion().front()); + } + mlir::Value tmpAddr = builder.createAlloca( loc, /*addr type*/ builder.getPointerTo(eltTy), /*var type*/ eltTy, "__array_idx", builder.getAlignmentAttr(1)); builder.createStore(loc, start, tmpAddr); - cir::DoWhileOp loop = builder.createDoWhile( + builder.createDoWhile( loc, /*condBuilder=*/ [&](mlir::OpBuilder &b, mlir::Location loc) { @@ -1396,7 +1421,9 @@ static void lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder, builder.createYield(loc); }); - op->replaceAllUsesWith(loop); + if (ifOp) + cir::YieldOp::create(builder, loc); + op->erase(); } @@ -1405,11 +1432,20 @@ void LoweringPreparePass::lowerArrayDtor(cir::ArrayDtor op) { builder.setInsertionPointAfter(op.getOperation()); mlir::Type eltTy = op->getRegion(0).getArgument(0).getType(); + + if (op.getNumElements()) { + lowerArrayDtorCtorIntoLoop(builder, astCtx, op, eltTy, op.getAddr(), + op.getNumElements(), /*arrayLen=*/0, + /*isCtor=*/false); + return; + } + assert(!cir::MissingFeatures::vlas()); auto arrayLen = mlir::cast<cir::ArrayType>(op.getAddr().getType().getPointee()).getSize(); - lowerArrayDtorCtorIntoLoop(builder, astCtx, op, eltTy, op.getAddr(), arrayLen, - false); + lowerArrayDtorCtorIntoLoop(builder, astCtx, op, eltTy, op.getAddr(), + /*numElements=*/nullptr, arrayLen, + /*isCtor=*/false); } void LoweringPreparePass::lowerArrayCtor(cir::ArrayCtor op) { @@ -1420,8 +1456,9 @@ void LoweringPreparePass::lowerArrayCtor(cir::ArrayCtor op) { assert(!cir::MissingFeatures::vlas()); auto arrayLen = mlir::cast<cir::ArrayType>(op.getAddr().getType().getPointee()).getSize(); - lowerArrayDtorCtorIntoLoop(builder, astCtx, op, eltTy, op.getAddr(), arrayLen, - true); + lowerArrayDtorCtorIntoLoop(builder, astCtx, op, eltTy, op.getAddr(), + /*numElements=*/nullptr, arrayLen, + /*isCtor=*/true); } void LoweringPreparePass::lowerTrivialCopyCall(cir::CallOp op) { diff --git a/clang/test/CIR/CodeGen/delete-array.cpp b/clang/test/CIR/CodeGen/delete-array.cpp index 117943f005a0a..2ce2880b9d464 100644 --- a/clang/test/CIR/CodeGen/delete-array.cpp +++ b/clang/test/CIR/CodeGen/delete-array.cpp @@ -153,3 +153,124 @@ void test_sized_array_delete(SizedArrayDelete *ptr) { // OGCG: call void @_ZN16SizedArrayDeletedaEPvm(ptr {{.*}} %[[ALLOC_PTR]], i64 {{.*}} %[[TOTAL_SIZE]]) // OGCG: br label %[[DELETE_END]] // OGCG: [[DELETE_END]]: + +struct Destructed { + ~Destructed(); + int x; +}; +void test_delete_array_destructed(Destructed *ptr) { + delete[] ptr; +} + +// CIR-BEFORE: cir.func {{.*}} @_Z28test_delete_array_destructedP10Destructed +// CIR-BEFORE: %[[PTR:.*]] = cir.load +// CIR-BEFORE: %[[NULL:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!rec_Destructed> +// CIR-BEFORE: %[[NOT_NULL:.*]] = cir.cmp ne %[[PTR]], %[[NULL]] : !cir.ptr<!rec_Destructed> +// CIR-BEFORE: cir.if %[[NOT_NULL]] { +// CIR-BEFORE: cir.delete_array %[[PTR]] : !cir.ptr<!rec_Destructed> { +// CIR-BEFORE-SAME: delete_fn = @_ZdaPvm, +// CIR-BEFORE-SAME: delete_params = #cir.usual_delete_params<size = true>, +// CIR-BEFORE-SAME: element_dtor = @_ZN10DestructedD1Ev} +// CIR-BEFORE: } + +// CIR: cir.func {{.*}} @_Z28test_delete_array_destructedP10Destructed +// CIR: %[[PTR:.*]] = cir.load +// CIR: %[[NULL:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!rec_Destructed> +// CIR: %[[NOT_NULL:.*]] = cir.cmp ne %[[PTR]], %[[NULL]] : !cir.ptr<!rec_Destructed> +// CIR: cir.if %[[NOT_NULL]] { +// +// Read the array cookie. +// CIR: %[[BYTE_PTR:.*]] = cir.cast bitcast %[[PTR]] : !cir.ptr<!rec_Destructed> -> !cir.ptr<!u8i> +// CIR: %[[NEG_COOKIE:.*]] = cir.const #cir.int<-8> : !s64i +// CIR: %[[ALLOC_BYTE_PTR:.*]] = cir.ptr_stride %[[BYTE_PTR]], %[[NEG_COOKIE]] : (!cir.ptr<!u8i>, !s64i) -> !cir.ptr<!u8i> +// CIR: %[[VOID_PTR:.*]] = cir.cast bitcast %[[ALLOC_BYTE_PTR]] : !cir.ptr<!u8i> -> !cir.ptr<!void> +// CIR: %[[COOKIE_PTR:.*]] = cir.cast bitcast %[[ALLOC_BYTE_PTR]] : !cir.ptr<!u8i> -> !cir.ptr<!u64i> +// CIR: %[[NUM_ELEM:.*]] = cir.load{{.*}} %[[COOKIE_PTR]] : !cir.ptr<!u64i>, !u64i +// +// Destruct elements in reverse order. +// CIR: %[[ONE:.*]] = cir.const #cir.int<1> : !u64i +// CIR: %[[NUM_ELEM_MINUS_ONE:.*]] = cir.sub %[[NUM_ELEM]], %[[ONE]] : !u64i +// CIR: %[[END:.*]] = cir.ptr_stride %[[PTR]], %[[NUM_ELEM_MINUS_ONE]] : (!cir.ptr<!rec_Destructed>, !u64i) -> !cir.ptr<!rec_Destructed> +// CIR: %[[NOT_EMPTY:.*]] = cir.cmp ne %[[END]], %[[PTR]] : !cir.ptr<!rec_Destructed> +// CIR: cir.if %[[NOT_EMPTY]] { +// CIR: %[[ARR_IDX:.*]] = cir.alloca !cir.ptr<!rec_Destructed>, !cir.ptr<!cir.ptr<!rec_Destructed>>, ["__array_idx"] {alignment = 1 : i64} +// CIR: cir.store %[[END]], %[[ARR_IDX]] : !cir.ptr<!rec_Destructed>, !cir.ptr<!cir.ptr<!rec_Destructed>> +// CIR: cir.do { +// CIR: %[[ARR_CUR:.*]] = cir.load{{.*}} %[[ARR_IDX]] : !cir.ptr<!cir.ptr<!rec_Destructed>>, !cir.ptr<!rec_Destructed> +// CIR: cir.call @_ZN10DestructedD1Ev(%[[ARR_CUR]]) : (!cir.ptr<!rec_Destructed>) -> () +// CIR: %[[NEG_ONE:.*]] = cir.const #cir.int<-1> : !s64i +// CIR: %[[ARR_NEXT:.*]] = cir.ptr_stride %[[ARR_CUR]], %[[NEG_ONE]] : (!cir.ptr<!rec_Destructed>, !s64i) -> !cir.ptr<!rec_Destructed> +// CIR: cir.store %[[ARR_NEXT]], %[[ARR_IDX]] : !cir.ptr<!rec_Destructed>, !cir.ptr<!cir.ptr<!rec_Destructed>> +// CIR: cir.yield +// CIR: } while { +// CIR: %[[ARR_CUR:.*]] = cir.load{{.*}} %[[ARR_IDX]] : !cir.ptr<!cir.ptr<!rec_Destructed>>, !cir.ptr<!rec_Destructed> +// CIR: %[[CMP:.*]] = cir.cmp ne %[[ARR_CUR]], %[[PTR]] : !cir.ptr<!rec_Destructed> +// CIR: cir.condition(%[[CMP]]) +// CIR: } +// CIR: } +// +// Compute total size and call delete function. +// CIR: %[[ELEM_SIZE:.*]] = cir.const #cir.int<4> : !u64i +// CIR: %[[ARRAY_SIZE:.*]] = cir.mul %[[ELEM_SIZE]], %[[NUM_ELEM]] : !u64i +// CIR: %[[COOKIE_SIZE:.*]] = cir.const #cir.int<8> : !u64i +// CIR: %[[TOTAL_SIZE:.*]] = cir.add %[[ARRAY_SIZE]], %[[COOKIE_SIZE]] : !u64i +// CIR: cir.call @_ZdaPvm(%[[VOID_PTR]], %[[TOTAL_SIZE]]) +// CIR: } + +// LLVM: define {{.*}} void @_Z28test_delete_array_destructedP10Destructed +// LLVM: %[[PTR:.*]] = load ptr, ptr %{{.*}} +// LLVM: %[[NOT_NULL:.*]] = icmp ne ptr %[[PTR]], null +// LLVM: br i1 %[[NOT_NULL]], label %[[DELETE_NOTNULL:.*]], label %[[DONE:.*]] +// LLVM: [[DELETE_NOTNULL]]: +// LLVM: %[[ALLOC_PTR:.*]] = getelementptr i8, ptr %[[PTR]], i64 -8 +// LLVM: %[[NUM_ELEM:.*]] = load i64, ptr %[[ALLOC_PTR]], align 4 +// LLVM: %[[NUM_ELEM_MINUS_ONE:.*]] = sub i64 %[[NUM_ELEM]], 1 +// LLVM: %[[ARR_END:.*]] = getelementptr %struct.Destructed, ptr %[[PTR]], i64 %[[NUM_ELEM_MINUS_ONE]] +// LLVM: %[[NOT_EMPTY:.*]] = icmp ne ptr %[[ARR_END]], %[[PTR]] +// LLVM: br i1 %[[NOT_EMPTY]], label %[[DESTROY_ELEMENTS:.*]], label %[[CALL_DELETE:.*]] +// LLVM: [[DESTROY_ELEMENTS:.*]]: +// LLVM: store ptr %[[ARR_END]], ptr %[[ARR_IDX:.*]] +// LLVM: br label %[[DELETE_ELEMENT:.*]] +// LLVM: [[LOOP_CONDITION:.*]] +// LLVM: %[[ARR_CUR:.*]] = load ptr, ptr %[[ARR_IDX]] +// LLVM: %[[CMP:.*]] = icmp ne ptr %[[ARR_CUR]], %[[PTR]] +// LLVM: br i1 %[[CMP]], label %[[DELETE_ELEMENT:.*]], label %[[LOOP_END:.*]] +// LLVM: [[DELETE_ELEMENT]]: +// LLVM: %[[ELEM:.*]] = load ptr, ptr %[[ARR_IDX]] +// LLVM: call void @_ZN10DestructedD1Ev(ptr %[[ELEM]]) +// LLVM: %[[NEXT:.*]] = getelementptr %struct.Destructed, ptr %[[ELEM]], i64 -1 +// LLVM: store ptr %[[NEXT]], ptr %[[ARR_IDX]] +// LLVM: br label %[[LOOP_CONDITION]] +// LLVM: [[LOOP_END]]: +// LLVM: br label %[[CALL_DELETE]] +// LLVM: [[CALL_DELETE]]: +// LLVM: %[[ARRAY_SIZE:.*]] = mul i64 4, %[[NUM_ELEM]] +// LLVM: %[[TOTAL_SIZE:.*]] = add i64 %[[ARRAY_SIZE]], 8 +// LLVM: call void @_ZdaPvm(ptr %[[ALLOC_PTR]], i64 %[[TOTAL_SIZE]]) +// LLVM: br label %[[DONE]] +// LLVM: [[DONE]]: +// LLVM: ret void + +// OGCG: define {{.*}} void @_Z28test_delete_array_destructedP10Destructed +// OGCG: %[[PTR:.*]] = load ptr, ptr %{{.*}} +// OGCG: %[[IS_NULL:.*]] = icmp eq ptr %[[PTR]], null +// OGCG: br i1 %[[IS_NULL]], label %[[DELETE_END:.*]], label %[[DELETE_NOT_NULL:.*]] +// OGCG: [[DELETE_NOT_NULL]]: +// OGCG: %[[ALLOC_PTR:.*]] = getelementptr inbounds i8, ptr %[[PTR]], i64 -8 +// OGCG: %[[NUM_ELEM:.*]] = load i64, ptr %[[ALLOC_PTR]], align 4 +// OGCG: %[[ARR_END:.*]] = getelementptr inbounds %struct.Destructed, ptr %[[PTR]], i64 %[[NUM_ELEM]] +// OGCG: %[[ARR_IS_EMPTY:.*]] = icmp eq ptr %[[PTR]], %[[ARR_END]] +// OGCG: br i1 %[[ARR_IS_EMPTY]], label %[[ARRAY_DESTROY_DONE1:.*]], label %[[ARRAY_DESTROY_BODY:.*]] +// OGCG: [[ARRAY_DESTROY_BODY]]: +// OGCG: %[[ARRAY_DESTROY_ELEMENT_PAST:.*]] = phi ptr [ %[[ARR_END]], %[[DELETE_NOT_NULL]] ], [ %[[ARRAY_DESTROY_ELEMENT:.*]], %[[ARRAY_DESTROY_BODY]] ] +// OGCG: %[[ARRAY_DESTROY_ELEMENT]] = getelementptr inbounds %struct.Destructed, ptr %[[ARRAY_DESTROY_ELEMENT_PAST]], i64 -1 +// OGCG: call void @_ZN10DestructedD1Ev(ptr {{.*}} %[[ARRAY_DESTROY_ELEMENT]]) +// OGCG: %[[ARRAY_DESTROY_DONE:.*]] = icmp eq ptr %[[ARRAY_DESTROY_ELEMENT]], %[[PTR]] +// OGCG: br i1 %[[ARRAY_DESTROY_DONE]], label %[[ARRAY_DESTROY_DONE1:.*]], label %[[ARRAY_DESTROY_BODY]] +// OGCG: [[ARRAY_DESTROY_DONE1]]: +// OGCG: %[[ARRAY_SIZE:.*]] = mul i64 4, %[[NUM_ELEM]] +// OGCG: %[[TOTAL_SIZE:.*]] = add i64 %[[ARRAY_SIZE]], 8 +// OGCG: call void @_ZdaPvm(ptr {{.*}} %[[ALLOC_PTR]], i64 {{.*}} %[[TOTAL_SIZE]]) +// OGCG: br label %[[DELETE_END]] +// OGCG: [[DELETE_END]]: +// OGCG: ret void _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
