https://github.com/andykaylor created 
https://github.com/llvm/llvm-project/pull/184827

This introduces the cir.delete_array operation, adds code to emit that 
operation during CIR codegen, and adds lowering of the operation to the 
CXXABILowering pass.

The IR that will need to be executed to delete the array is embedded in the 
body of the operation. This is necessary in order to avoid preserving all the 
information that would be needed to fully generalize the delete call handling. 
However, there is still value in having a high-level operation because it will 
be able to abstract the details of reading the array cookie and calling 
destructors if needed.

This initial implementation does not yet support cases where a cookie and 
destructor calls are needed.

>From cdeed214abe4abe1a17f40ac5f278d081ae38cb6 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <[email protected]>
Date: Tue, 3 Mar 2026 12:19:30 -0800
Subject: [PATCH] [CIR] Add support for array delete handling

This introduces the cir.delete_array operation, adds code to emit that
operation during CIR codegen, and adds lowering of the operation to the
CXXABILowering pass.

The IR that will need to be executed to delete the array is embedded in the
body of the operation. This is necessary in order to avoid preserving all
the information that would be needed to fully generalize the delete call
handling. However, there is still value in having a high-level operation
because it will be able to abstract the details of reading the array cookie
and calling destructors if needed.

This initial implementation does not yet support cases where a cookie and
destructor calls are needed.
---
 clang/include/clang/CIR/Dialect/IR/CIROps.td  | 43 +++++++++++++++++--
 clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp       | 20 +++++++--
 .../CIR/Dialect/Transforms/CXXABILowering.cpp | 18 +++++++-
 clang/test/CIR/CodeGen/delete-array.cpp       | 38 ++++++++++++++++
 4 files changed, 112 insertions(+), 7 deletions(-)
 create mode 100644 clang/test/CIR/CodeGen/delete-array.cpp

diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td 
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 5db68b44c2804..dc642fc47a49a 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -936,9 +936,9 @@ def CIR_ConditionOp : CIR_Op<"condition", [
 
//===----------------------------------------------------------------------===//
 
 defvar CIR_YieldableScopes = [
-  "ArrayCtor", "ArrayDtor", "AwaitOp", "CaseOp", "CleanupScopeOp", "DoWhileOp",
-  "ForOp", "GlobalOp", "IfOp", "ScopeOp", "SwitchOp", "TernaryOp", "TryOp",
-  "WhileOp"
+  "ArrayCtor", "ArrayDtor", "AwaitOp", "CaseOp", "CleanupScopeOp",
+  "DeleteArrayOp", "DoWhileOp", "ForOp", "GlobalOp", "IfOp", "ScopeOp",
+  "SwitchOp", "TernaryOp", "TryOp", "WhileOp"
 ];
 
 def CIR_YieldOp : CIR_Op<"yield", [
@@ -3589,6 +3589,43 @@ def CIR_LLVMIntrinsicCallOp : 
CIR_Op<"call_llvm_intrinsic"> {
   ];
 }
 
+//===----------------------------------------------------------------------===//
+// DeleteArrayOp
+//===----------------------------------------------------------------------===//
+
+def CIR_DeleteArrayOp : CIR_Op<"delete_array"> {
+  let summary = "Delete address representing an array";
+  let description = [{
+    `cir.delete_array` operation deletes an array. For example, `delete[] ptr;`
+    will be translated to `cir.delete.array %ptr`.
+
+    The body region contains the deallocation call that will be used to free
+    the array memory.
+  }];
+
+  let arguments = (ins CIR_PointerType:$address);
+  let regions = (region SizedRegion<1>:$body);
+
+  let assemblyFormat = [{
+    $address `:` qualified(type($address)) $body attr-dict
+  }];
+
+  let builders = [
+    OpBuilder<(ins "mlir::Value":$address,
+                   "BuilderCallbackRef":$bodyBuilder), [{
+        assert(bodyBuilder && "builder callback expected");
+        mlir::OpBuilder::InsertionGuard guard($_builder);
+        mlir::Region *r = $_state.addRegion();
+        $_state.addOperands(ValueRange{address});
+        $_builder.createBlock(r);
+        bodyBuilder($_builder, $_state.location);
+    }]>
+  ];
+
+  let hasLLVMLowering = false;
+  let hasCXXABILowering = true;
+}
+
 
//===----------------------------------------------------------------------===//
 // CallOp and TryCallOp
 
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
index 97f496c89ab0f..f811362c2af18 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
@@ -1071,9 +1071,23 @@ void CIRGenFunction::emitCXXDeleteExpr(const 
CXXDeleteExpr *e) {
   }
 
   if (e->isArrayForm()) {
-    assert(!cir::MissingFeatures::deleteArray());
-    cgm.errorNYI(e->getSourceRange(), "emitCXXDeleteExpr: array delete");
-    return;
+    // To handle this for cases that requires array cookie, we will need to
+    // introduce a new operation in the array_delete body region to read
+    // the cookie and pass values returned by that operation to emitDeleteCall.
+    if (e->doesUsualArrayDeleteWantSize() || deleteTy.isDestructedType()) {
+      cgm.errorNYI(e->getSourceRange(),
+                   "emitCXXDeleteExpr: array delete requires cookies");
+    }
+    cir::DeleteArrayOp::create(
+        builder, ptr.getPointer().getLoc(), ptr.getPointer(),
+        [&](mlir::OpBuilder &b, mlir::Location loc) {
+          // We want to have a high-level representation of the array delete,
+          // but proper handling of the delete call details need to be handled
+          // here. As a compromise, we emit the delete call into the body 
region
+          // of the delete_array operation.
+          emitDeleteCall(e->getOperatorDelete(), ptr.getPointer(), deleteTy);
+          cir::YieldOp::create(builder, loc);
+        });
   } 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 b76dee98713cc..6f38f4388146e 100644
--- a/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp
@@ -58,7 +58,7 @@ class CIRGenericCXXABILoweringPattern : public 
mlir::ConversionPattern {
                   mlir::ConversionPatternRewriter &rewriter) const override {
     // Do not match on operations that have dedicated ABI lowering rewrite 
rules
     if (llvm::isa<cir::AllocaOp, cir::BaseDataMemberOp, cir::BaseMethodOp,
-                  cir::CastOp, cir::CmpOp, cir::ConstantOp,
+                  cir::CastOp, cir::CmpOp, cir::ConstantOp, cir::DeleteArrayOp,
                   cir::DerivedDataMemberOp, cir::DerivedMethodOp, cir::FuncOp,
                   cir::GetMethodOp, cir::GetRuntimeMemberOp, 
cir::GlobalOp>(op))
       return mlir::failure();
@@ -320,6 +320,19 @@ mlir::LogicalResult 
CIRBaseMethodOpABILowering::matchAndRewrite(
   return mlir::success();
 }
 
+mlir::LogicalResult CIRDeleteArrayOpABILowering::matchAndRewrite(
+    cir::DeleteArrayOp op, OpAdaptor adaptor,
+    mlir::ConversionPatternRewriter &rewriter) const {
+  mlir::Block *body = &op.getBody().front();
+  rewriter.eraseOp(body->getTerminator());
+  rewriter.inlineBlockBefore(body, op);
+  // TODO(cir): If the deleted type is a destructed type, we need to emit
+  // array destroy code here. That's currently implemented in LoweringPrepare,
+  // but it will make sense to move it into this pass when we are ready for it.
+  rewriter.eraseOp(op);
+  return mlir::success();
+}
+
 mlir::LogicalResult CIRDerivedDataMemberOpABILowering::matchAndRewrite(
     cir::DerivedDataMemberOp op, OpAdaptor adaptor,
     mlir::ConversionPatternRewriter &rewriter) const {
@@ -454,6 +467,9 @@ populateCXXABIConversionTarget(mlir::ConversionTarget 
&target,
       [&typeConverter](cir::GlobalOp op) {
         return typeConverter.isLegal(op.getSymType());
       });
+  // Operations that do not use any special types must be explicitly marked as
+  // illegal to trigger processing here.
+  target.addIllegalOp<cir::DeleteArrayOp>();
   target.addIllegalOp<cir::DynamicCastOp>();
   target.addIllegalOp<cir::VTableGetTypeInfoOp>();
 }
diff --git a/clang/test/CIR/CodeGen/delete-array.cpp 
b/clang/test/CIR/CodeGen/delete-array.cpp
new file mode 100644
index 0000000000000..a1dab3253c026
--- /dev/null
+++ b/clang/test/CIR/CodeGen/delete-array.cpp
@@ -0,0 +1,38 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -fclangir 
-emit-cir -mmlir -mlir-print-ir-before=cir-cxxabi-lowering %s -o %t.cir 2> 
%t-before.cir
+// RUN: FileCheck --input-file=%t-before.cir -check-prefix=CIR-BEFORE %s
+// RUN: FileCheck --input-file=%t.cir --check-prefix=CIR %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -fclangir 
-emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll --check-prefix=LLVM %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -emit-llvm %s 
-o %t.ll
+// RUN: FileCheck --input-file=%t.ll --check-prefix=OGCG %s
+
+void test_delete_array(int *ptr) {
+  delete[] ptr;
+}
+
+// CIR-BEFORE: cir.func {{.*}} @_Z17test_delete_arrayPi
+// CIR-BEFORE:   %[[PTR:.*]] = cir.load{{.*}} %{{.*}}
+// CIR-BEFORE:   cir.delete_array %[[PTR]] : !cir.ptr<!s32i> {
+// CIR-BEFORE:     %[[VOID_PTR:.*]] = cir.cast bitcast %[[PTR]] : 
!cir.ptr<!s32i> -> !cir.ptr<!void>
+// CIR-BEFORE:     cir.call @_ZdaPv(%[[VOID_PTR]]) {{.*}} : 
(!cir.ptr<!void>{{.*}}) -> ()
+// CIR-BEFORE:     cir.yield
+// CIR-BEFORE:   }
+
+// CIR: cir.func {{.*}} @_Z17test_delete_arrayPi
+// CIR:   %[[PTR:.*]] = cir.load{{.*}} %{{.*}}
+// CIR:   %[[VOID_PTR:.*]] = cir.cast bitcast %[[PTR]] : !cir.ptr<!s32i> -> 
!cir.ptr<!void>
+// CIR:   cir.call @_ZdaPv(%[[VOID_PTR]])
+
+// LLVM: define {{.*}} void @_Z17test_delete_arrayPi
+// LLVM:   %[[PTR:.*]] = load ptr, ptr %{{.*}}
+// LLVM:   call void @_ZdaPv(ptr {{.*}} %[[PTR]])
+
+// OGCG: define {{.*}} void @_Z17test_delete_arrayPi
+// 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:   call void @_ZdaPv(ptr {{.*}} %[[PTR]])
+// 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

Reply via email to