[clang] ec93414 - [CIR] Implement array delete for destructed types (#186248)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Mar 12 16:55:54 PDT 2026
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
More information about the cfe-commits
mailing list