[clang] [CIR] Correct array-new 'asserts' (PR #228461)
Erich Keane via cfe-commits
cfe-commits at lists.llvm.org
Fri Oct 2 11:49:16 PDT 2026
https://github.com/erichkeane updated https://github.com/llvm/llvm-project/pull/228461
>From a2624c825bd9886aa0dc294b50d07c838e016e02 Mon Sep 17 00:00:00 2001
From: erichkeane <ekeane at nvidia.com>
Date: Fri, 2 Oct 2026 07:23:37 -0700
Subject: [PATCH] [CIR] Correct array-new 'asserts'
The assertion checked whether the value was signed (which just checks
the MSB), but didn't check if the expression itself was of signed
type. The result was a size_t expression in the upper half (that is,
MSB set!) would hit the assertion. This patch checks the sign of
the thing too, which still allows us to do the zext later.
Additionally, this test case ALSO overflows, so we make sure we handle
overflow correctly here as well.
---
clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp | 13 +++++----
clang/test/CIR/CodeGen/new.cpp | 35 +++++++++++++++++++++++++
2 files changed, 41 insertions(+), 7 deletions(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
index f8c2a5cce47e405..decddca749d8351 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
@@ -485,7 +485,7 @@ static mlir::Value emitCXXNewAllocSize(CIRGenFunction &cgf, const CXXNewExpr *e,
// the cookie size would bring the total size >= 0.
//
// If the array size is constant, Sema will have prevented negative
- // values and size overflow.
+ // values.
// Compute the constant factor.
llvm::APInt arraySizeMultiplier(sizeWidth, 1);
@@ -515,8 +515,8 @@ static mlir::Value emitCXXNewAllocSize(CIRGenFunction &cgf, const CXXNewExpr *e,
.tryEmitAbstract(arraySize, arraySize->getType());
if (constNumElements) {
// Get an APInt from the constant
- const llvm::APInt &count =
- mlir::cast<cir::IntAttr>(constNumElements).getValue();
+ auto numEltsAttr = mlir::cast<cir::IntAttr>(constNumElements);
+ const llvm::APInt &count = numEltsAttr.getValue();
[[maybe_unused]] unsigned numElementsWidth = count.getBitWidth();
bool hasAnyOverflow = false;
@@ -529,7 +529,8 @@ static mlir::Value emitCXXNewAllocSize(CIRGenFunction &cgf, const CXXNewExpr *e,
// that. We immediately do the zextOrTrunc below (which should really only
// do zext, since our assert handles the trunc), but it will make sure the
// width is correct.
- assert(!count.isNegative() && "Expected non-negative array size");
+ assert(!(numEltsAttr.isSigned() && count.isNegative()) &&
+ "Expected non-negative array size");
assert(numElementsWidth <= sizeWidth &&
"Expected a size_t array size constant");
@@ -548,9 +549,7 @@ static mlir::Value emitCXXNewAllocSize(CIRGenFunction &cgf, const CXXNewExpr *e,
bool overflow;
llvm::APInt allocationSize =
adjustedCount.umul_ov(typeSizeMultiplier, overflow);
-
- // Sema prevents us from hitting this case
- assert(!overflow && "Overflow in array allocation size");
+ hasAnyOverflow |= overflow;
// Add in the cookie, and check whether it's overflowed.
if (cookieSize != 0) {
diff --git a/clang/test/CIR/CodeGen/new.cpp b/clang/test/CIR/CodeGen/new.cpp
index 539f40e24c98d53..3034388c3f3292c 100644
--- a/clang/test/CIR/CodeGen/new.cpp
+++ b/clang/test/CIR/CodeGen/new.cpp
@@ -454,6 +454,25 @@ void t_constant_size_partial_init() {
// OGCG: %[[ELEM_3:.*]] = getelementptr inbounds i32, ptr %[[ELEM_2]], i64 1
// OGCG: call void @llvm.memset.p0.i64(ptr{{.*}} %[[ELEM_3]], i8 0, i64 52, i1 false)
+// Array size is a converted constant expression that overflows converting
+// from double to size_t (UB, Sema only warns). The converted count
+// saturates to SIZE_MAX, and multiplying by the element size overflows
+// size_t, so the allocation size must be folded to SIZE_MAX (causing
+// operator new to fail at runtime) rather than asserting in the compiler.
+void t_new_huge_float_size() {
+ auto p = new int[1e20];
+}
+
+// CHECK: cir.func {{.*}} @_Z21t_new_huge_float_sizev()
+// CHECK: %[[ALLOCATION_SIZE:.*]] = cir.const #cir.int<18446744073709551615> : !u64i
+// CHECK: %{{.*}} = cir.call @_Znam(%[[ALLOCATION_SIZE]]) {allocsize = array<i32: 0>, builtin} : (!u64i {llvm.noundef}) -> (!cir.ptr<!void> {llvm.nonnull, llvm.noundef})
+
+// LLVM: define{{.*}} void @_Z21t_new_huge_float_sizev()
+// LLVM: %{{.*}} = call{{.*}} ptr @_Znam(i64{{.*}} -1)
+
+// OGCG: define{{.*}} void @_Z21t_new_huge_float_sizev()
+// OGCG: %{{.*}} = call{{.*}} ptr @_Znam(i64{{.*}} -1)
+
void t_new_var_size(size_t n) {
auto p = new char[n];
}
@@ -1214,3 +1233,19 @@ void test_array_new_with_ctor_partial_init_list() {
// OGCG: store ptr %[[RAW_PTR]], ptr %[[P_ADDR]], align 8
// OGCG: ret void
//
+
+namespace gh227980 {
+ void huge_float_size() {
+ auto p = new int[1e20];
+ }
+
+// CHECK-LABEL: cir.func{{.*}}@_ZN8gh22798015huge_float_sizeEv()
+// CHECK: %[[NEG:.*]] = cir.const #cir.int<18446744073709551615> : !u64i
+// CHECK: cir.call @_Znam(%[[NEG]]) {allocsize = array<i32: 0>, builtin} : (!u64i {llvm.noundef})
+
+// LLVM-LABEL: define {{.*}}@_ZN8gh22798015huge_float_sizeEv()
+// LLVM: call{{.*}} ptr @_Znam(i64 noundef -1)
+
+// OGCG-LABEL: define {{.*}}@_ZN8gh22798015huge_float_sizeEv()
+// OGCG: call{{.*}} ptr @_Znam(i64 noundef -1)
+}
More information about the cfe-commits
mailing list