Files
llvm-project/clang/test/CIR/CodeGen/new-null.cpp
AbdallahRashed ca946f7fae [CIR] Implement null check for CXXNewExpr (#192848)
When operator new is allowed to return null (e.g. nothrow new), the
initialization needs to be guarded by a null check,Previously this was
an errorNYI.
This wraps the initialization in a cir.if conditioned on the allocation
being non null, then uses cir.select to pick between the allocation
pointer and null as the result. Also removes
MissingFeatures::exprNewNullCheck.
part of https://github.com/llvm/llvm-project/issues/192328
2026-04-28 13:07:41 -07:00

117 lines
4.9 KiB
C++

// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fexceptions -fcxx-exceptions -fclangir -emit-cir %s -o %t.cir
// RUN: FileCheck --input-file=%t.cir %s
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fexceptions -fcxx-exceptions -fclangir -emit-llvm %s -o %t-cir.ll
// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fexceptions -fcxx-exceptions -emit-llvm %s -o %t.ll
// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
typedef __typeof__(sizeof(int)) size_t;
namespace std {
struct nothrow_t {};
}
std::nothrow_t nothrow;
void *operator new(size_t, const std::nothrow_t &) throw();
void operator delete(void *, const std::nothrow_t &) throw();
struct S {
S();
~S();
int a;
};
// nothrow new with non-POD type triggers null check
S *test_nothrow_new() {
return new (nothrow) S;
}
// CHECK: cir.func {{.*}} @_Z16test_nothrow_newv()
// CHECK: %[[ALLOC:.*]] = cir.call @_ZnwmRKSt9nothrow_t({{.*}}) nothrow
// CHECK: %[[NULL:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!void>
// CHECK: %[[IS_NOT_NULL:.*]] = cir.cmp ne %[[ALLOC]], %[[NULL]] : !cir.ptr<!void>
// CHECK: cir.if %[[IS_NOT_NULL]] {
// CHECK: cir.cleanup.scope {
// CHECK: %[[CAST:.*]] = cir.cast bitcast %[[ALLOC]] : !cir.ptr<!void> -> !cir.ptr<!rec_S>
// CHECK: cir.call @_ZN1SC1Ev(%[[CAST]])
// CHECK: } cleanup eh {
// CHECK: cir.call @_ZdlPvRKSt9nothrow_t(%[[ALLOC]], {{.*}}) nothrow
// CHECK: } loc(
// CHECK: } loc(
// CHECK-NEXT: %[[LOADED:.*]] = cir.load
// CHECK: %[[NULL_S:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!rec_S>
// CHECK: cir.select if %[[IS_NOT_NULL]] then %[[LOADED]] else %[[NULL_S]]
// LLVM: define {{.*}} ptr @_Z16test_nothrow_newv() {{.*}}personality ptr @__gxx_personality_v0
// LLVM: %[[ALLOC:.*]] = call {{.*}} ptr @_ZnwmRKSt9nothrow_t(i64 noundef 4, {{.*}})
// LLVM: %[[CMP:.*]] = icmp ne ptr %[[ALLOC]], null
// LLVM: br i1 %[[CMP]], label %[[NOT_NULL:.*]], label %[[CONT:.*]]
// LLVM: [[NOT_NULL]]:
// LLVM: invoke void @_ZN1SC1Ev({{.*}} %[[ALLOC]])
// LLVM: to label {{.*}} unwind label %[[LPAD:.*]]
// LLVM: [[LPAD]]:
// LLVM: landingpad { ptr, i32 }
// LLVM: cleanup
// LLVM: call void @_ZdlPvRKSt9nothrow_t({{.*}} %[[ALLOC]], {{.*}})
// LLVM: resume
// LLVM: [[CONT]]:
// LLVM: select i1 %[[CMP]], ptr {{.*}}, ptr null
// OGCG: define {{.*}} ptr @_Z16test_nothrow_newv() {{.*}}personality ptr @__gxx_personality_v0
// OGCG: %[[ALLOC:.*]] = call {{.*}} ptr @_ZnwmRKSt9nothrow_t(i64 noundef 4, {{.*}})
// OGCG: %[[IS_NULL:.*]] = icmp eq ptr %[[ALLOC]], null
// OGCG: br i1 %[[IS_NULL]], label %[[CONT:.*]], label %[[NOT_NULL:.*]]
// OGCG: [[NOT_NULL]]:
// OGCG: invoke void @_ZN1SC1Ev({{.*}} %[[ALLOC]])
// OGCG: to label %[[OK:.*]] unwind label %[[LPAD:.*]]
// OGCG: [[OK]]:
// OGCG: br label %[[CONT]]
// OGCG: [[CONT]]:
// OGCG: phi ptr
// OGCG: [[LPAD]]:
// OGCG: landingpad { ptr, i32 }
// OGCG: cleanup
// OGCG: call void @_ZdlPvRKSt9nothrow_t({{.*}} %[[ALLOC]], {{.*}})
// OGCG: resume
// nothrow new with POD + initializer triggers null check
int *test_nothrow_new_init() {
return new (nothrow) int(42);
}
// CHECK: cir.func {{.*}} @_Z21test_nothrow_new_initv()
// CHECK: %[[ALLOC:.*]] = cir.call @_ZnwmRKSt9nothrow_t({{.*}}) nothrow
// CHECK: %[[NULL:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!void>
// CHECK: %[[IS_NOT_NULL:.*]] = cir.cmp ne %[[ALLOC]], %[[NULL]] : !cir.ptr<!void>
// CHECK: cir.if %[[IS_NOT_NULL]] {
// CHECK: cir.cleanup.scope {
// CHECK: %[[CAST:.*]] = cir.cast bitcast %[[ALLOC]] : !cir.ptr<!void> -> !cir.ptr<!s32i>
// CHECK: %[[FORTY_TWO:.*]] = cir.const #cir.int<42> : !s32i
// CHECK: cir.store {{.*}} %[[FORTY_TWO]], %[[CAST]]
// CHECK: } cleanup eh {
// CHECK: cir.call @_ZdlPvRKSt9nothrow_t(%[[ALLOC]], {{.*}}) nothrow
// CHECK: } loc(
// CHECK: } loc(
// CHECK-NEXT: %[[LOADED_I:.*]] = cir.load
// CHECK: %[[NULL_I:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!s32i>
// CHECK: cir.select if %[[IS_NOT_NULL]] then %[[LOADED_I]] else %[[NULL_I]]
// LLVM: define {{.*}} ptr @_Z21test_nothrow_new_initv()
// LLVM: %[[ALLOC:.*]] = call {{.*}} ptr @_ZnwmRKSt9nothrow_t(i64 noundef 4, {{.*}})
// LLVM: %[[CMP:.*]] = icmp ne ptr %[[ALLOC]], null
// LLVM: br i1 %[[CMP]], label %[[NOT_NULL:.*]], label %[[CONT:.*]]
// LLVM: [[NOT_NULL]]:
// LLVM: store i32 42, ptr %[[ALLOC]], align 4
// LLVM: [[CONT]]:
// LLVM: select i1 %[[CMP]], ptr {{.*}}, ptr null
// OGCG: define {{.*}} ptr @_Z21test_nothrow_new_initv()
// OGCG: %[[ALLOC:.*]] = call {{.*}} ptr @_ZnwmRKSt9nothrow_t(i64 noundef 4, {{.*}})
// OGCG: %[[IS_NULL:.*]] = icmp eq ptr %[[ALLOC]], null
// OGCG: br i1 %[[IS_NULL]], label %[[CONT:.*]], label %[[NOT_NULL:.*]]
// OGCG: [[NOT_NULL]]:
// OGCG: store i32 42, ptr %[[ALLOC]], align 4
// OGCG: br label %[[CONT]]
// OGCG: [[CONT]]:
// OGCG: phi ptr