https://github.com/andykaylor updated 
https://github.com/llvm/llvm-project/pull/185987

>From 4826410b209ef3201fac8eb207ec4caeea79669c Mon Sep 17 00:00:00 2001
From: Andy Kaylor <[email protected]>
Date: Wed, 11 Mar 2026 14:41:23 -0700
Subject: [PATCH 1/2] [CIR] Add support for null checks with operator delete

This implements a check for null pointers when processing operator delete.
This is especially important now that we have implemented array delete
because we need to read from the pointer location in that case.
---
 .../CIR/Dialect/Builder/CIRBaseBuilder.h      |  5 ++
 clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp       | 22 ++++--
 clang/test/CIR/CodeGen/delete-array.cpp       | 78 +++++++++++++-----
 clang/test/CIR/CodeGen/delete.cpp             | 79 ++++++++++++++++---
 4 files changed, 146 insertions(+), 38 deletions(-)

diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h 
b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
index f51bea894d2ae..409f49f95f816 100644
--- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
+++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
@@ -520,6 +520,11 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
     return createCompare(ptr.getLoc(), cir::CmpOpKind::eq, ptr, nullPtr);
   }
 
+  mlir::Value createPtrNotNull(mlir::Value ptr) {
+    mlir::Value nullPtr = getNullPtr(ptr.getType(), ptr.getLoc());
+    return createCompare(ptr.getLoc(), cir::CmpOpKind::ne, ptr, nullPtr);
+  }
+
   mlir::Value createAddrSpaceCast(mlir::Location loc, mlir::Value src,
                                   mlir::Type newTy) {
     return createCast(loc, cir::CastKind::address_space, src, newTy);
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
index 72c3c67152771..e7ce74b6aab54 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
@@ -1182,11 +1182,6 @@ static void emitObjectDelete(CIRGenFunction &cgf, const 
CXXDeleteExpr *de,
     cgf.cgm.errorNYI(de->getSourceRange(), "emitObjectDelete: ObjCLifetime");
   }
 
-  // In traditional LLVM codegen null checks are emitted to save a delete call.
-  // In CIR we optimize for size by default, the null check should be added 
into
-  // this function callers.
-  assert(!cir::MissingFeatures::emitNullCheckForDeleteCalls());
-
   cgf.popCleanupBlock();
 }
 
@@ -1201,10 +1196,21 @@ void CIRGenFunction::emitCXXDeleteExpr(const 
CXXDeleteExpr *e) {
   // unconditionally perform the operator delete call in that case. For now, we
   // assume that deleted pointers are null rarely enough that it's better to
   // keep the branch. This might be worth revisiting for a -O0 code size win.
-  //
-  // CIR note: emit the code size friendly by default for now, such as 
mentioned
-  // in `emitObjectDelete`.
   assert(!cir::MissingFeatures::emitNullCheckForDeleteCalls());
+  cir::YieldOp thenYield;
+  mlir::Value notNull = builder.createPtrNotNull(ptr.getPointer());
+  cir::IfOp::create(builder, getLoc(e->getExprLoc()), notNull,
+                    /*withElseRegion=*/false,
+                    /*thenBuilder=*/
+                    [&](mlir::OpBuilder &b, mlir::Location loc) {
+                      thenYield = builder.createYield(loc);
+                    });
+
+  // Emit the rest of the CIR inside the if-op's then region, but restore the
+  // insertion point to the point after the if when this function returns.
+  mlir::OpBuilder::InsertionGuard guard(builder);
+  builder.setInsertionPoint(thenYield);
+
   QualType deleteTy = e->getDestroyedType();
 
   // A destroying operator delete overrides the entire operation of the
diff --git a/clang/test/CIR/CodeGen/delete-array.cpp 
b/clang/test/CIR/CodeGen/delete-array.cpp
index ed776ce0e1008..117943f005a0a 100644
--- a/clang/test/CIR/CodeGen/delete-array.cpp
+++ b/clang/test/CIR/CodeGen/delete-array.cpp
@@ -12,16 +12,30 @@ void test_delete_array(int *ptr) {
 
 // CIR-BEFORE: cir.func {{.*}} @_Z17test_delete_arrayPi
 // CIR-BEFORE:   %[[PTR:.*]] = cir.load{{.*}} %{{.*}}
-// CIR-BEFORE:   cir.delete_array %[[PTR]] : !cir.ptr<!s32i> {delete_fn = 
@_ZdaPv, delete_params = #cir.usual_delete_params<>
+// CIR-BEFORE:   %[[NULL:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!s32i>
+// CIR-BEFORE:   %[[NOT_NULL:.*]] = cir.cmp ne %[[PTR]], %[[NULL]] : 
!cir.ptr<!s32i>
+// CIR-BEFORE:   cir.if %[[NOT_NULL]] {
+// CIR-BEFORE:     cir.delete_array %[[PTR]] : !cir.ptr<!s32i> {delete_fn = 
@_ZdaPv, delete_params = #cir.usual_delete_params<>
+// 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]])
+// CIR:   %[[NULL:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!s32i>
+// CIR:   %[[NOT_NULL:.*]] = cir.cmp ne %[[PTR]], %[[NULL]] : !cir.ptr<!s32i>
+// CIR:   cir.if %[[NOT_NULL]] {
+// CIR:     %[[VOID_PTR:.*]] = cir.cast bitcast %[[PTR]] : !cir.ptr<!s32i> -> 
!cir.ptr<!void>
+// CIR:     cir.call @_ZdaPv(%[[VOID_PTR]])
+// CIR:   }
 
 // LLVM: define {{.*}} void @_Z17test_delete_arrayPi
 // LLVM:   %[[PTR:.*]] = load ptr, ptr %{{.*}}
+// LLVM:   %[[NOT_NULL:.*]] = icmp ne ptr %[[PTR]], null
+// LLVM:   br i1 %[[NOT_NULL]], label %[[DELETE_NOTNULL:.*]], label 
%[[DELETE_END:.*]]
+// LLVM: [[DELETE_NOTNULL]]:
 // LLVM:   call void @_ZdaPv(ptr %[[PTR]])
+// LLVM:   br label %[[DELETE_END]]
+// LLVM: [[DELETE_END]]:
+// LLVM:   ret void
 
 // OGCG: define {{.*}} void @_Z17test_delete_arrayPi
 // OGCG:   %[[PTR:.*]] = load ptr, ptr %{{.*}}
@@ -43,16 +57,30 @@ void test_simple_delete_array(SimpleArrDelete *ptr) {
 
 // CIR-BEFORE: cir.func {{.*}} @_Z24test_simple_delete_arrayP15SimpleArrDelete
 // CIR-BEFORE:   %[[PTR:.*]] = cir.load{{.*}} %{{.*}}
-// CIR-BEFORE:   cir.delete_array %[[PTR]] : !cir.ptr<!rec_SimpleArrDelete> 
{delete_fn = @_ZN15SimpleArrDeletedaEPv, delete_params = 
#cir.usual_delete_params<>
+// CIR-BEFORE:   %[[NULL:.*]] = cir.const #cir.ptr<null> : 
!cir.ptr<!rec_SimpleArrDelete>
+// CIR-BEFORE:   %[[NOT_NULL:.*]] = cir.cmp ne %[[PTR]], %[[NULL]] : 
!cir.ptr<!rec_SimpleArrDelete>
+// CIR-BEFORE:   cir.if %[[NOT_NULL]] {
+// CIR-BEFORE:     cir.delete_array %[[PTR]] : !cir.ptr<!rec_SimpleArrDelete> 
{delete_fn = @_ZN15SimpleArrDeletedaEPv, delete_params = 
#cir.usual_delete_params<>
+// CIR-BEFORE:   }
 
 // CIR: cir.func {{.*}} @_Z24test_simple_delete_arrayP15SimpleArrDelete
 // CIR:   %[[PTR:.*]] = cir.load{{.*}} %{{.*}}
-// CIR:   %[[VOID_PTR:.*]] = cir.cast bitcast %[[PTR]] : 
!cir.ptr<!rec_SimpleArrDelete> -> !cir.ptr<!void>
-// CIR:   cir.call @_ZN15SimpleArrDeletedaEPv(%[[VOID_PTR]])
+// CIR:   %[[NULL:.*]] = cir.const #cir.ptr<null> : 
!cir.ptr<!rec_SimpleArrDelete>
+// CIR:   %[[NOT_NULL:.*]] = cir.cmp ne %[[PTR]], %[[NULL]] : 
!cir.ptr<!rec_SimpleArrDelete>
+// CIR:   cir.if %[[NOT_NULL]] {
+// CIR:     %[[VOID_PTR:.*]] = cir.cast bitcast %[[PTR]] : 
!cir.ptr<!rec_SimpleArrDelete> -> !cir.ptr<!void>
+// CIR:     cir.call @_ZN15SimpleArrDeletedaEPv(%[[VOID_PTR]])
+// CIR:   }
 
 // LLVM: define {{.*}} void @_Z24test_simple_delete_arrayP15SimpleArrDelete
 // LLVM:   %[[PTR:.*]] = load ptr, ptr %{{.*}}
+// LLVM:   %[[NOT_NULL:.*]] = icmp ne ptr %[[PTR]], null
+// LLVM:   br i1 %[[NOT_NULL]], label %[[DELETE_NOTNULL:.*]], label 
%[[DELETE_END:.*]]
+// LLVM: [[DELETE_NOTNULL]]:
 // LLVM:   call void @_ZN15SimpleArrDeletedaEPv(ptr %[[PTR]])
+// LLVM:   br label %[[DELETE_END]]
+// LLVM: [[DELETE_END]]:
+// LLVM:   ret void
 
 // OGCG: define {{.*}} void @_Z24test_simple_delete_arrayP15SimpleArrDelete
 // OGCG:   %[[PTR:.*]] = load ptr, ptr %{{.*}}
@@ -75,29 +103,43 @@ void test_sized_array_delete(SizedArrayDelete *ptr) {
 
 // CIR-BEFORE: cir.func {{.*}} @_Z23test_sized_array_deleteP16SizedArrayDelete
 // CIR-BEFORE:   %[[PTR:.*]] = cir.load{{.*}} %{{.*}}
-// CIR-BEFORE:   cir.delete_array %[[PTR]] : !cir.ptr<!rec_SizedArrayDelete> 
{delete_fn = @_ZN16SizedArrayDeletedaEPvm, delete_params = 
#cir.usual_delete_params<size = true>
+// CIR-BEFORE:   %[[NULL:.*]] = cir.const #cir.ptr<null> : 
!cir.ptr<!rec_SizedArrayDelete>
+// CIR-BEFORE:   %[[NOT_NULL:.*]] = cir.cmp ne %[[PTR]], %[[NULL]] : 
!cir.ptr<!rec_SizedArrayDelete>
+// CIR-BEFORE:   cir.if %[[NOT_NULL]] {
+// CIR-BEFORE:     cir.delete_array %[[PTR]] : !cir.ptr<!rec_SizedArrayDelete> 
{delete_fn = @_ZN16SizedArrayDeletedaEPvm, delete_params = 
#cir.usual_delete_params<size = true>
+// CIR-BEFORE:   }
 
 // CIR: cir.func {{.*}} @_Z23test_sized_array_deleteP16SizedArrayDelete
 // CIR:   %[[PTR:.*]] = cir.load{{.*}} %{{.*}}
-// CIR:   %[[BYTE_PTR:.*]] = cir.cast bitcast %[[PTR]] : 
!cir.ptr<!rec_SizedArrayDelete> -> !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 align(4) %[[COOKIE_PTR]] : 
!cir.ptr<!u64i>, !u64i
-// 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 @_ZN16SizedArrayDeletedaEPvm(%[[VOID_PTR]], %[[TOTAL_SIZE]])
+// CIR:   %[[NULL:.*]] = cir.const #cir.ptr<null> : 
!cir.ptr<!rec_SizedArrayDelete>
+// CIR:   %[[NOT_NULL:.*]] = cir.cmp ne %[[PTR]], %[[NULL]] : 
!cir.ptr<!rec_SizedArrayDelete>
+// CIR:   cir.if %[[NOT_NULL]] {
+// CIR:     %[[BYTE_PTR:.*]] = cir.cast bitcast %[[PTR]] : 
!cir.ptr<!rec_SizedArrayDelete> -> !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 align(4) %[[COOKIE_PTR]] : 
!cir.ptr<!u64i>, !u64i
+// 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 @_ZN16SizedArrayDeletedaEPvm(%[[VOID_PTR]], 
%[[TOTAL_SIZE]])
+// CIR:   }
 
 // LLVM: define {{.*}} void @_Z23test_sized_array_deleteP16SizedArrayDelete
 // LLVM:   %[[PTR:.*]] = load ptr, ptr %{{.*}}
+// LLVM:   %[[NOT_NULL:.*]] = icmp ne ptr %[[PTR]], null
+// LLVM:   br i1 %[[NOT_NULL]], label %[[DELETE_NOTNULL:.*]], label 
%[[DELETE_END:.*]]
+// LLVM: [[DELETE_NOTNULL]]:
 // LLVM:   %[[ALLOC_PTR:.*]] = getelementptr i8, ptr %[[PTR]], i64 -8
 // LLVM:   %[[NUM_ELEM:.*]] = load i64, ptr %[[ALLOC_PTR]], align 4
 // LLVM:   %[[ARRAY_SIZE:.*]] = mul i64 4, %[[NUM_ELEM]]
 // LLVM:   %[[TOTAL_SIZE:.*]] = add i64 %[[ARRAY_SIZE]], 8
 // LLVM:   call void @_ZN16SizedArrayDeletedaEPvm(ptr %[[ALLOC_PTR]], i64 
%[[TOTAL_SIZE]])
+// LLVM:   br label %[[DELETE_END]]
+// LLVM: [[DELETE_END]]:
+// LLVM:   ret void
 
 // OGCG: define {{.*}} void @_Z23test_sized_array_deleteP16SizedArrayDelete
 // OGCG:   %[[PTR:.*]] = load ptr, ptr %{{.*}}
diff --git a/clang/test/CIR/CodeGen/delete.cpp 
b/clang/test/CIR/CodeGen/delete.cpp
index b8f8fe483fca2..a2fb59cfec2ba 100644
--- a/clang/test/CIR/CodeGen/delete.cpp
+++ b/clang/test/CIR/CodeGen/delete.cpp
@@ -21,13 +21,36 @@ void test_sized_delete(SizedDelete *x) {
 
 // CIR: cir.func {{.*}} @_Z17test_sized_deleteP11SizedDelete
 // CIR:   %[[X:.*]] = cir.load{{.*}} %{{.*}}
-// CIR:   %[[X_CAST:.*]] = cir.cast bitcast %[[X]] : 
!cir.ptr<!rec_SizedDelete> -> !cir.ptr<!void>
-// CIR:   %[[OBJ_SIZE:.*]] = cir.const #cir.int<4> : !u64i
-// CIR:   cir.call @_ZN11SizedDeletedlEPvm(%[[X_CAST]], %[[OBJ_SIZE]]) nothrow 
: (!cir.ptr<!void> {llvm.noundef}, !u64i {llvm.noundef}) -> ()
+// CIR:   %[[NULL:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!rec_SizedDelete>
+// CIR:   %[[NOT_NULL:.*]] = cir.cmp ne %[[X]], %[[NULL]] : 
!cir.ptr<!rec_SizedDelete>
+// CIR:   cir.if %[[NOT_NULL]] {
+// CIR:     cir.cleanup.scope {
+// CIR:       cir.yield
+// CIR:     } cleanup normal {
+// CIR:       %[[X_CAST:.*]] = cir.cast bitcast %[[X]] : 
!cir.ptr<!rec_SizedDelete> -> !cir.ptr<!void>
+// CIR:       %[[OBJ_SIZE:.*]] = cir.const #cir.int<4> : !u64i
+// CIR:       cir.call @_ZN11SizedDeletedlEPvm(%[[X_CAST]], %[[OBJ_SIZE]]) 
nothrow : (!cir.ptr<!void> {llvm.noundef}, !u64i {llvm.noundef}) -> ()
+// CIR:       cir.yield
+// CIR:     }
+// CIR:   }
 
 // LLVM: define dso_local void @_Z17test_sized_deleteP11SizedDelete
 // LLVM:   %[[X:.*]] = load ptr, ptr %{{.*}}
+// LLVM:   %[[NOT_NULL:.*]] = icmp ne ptr %[[X]], null
+// LLVM:   br i1 %[[NOT_NULL]], label %[[DELETE_NOTNULL:.*]], label 
%[[DELETE_END:.*]]
+// LLVM: [[DELETE_NOTNULL]]:
+// LLVM:   br label %[[ENTER_CLEANUP_SCOPE:.*]]
+// LLVM: [[ENTER_CLEANUP_SCOPE]]:
+// LLVM:   br label %[[NORMAL_CLEANUP:.*]]
+// LLVM: [[NORMAL_CLEANUP]]:
 // LLVM:   call void @_ZN11SizedDeletedlEPvm(ptr noundef %[[X]], i64 noundef 4)
+// LLVM:   br label %[[EXIT_CLEANUP:.*]]
+// LLVM: [[EXIT_CLEANUP]]:
+// LLVM:   br label %[[EXIT_CLEANUP_SCOPE:.*]]
+// LLVM: [[EXIT_CLEANUP_SCOPE]]:
+// LLVM:   br label %[[DELETE_END]]
+// LLVM: [[DELETE_END]]:
+// LLVM:   ret void
 
 // OGCG: define dso_local void @_Z17test_sized_deleteP11SizedDelete
 // OGCG:   %[[X:.*]] = load ptr, ptr %{{.*}}
@@ -61,17 +84,40 @@ Container::~Container() { delete contents; }
 // CIR:   %[[THIS:.*]] = cir.load %{{.*}}
 // CIR:   %[[CONTENTS_PTR_ADDR:.*]] = cir.get_member %[[THIS]][0] {name = 
"contents"} : !cir.ptr<!rec_Container> -> !cir.ptr<!cir.ptr<!rec_Contents>>
 // CIR:   %[[CONTENTS_PTR:.*]] = cir.load{{.*}} %[[CONTENTS_PTR_ADDR]]
-// CIR:   cir.call @_ZN8ContentsD2Ev(%[[CONTENTS_PTR]]) nothrow : 
(!cir.ptr<!rec_Contents> {llvm.align = 1 : i64, llvm.dereferenceable = 1 : i64, 
llvm.nonnull, llvm.noundef}) -> ()
-// CIR:   %[[CONTENTS_CAST:.*]] = cir.cast bitcast %[[CONTENTS_PTR]] : 
!cir.ptr<!rec_Contents> -> !cir.ptr<!void>
-// CIR:   %[[OBJ_SIZE:.*]] = cir.const #cir.int<1> : !u64i
-// CIR:   cir.call @_ZdlPvm(%[[CONTENTS_CAST]], %[[OBJ_SIZE]]) nothrow 
{builtin} : (!cir.ptr<!void> {llvm.noundef}, !u64i {llvm.noundef}) -> ()
+// CIR:   %[[NULL:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!rec_Contents>
+// CIR:   %[[NOT_NULL:.*]] = cir.cmp ne %[[CONTENTS_PTR]], %[[NULL]] : 
!cir.ptr<!rec_Contents>
+// CIR:   cir.if %[[NOT_NULL]] {
+// CIR:     cir.cleanup.scope {
+// CIR:       cir.call @_ZN8ContentsD2Ev(%[[CONTENTS_PTR]]) nothrow : 
(!cir.ptr<!rec_Contents> {llvm.align = 1 : i64, llvm.dereferenceable = 1 : i64, 
llvm.nonnull, llvm.noundef}) -> ()
+// CIR:       cir.yield
+// CIR:     } cleanup normal {
+// CIR:       %[[CONTENTS_CAST:.*]] = cir.cast bitcast %[[CONTENTS_PTR]] : 
!cir.ptr<!rec_Contents> -> !cir.ptr<!void>
+// CIR:       %[[OBJ_SIZE:.*]] = cir.const #cir.int<1> : !u64i
+// CIR:       cir.call @_ZdlPvm(%[[CONTENTS_CAST]], %[[OBJ_SIZE]]) nothrow 
{builtin} : (!cir.ptr<!void> {llvm.noundef}, !u64i {llvm.noundef}) -> ()
+// CIR:       cir.yield
+// CIR:     }
+// CIR:   }
 
 // LLVM: define dso_local void @_ZN9ContainerD2Ev
 // LLVM:   %[[THIS:.*]] = load ptr, ptr %{{.*}}
 // LLVM:   %[[CONTENTS_PTR_ADDR:.*]] = getelementptr %struct.Container, ptr 
%[[THIS]], i32 0, i32 0
 // LLVM:   %[[CONTENTS_PTR:.*]] = load ptr, ptr %[[CONTENTS_PTR_ADDR]]
+// LLVM:   %[[NOT_NULL:.*]] = icmp ne ptr %[[CONTENTS_PTR]], null
+// LLVM:   br i1 %[[NOT_NULL]], label %[[DELETE_NOTNULL:.*]], label 
%[[DELETE_END:.*]]
+// LLVM: [[DELETE_NOTNULL]]:
+// LLVM:   br label %[[ENTER_CLEANUP_SCOPE:.*]]
+// LLVM: [[ENTER_CLEANUP_SCOPE]]:
 // LLVM:   call void @_ZN8ContentsD2Ev(ptr noundef nonnull align 1 
dereferenceable(1) %[[CONTENTS_PTR]])
+// LLVM:   br label %[[NORMAL_CLEANUP:.*]]
+// LLVM: [[NORMAL_CLEANUP]]:
 // LLVM:   call void @_ZdlPvm(ptr noundef %[[CONTENTS_PTR]], i64 noundef 1)
+// LLVM:   br label %[[EXIT_CLEANUP:.*]]
+// LLVM: [[EXIT_CLEANUP]]:
+// LLVM:   br label %[[EXIT_CLEANUP_SCOPE:.*]]
+// LLVM: [[EXIT_CLEANUP_SCOPE]]:
+// LLVM:   br label %[[DELETE_END]]
+// LLVM: [[DELETE_END]]:
+// LLVM:   ret void
 
 // OGCG: define dso_local void @_ZN9ContainerD2Ev
 // OGCG:   %[[THIS:.*]] = load ptr, ptr %{{.*}}
@@ -99,20 +145,29 @@ void destroy(StructWithVirtualDestructor *x) {
 // CIR:   %[[X_ADDR:.*]] = cir.alloca 
!cir.ptr<!rec_StructWithVirtualDestructor>
 // CIR:   cir.store %[[X_ARG]], %[[X_ADDR]]
 // CIR:   %[[X:.*]] = cir.load{{.*}} %[[X_ADDR]]
-// CIR:   %[[VTABLE_PTR:.*]] = cir.vtable.get_vptr %[[X]] : 
!cir.ptr<!rec_StructWithVirtualDestructor> -> !cir.ptr<!cir.vptr>
-// CIR:   %[[VTABLE:.*]] = cir.load{{.*}} %[[VTABLE_PTR]] : 
!cir.ptr<!cir.vptr>, !cir.vptr
-// CIR:   %[[DTOR_FN_ADDR_PTR:.*]] = cir.vtable.get_virtual_fn_addr 
%[[VTABLE]][1]
-// CIR:   %[[DTOR_FN_ADDR:.*]] = cir.load{{.*}} %[[DTOR_FN_ADDR_PTR]]
-// CIR:   cir.call %[[DTOR_FN_ADDR]](%[[X]])
+// CIR:   %[[NULL:.*]] = cir.const #cir.ptr<null> : 
!cir.ptr<!rec_StructWithVirtualDestructor>
+// CIR:   %[[NOT_NULL:.*]] = cir.cmp ne %[[X]], %[[NULL]] : 
!cir.ptr<!rec_StructWithVirtualDestructor>
+// CIR:   cir.if %[[NOT_NULL]] {
+// CIR:     %[[VTABLE_PTR:.*]] = cir.vtable.get_vptr %[[X]] : 
!cir.ptr<!rec_StructWithVirtualDestructor> -> !cir.ptr<!cir.vptr>
+// CIR:     %[[VTABLE:.*]] = cir.load{{.*}} %[[VTABLE_PTR]] : 
!cir.ptr<!cir.vptr>, !cir.vptr
+// CIR:     %[[DTOR_FN_ADDR_PTR:.*]] = cir.vtable.get_virtual_fn_addr 
%[[VTABLE]][1]
+// CIR:     %[[DTOR_FN_ADDR:.*]] = cir.load{{.*}} %[[DTOR_FN_ADDR_PTR]]
+// CIR:     cir.call %[[DTOR_FN_ADDR]](%[[X]])
+// CIR:   }
 
 // LLVM: define {{.*}} void @_Z7destroyP27StructWithVirtualDestructor(ptr 
{{.*}} %[[X_ARG:.*]])
 // LLVM:   %[[X_ADDR:.*]] = alloca ptr
 // LLVM:   store ptr %[[X_ARG]], ptr %[[X_ADDR]]
 // LLVM:   %[[X:.*]] = load ptr, ptr %[[X_ADDR]]
+// LLVM:   %[[NOT_NULL:.*]] = icmp ne ptr %[[X]], null
+// LLVM:   br i1 %[[NOT_NULL]], label %[[DELETE_NOTNULL:.*]], label 
%[[DELETE_END:.*]]
+// LLVM: [[DELETE_NOTNULL]]:
 // LLVM:   %[[VTABLE:.*]] = load ptr, ptr %[[X]]
 // LLVM:   %[[DTOR_FN_ADDR_PTR:.*]] = getelementptr inbounds ptr, ptr 
%[[VTABLE]], i32 1
 // LLVM:   %[[DTOR_FN_ADDR:.*]] = load ptr, ptr %[[DTOR_FN_ADDR_PTR]]
 // LLVM:   call void %[[DTOR_FN_ADDR]](ptr {{.*}} %[[X]])
+// LLVM: [[DELETE_END]]:
+// LLVM:   ret void
 
 // OGCG: define {{.*}} void @_Z7destroyP27StructWithVirtualDestructor(ptr 
{{.*}} %[[X_ARG:.*]])
 // OGCG:   %[[X_ADDR:.*]] = alloca ptr

>From 667ed84c9866ff5a2f0595a039ec8fe967fe8bc8 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <[email protected]>
Date: Thu, 12 Mar 2026 09:25:07 -0700
Subject: [PATCH 2/2] Rename createPtrNotNull to createPtrIsNotNull

---
 clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h | 2 +-
 clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp                  | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h 
b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
index 409f49f95f816..e60288c40132f 100644
--- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
+++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
@@ -520,7 +520,7 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
     return createCompare(ptr.getLoc(), cir::CmpOpKind::eq, ptr, nullPtr);
   }
 
-  mlir::Value createPtrNotNull(mlir::Value ptr) {
+  mlir::Value createPtrIsNotNull(mlir::Value ptr) {
     mlir::Value nullPtr = getNullPtr(ptr.getType(), ptr.getLoc());
     return createCompare(ptr.getLoc(), cir::CmpOpKind::ne, ptr, nullPtr);
   }
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
index e7ce74b6aab54..0ba7384e307fb 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
@@ -1198,7 +1198,7 @@ void CIRGenFunction::emitCXXDeleteExpr(const 
CXXDeleteExpr *e) {
   // keep the branch. This might be worth revisiting for a -O0 code size win.
   assert(!cir::MissingFeatures::emitNullCheckForDeleteCalls());
   cir::YieldOp thenYield;
-  mlir::Value notNull = builder.createPtrNotNull(ptr.getPointer());
+  mlir::Value notNull = builder.createPtrIsNotNull(ptr.getPointer());
   cir::IfOp::create(builder, getLoc(e->getExprLoc()), notNull,
                     /*withElseRegion=*/false,
                     /*thenBuilder=*/

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

Reply via email to