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
