https://github.com/erichkeane created 
https://github.com/llvm/llvm-project/pull/216789

This ends up being a pretty trivial implementation, since we just have to add 
the additional i64 argument to the call. This patch adds that, after threading 
the alignment through CIR.

Also, the type-aware and destroying deletes aren't valid on a array delete, so 
replace those checks with 'assert' instead.

>From a5d546c1559506de790af6f05d2c05e749612b47 Mon Sep 17 00:00:00 2001
From: erichkeane <[email protected]>
Date: Mon, 17 Aug 2026 10:09:22 -0700
Subject: [PATCH] [CIR] Implement aligned-array-delete.

This ends up being a pretty trivial implementation, since we just have
to add the additional i64 argument to the call. This patch adds that,
after threading the alignment through CIR.

Also, the type-aware and destroying deletes aren't valid on a
array delete, so replace those checks with 'assert' instead.
---
 .../include/clang/CIR/Dialect/IR/CIRAttrs.td  |   2 +-
 clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp       |  10 +-
 .../CIR/Dialect/Transforms/CXXABILowering.cpp |  14 +-
 .../test/CIR/CodeGen/delete-array-aligned.cpp | 140 ++++++++++++++++++
 4 files changed, 160 insertions(+), 6 deletions(-)
 create mode 100644 clang/test/CIR/CodeGen/delete-array-aligned.cpp

diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td 
b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
index 29e82b9392b82..bf869ec4f99ea 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
@@ -1959,7 +1959,7 @@ def CIR_UsualDeleteParamsAttr
 
   let parameters = (ins
     DefaultValuedParameter<"bool", "false">:$size,
-    DefaultValuedParameter<"bool", "false">:$alignment,
+    OptionalParameter<"std::optional<uint64_t>">:$alignment,
     DefaultValuedParameter<"bool", "false">:$type_aware_delete,
     DefaultValuedParameter<"bool", "false">:$destroying_delete
   );
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
index 43c210b44f011..cf259a9805203 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
@@ -1526,8 +1526,16 @@ void CIRGenFunction::emitCXXDeleteExpr(const 
CXXDeleteExpr *e) {
     auto deleteFn =
         mlir::FlatSymbolRefAttr::get(operatorDeleteFn.getSymNameAttr());
     UsualDeleteParams udp = operatorDelete->getUsualDeleteParams();
+    std::optional<uint64_t> align;
+
+    if (isAlignedAllocation(udp.Alignment)) {
+      CharUnits elementSize = cgm.getASTContext().getTypeSizeInChars(deleteTy);
+      align =
+          
ptr.getAlignment().alignmentOfArrayElement(elementSize).getQuantity();
+    }
+
     auto deleteParams = cir::UsualDeleteParamsAttr::get(
-        builder.getContext(), udp.Size, isAlignedAllocation(udp.Alignment),
+        builder.getContext(), udp.Size, align,
         isTypeAwareAllocation(udp.TypeAwareDelete), udp.DestroyingDelete);
 
     mlir::FlatSymbolRefAttr elementDtor;
diff --git a/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp 
b/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp
index e8e7f4d87346c..a23a0616aa044 100644
--- a/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp
@@ -636,10 +636,10 @@ mlir::LogicalResult 
CIRDeleteArrayOpABILowering::matchAndRewrite(
   cir::UsualDeleteParamsAttr deleteParams = op.getDeleteParams();
   bool cookieRequired = deleteParams.getSize() || op.getElementDtorAttr();
 
-  if (deleteParams.getTypeAwareDelete() || deleteParams.getDestroyingDelete() 
||
-      deleteParams.getAlignment())
-    return rewriter.notifyMatchFailure(
-        op, "type-aware, destroying, or aligned delete not yet supported");
+  assert(!deleteParams.getDestroyingDelete() &&
+         "destroying delete not legal on arrays");
+  assert(!deleteParams.getTypeAwareDelete() &&
+         "type-aware delete not legal on arrays");
 
   const CIRCXXABI &cxxABI = lowerModule->getCXXABI();
   CIRBaseBuilderTy cirBuilder(rewriter);
@@ -716,6 +716,12 @@ mlir::LogicalResult 
CIRDeleteArrayOpABILowering::matchAndRewrite(
               cir::AddOp::create(b, l, sizeTy, allocSize, cookieSizeVal);
           callArgs.push_back(allocSize);
         }
+        if (deleteParams.getAlignment()) {
+          auto alignVal = cir::ConstantOp::create(
+              b, l, cir::IntAttr::get(sizeTy, *deleteParams.getAlignment()));
+          callArgs.push_back(alignVal);
+        }
+
         auto deleteCall =
             cir::CallOp::create(b, l, deleteFn, cir::VoidType(), callArgs);
         // operator delete[] is implicitly nothrow per [basic.stc.dynamic],
diff --git a/clang/test/CIR/CodeGen/delete-array-aligned.cpp 
b/clang/test/CIR/CodeGen/delete-array-aligned.cpp
new file mode 100644
index 0000000000000..41372cb1c1be8
--- /dev/null
+++ b/clang/test/CIR/CodeGen/delete-array-aligned.cpp
@@ -0,0 +1,140 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -fclangir 
-mconstructor-aliases -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,CIR-BEFORE %s
+// RUN: FileCheck --input-file=%t.cir --check-prefix=CIR,CIR-AFTER %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -fclangir 
-mconstructor-aliases -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++17 
-mconstructor-aliases -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll --check-prefix=LLVM %s
+
+typedef decltype(sizeof(0)) size_t;
+namespace std { enum class align_val_t : size_t {}; }
+
+struct alignas(128) OverAlignedTy {
+  OverAlignedTy();
+  void* v;
+};
+void test_global_aligned(OverAlignedTy *p) { delete[] p; }
+// CIR-LABEL: cir.func no_inline dso_local 
@_Z19test_global_alignedP13OverAlignedTy(
+// CIR-BEFORE: cir.delete_array %{{.*}} : !cir.ptr<!rec_OverAlignedTy> 
{delete_fn = @_ZdaPvSt11align_val_t, delete_params = 
#cir.usual_delete_params<alignment = 128>}
+
+// CIR-AFTER:      %[[PTR:.*]] = cir.cast bitcast %{{.*}} : 
!cir.ptr<!rec_OverAlignedTy> -> !cir.ptr<!void>
+// CIR-AFTER-NEXT: cir.cleanup.scope {
+// CIR-AFTER-NEXT:   cir.yield
+// CIR-AFTER-NEXT: } cleanup normal {
+// CIR-AFTER-NEXT:   %[[ALIGN:.*]] = cir.const #cir.int<128> : !u64i
+// CIR-AFTER-NEXT:   cir.call @_ZdaPvSt11align_val_t(%[[PTR]], %[[ALIGN]]) 
nothrow : (!cir.ptr<!void>, !u64i) -> ()
+// CIR-AFTER-NEXT:   cir.yield
+// CIR-AFTER-NEXT: }
+
+// LLVM-LABEL: define dso_local void @_Z19test_global_alignedP13OverAlignedTy(
+// LLVM: call void @_ZdaPvSt11align_val_t(ptr {{(noundef )?}}%{{.*}}, i64 
{{(noundef )?}}128)
+
+struct alignas(128) OverAlignedTyClassDelete {
+  OverAlignedTyClassDelete();
+  void operator delete[](void *, std::align_val_t);
+  void *v;
+};
+void test_class_align_only(OverAlignedTyClassDelete *p) { delete[] p; }
+// CIR-LABEL: cir.func no_inline dso_local 
@_Z21test_class_align_onlyP24OverAlignedTyClassDelete(
+// CIR-BEFORE: cir.delete_array %{{.*}} : 
!cir.ptr<!rec_OverAlignedTyClassDelete> {delete_fn = 
@_ZN24OverAlignedTyClassDeletedaEPvSt11align_val_t, delete_params = 
#cir.usual_delete_params<alignment = 128>}
+// CIR-AFTER: %[[PTR:.*]] = cir.cast bitcast %{{.*}} : 
!cir.ptr<!rec_OverAlignedTyClassDelete> -> !cir.ptr<!void>
+// CIR-AFTER-NEXT: cir.cleanup.scope {
+// CIR-AFTER-NEXT:   cir.yield
+// CIR-AFTER-NEXT: } cleanup normal {
+// CIR-AFTER-NEXT:   %[[ALIGN:.*]] = cir.const #cir.int<128> : !u64i
+// CIR-AFTER-NEXT:   cir.call 
@_ZN24OverAlignedTyClassDeletedaEPvSt11align_val_t(%[[PTR]], %[[ALIGN]]) 
nothrow : (!cir.ptr<!void>, !u64i) -> ()
+// CIR-AFTER-NEXT:   cir.yield
+// CIR-AFTER-NEXT: }
+
+// LLVM-LABEL: define dso_local void 
@_Z21test_class_align_onlyP24OverAlignedTyClassDelete(
+// LLVM: call void @_ZN24OverAlignedTyClassDeletedaEPvSt11align_val_t(ptr 
{{(noundef )?}}%{{.*}}, i64 {{(noundef )?}}128)
+
+struct alignas(128) OverAlignedTyClassDeleteCookie {
+  OverAlignedTyClassDeleteCookie();
+  void operator delete[](void *, size_t, std::align_val_t);
+  void *v;
+};
+void test_class_size_align(OverAlignedTyClassDeleteCookie *p) { delete[] p; }
+// CIR-LABEL: cir.func no_inline dso_local 
@_Z21test_class_size_alignP30OverAlignedTyClassDeleteCookie(
+// CIR-BEFORE: cir.delete_array %{{.*}} : 
!cir.ptr<!rec_OverAlignedTyClassDeleteCookie> {delete_fn = 
@_ZN30OverAlignedTyClassDeleteCookiedaEPvmSt11align_val_t, delete_params = 
#cir.usual_delete_params<size = true, alignment = 128>}
+// CIR-AFTER:      %[[ORIG_PTR:.*]] = cir.cast bitcast %{{.*}} : 
!cir.ptr<!rec_OverAlignedTyClassDeleteCookie> -> !cir.ptr<!u8i>
+// CIR-AFTER:      %[[COOKIE_STRIDE:.*]] = cir.ptr_stride %[[ORIG_PTR]], 
%{{.*}} : (!cir.ptr<!u8i>, !s64i) -> !cir.ptr<!u8i>
+// CIR-AFTER-NEXT: %[[PTR:.*]] = cir.cast bitcast %[[COOKIE_STRIDE]] : 
!cir.ptr<!u8i> -> !cir.ptr<!void>
+// CIR-AFTER:      cir.cleanup.scope {
+// CIR-AFTER-NEXT:   cir.yield
+// CIR-AFTER-NEXT: } cleanup normal {
+// CIR-AFTER:        %[[SIZE:.*]] = cir.add %{{.*}}, %{{.*}} : !u64i
+// CIR-AFTER-NEXT:   %[[ALIGN:.*]] = cir.const #cir.int<128> : !u64i
+// CIR-AFTER-NEXT:   cir.call 
@_ZN30OverAlignedTyClassDeleteCookiedaEPvmSt11align_val_t(%[[PTR]], %[[SIZE]], 
%[[ALIGN]]) nothrow : (!cir.ptr<!void>, !u64i, !u64i) -> ()
+// CIR-AFTER-NEXT:   cir.yield
+// CIR-AFTER-NEXT: }
+
+// LLVM-LABEL: define dso_local void 
@_Z21test_class_size_alignP30OverAlignedTyClassDeleteCookie(
+// LLVM: call void 
@_ZN30OverAlignedTyClassDeleteCookiedaEPvmSt11align_val_t(ptr {{(noundef 
)?}}%{{.*}}, i64 {{(noundef )?}}%{{.*}}, i64 {{(noundef )?}}128)
+
+struct alignas(128) OverAlignedTyClassDeleteDtor {
+  OverAlignedTyClassDeleteDtor();
+  ~OverAlignedTyClassDeleteDtor();
+  void operator delete[](void *, std::align_val_t);
+  void *v;
+};
+void test_class_align_only_dtor(OverAlignedTyClassDeleteDtor *p) { delete[] p; 
}
+// CIR-LABEL: cir.func no_inline dso_local 
@_Z26test_class_align_only_dtorP28OverAlignedTyClassDeleteDtor(
+// CIR-BEFORE: cir.delete_array %{{.*}} : 
!cir.ptr<!rec_OverAlignedTyClassDeleteDtor> {delete_fn = 
@_ZN28OverAlignedTyClassDeleteDtordaEPvSt11align_val_t, delete_params = 
#cir.usual_delete_params<alignment = 128>, element_dtor = 
@_ZN28OverAlignedTyClassDeleteDtorD1Ev}
+// CIR-AFTER:      %[[ORIG_PTR:.*]] = cir.cast bitcast %{{.*}} : 
!cir.ptr<!rec_OverAlignedTyClassDeleteDtor> -> !cir.ptr<!u8i>
+// CIR-AFTER:      %[[COOKIE_STRIDE:.*]] = cir.ptr_stride %[[ORIG_PTR]], 
%{{.*}} : (!cir.ptr<!u8i>, !s64i) -> !cir.ptr<!u8i>
+// CIR-AFTER-NEXT: %[[PTR:.*]] = cir.cast bitcast %[[COOKIE_STRIDE]] : 
!cir.ptr<!u8i> -> !cir.ptr<!void>
+// CIR-AFTER:      cir.cleanup.scope {
+// CIR-AFTER:        cir.if %{{.*}} {
+// CIR-AFTER:          cir.do {
+// CIR-AFTER:            cir.call 
@_ZN28OverAlignedTyClassDeleteDtorD1Ev(%{{.*}}) nothrow : 
(!cir.ptr<!rec_OverAlignedTyClassDeleteDtor>) -> ()
+// CIR-AFTER-NEXT:       cir.yield
+// CIR-AFTER-NEXT:     } while {
+// CIR-AFTER:            cir.condition(%{{.*}})
+// CIR-AFTER-NEXT:     }
+// CIR-AFTER-NEXT:   }
+// CIR-AFTER-NEXT:   cir.yield
+// CIR-AFTER-NEXT: } cleanup normal {
+// CIR-AFTER-NEXT:   %[[ALIGN:.*]] = cir.const #cir.int<128> : !u64i
+// CIR-AFTER-NEXT:   cir.call 
@_ZN28OverAlignedTyClassDeleteDtordaEPvSt11align_val_t(%[[PTR]], %[[ALIGN]]) 
nothrow : (!cir.ptr<!void>, !u64i) -> ()
+// CIR-AFTER-NEXT:   cir.yield
+// CIR-AFTER-NEXT: }
+
+// LLVM-LABEL: define dso_local void 
@_Z26test_class_align_only_dtorP28OverAlignedTyClassDeleteDtor(
+// LLVM: call void @_ZN28OverAlignedTyClassDeleteDtorD1Ev(ptr {{.*}}%{{.*}})
+// LLVM: call void @_ZN28OverAlignedTyClassDeleteDtordaEPvSt11align_val_t(ptr 
{{(noundef )?}}%{{.*}}, i64 {{(noundef )?}}128)
+
+struct alignas(128) OverAlignedTyClassDeleteCookieDtor {
+  OverAlignedTyClassDeleteCookieDtor();
+  ~OverAlignedTyClassDeleteCookieDtor();
+  void operator delete[](void *, size_t, std::align_val_t);
+  void *v;
+};
+void test_class_size_align_dtor(OverAlignedTyClassDeleteCookieDtor *p) {
+  delete[] p;
+}
+// CIR-LABEL: cir.func no_inline dso_local 
@_Z26test_class_size_align_dtorP34OverAlignedTyClassDeleteCookieDtor(
+// CIR-BEFORE: cir.delete_array %{{.*}} : 
!cir.ptr<!rec_OverAlignedTyClassDeleteCookieDtor> {delete_fn = 
@_ZN34OverAlignedTyClassDeleteCookieDtordaEPvmSt11align_val_t, delete_params = 
#cir.usual_delete_params<size = true, alignment = 128>, element_dtor = 
@_ZN34OverAlignedTyClassDeleteCookieDtorD1Ev}
+// CIR-AFTER:      %[[ORIG_PTR:.*]] = cir.cast bitcast %{{.*}} : 
!cir.ptr<!rec_OverAlignedTyClassDeleteCookieDtor> -> !cir.ptr<!u8i>
+// CIR-AFTER:      %[[COOKIE_STRIDE:.*]] = cir.ptr_stride %[[ORIG_PTR]], 
%{{.*}} : (!cir.ptr<!u8i>, !s64i) -> !cir.ptr<!u8i>
+// CIR-AFTER-NEXT: %[[PTR:.*]] = cir.cast bitcast %[[COOKIE_STRIDE]] : 
!cir.ptr<!u8i> -> !cir.ptr<!void>
+// CIR-AFTER:      cir.cleanup.scope {
+// CIR-AFTER:       cir.if %{{.*}} {
+// CIR-AFTER:           cir.do {
+// CIR-AFTER:            cir.call 
@_ZN34OverAlignedTyClassDeleteCookieDtorD1Ev(%{{.*}}) nothrow : 
(!cir.ptr<!rec_OverAlignedTyClassDeleteCookieDtor>) -> ()
+// CIR-AFTER-NEXT:       cir.yield
+// CIR-AFTER-NEXT:     } while {
+// CIR-AFTER:            cir.condition(%{{.*}})
+// CIR-AFTER-NEXT:     }
+// CIR-AFTER-NEXT:   }
+// CIR-AFTER-NEXT:   cir.yield
+// CIR-AFTER-NEXT: } cleanup normal {
+// CIR-AFTER:        %[[SIZE:.*]] = cir.add %{{.*}}, %{{.*}} : !u64i
+// CIR-AFTER-NEXT:   %[[ALIGN:.*]] = cir.const #cir.int<128> : !u64i
+// CIR-AFTER-NEXT:   cir.call 
@_ZN34OverAlignedTyClassDeleteCookieDtordaEPvmSt11align_val_t(%7, %[[SIZE]], 
%[[ALIGN]]) nothrow : (!cir.ptr<!void>, !u64i, !u64i) -> ()
+// CIR-AFTER-NEXT:   cir.yield
+// CIR-AFTER-NEXT: }
+
+// LLVM-LABEL: define dso_local void 
@_Z26test_class_size_align_dtorP34OverAlignedTyClassDeleteCookieDtor(
+// LLVM: call void @_ZN34OverAlignedTyClassDeleteCookieDtorD1Ev(ptr 
{{.*}}%{{.*}})
+// LLVM: call void 
@_ZN34OverAlignedTyClassDeleteCookieDtordaEPvmSt11align_val_t(ptr {{(noundef 
)?}}%{{.*}}, i64 {{(noundef )?}}%{{.*}}, i64 {{(noundef )?}}128)

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to