[clang] [CIR] Upstream support for array new with non-empty ILE (PR #179556)
Andy Kaylor via cfe-commits
cfe-commits at lists.llvm.org
Wed Feb 4 12:51:07 PST 2026
https://github.com/andykaylor updated https://github.com/llvm/llvm-project/pull/179556
>From 47e79012eaa430aa5e85c87728be030f43c5d1b1 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Fri, 30 Jan 2026 13:07:56 -0800
Subject: [PATCH 1/2] [CIR] Upstream support for array new with non-empty ILE
This adds CIR support for handling array new initialization with a non-empty
initializer list.
---
clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp | 30 +++++++--
clang/test/CIR/CodeGen/new.cpp | 89 +++++++++++++++++++++++++
2 files changed, 115 insertions(+), 4 deletions(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
index 3731773cffa45..2210fa86d9aca 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
@@ -14,6 +14,7 @@
#include "CIRGenConstantEmitter.h"
#include "CIRGenFunction.h"
+#include "clang/AST/CharUnits.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/ExprObjC.h"
@@ -574,6 +575,7 @@ void CIRGenFunction::emitNewArrayInitializer(
unsigned initListElements = 0;
const Expr *init = e->getInitializer();
+ Address endOfInit = Address::invalid();
QualType::DestructionKind dtorKind = elementType.isDestructedType();
assert(!cir::MissingFeatures::cleanupDeactivationScope());
@@ -652,10 +654,30 @@ void CIRGenFunction::emitNewArrayInitializer(
return;
}
- if (!initExprs.empty()) {
- cgm.errorNYI(ile->getSourceRange(),
- "emitNewArrayInitializer: non-empty init list");
- return;
+ CharUnits elementSize = getContext().getTypeSizeInChars(elementType);
+ CharUnits startAlign = curPtr.getAlignment();
+ unsigned i = 0;
+ for (const Expr *ie : initExprs) {
+ // Tell the cleanup that it needs to destroy up to this
+ // element. TODO: some of these stores can be trivially
+ // observed to be unnecessary.
+ if (endOfInit.isValid()) {
+ cgm.errorNYI(ie->getSourceRange(),
+ "emitNewArrayInitializer: update dtor cleanup ptr");
+ return;
+ }
+ // FIXME: If the last initializer is an incomplete initializer list for
+ // an array, and we have an array filler, we can fold together the two
+ // initialization loops.
+ storeAnyExprIntoOneUnit(*this, ie, ie->getType(), curPtr,
+ AggValueSlot::DoesNotOverlap);
+ mlir::Location loc = getLoc(ie->getExprLoc());
+ mlir::Value castOp = builder.createPtrBitcast(
+ curPtr.getPointer(), convertTypeForMem(allocType));
+ mlir::Value offsetOp = builder.getSignedInt(loc, 1, /*width=*/32);
+ mlir::Value dataPtr = builder.createPtrStride(loc, castOp, offsetOp);
+ curPtr = Address(dataPtr, curPtr.getElementType(),
+ startAlign.alignmentAtOffset((++i) * elementSize));
}
// The remaining elements are filled with the array filler expression.
diff --git a/clang/test/CIR/CodeGen/new.cpp b/clang/test/CIR/CodeGen/new.cpp
index ed6f5b6450fb2..b6ccffe1cd86d 100644
--- a/clang/test/CIR/CodeGen/new.cpp
+++ b/clang/test/CIR/CodeGen/new.cpp
@@ -361,3 +361,92 @@ void t_constant_size_memset_init() {
// OGCG: define {{.*}} void @_Z27t_constant_size_memset_initv()
// OGCG: %[[P:.*]] = call{{.*}} ptr @_Znam(i64{{.*}} 64)
// OGCG: call void @llvm.memset.p0.i64(ptr{{.*}} %[[P]], i8 0, i64 64, i1 false)
+
+void t_constant_size_full_init() {
+ auto p = new int[4] { 1, 2, 3, 4 };
+}
+
+// CHECK: cir.func {{.*}} @_Z25t_constant_size_full_initv()
+// CHECK: %[[ALLOCATION_SIZE:.*]] = cir.const #cir.int<16> : !u64i
+// CHECK: %[[ALLOC_PTR:.*]] = cir.call @_Znam(%[[ALLOCATION_SIZE]]) : (!u64i) -> !cir.ptr<!void>
+// CHECK: %[[ELEM_0_PTR:.*]] = cir.cast bitcast %[[ALLOC_PTR]] : !cir.ptr<!void> -> !cir.ptr<!s32i>
+// CHECK: %[[CONST_ONE:.*]] = cir.const #cir.int<1> : !s32i
+// CHECK: cir.store{{.*}} %[[CONST_ONE]], %[[ELEM_0_PTR]] : !s32i, !cir.ptr<!s32i>
+// CHECK: %[[OFFSET:.*]] = cir.const #cir.int<1> : !s32i
+// CHECK: %[[ELEM_1_PTR:.*]] = cir.ptr_stride %[[ELEM_0_PTR]], %[[OFFSET]] : (!cir.ptr<!s32i>, !s32i) -> !cir.ptr<!s32i>
+// CHECK: %[[CONST_TWO:.*]] = cir.const #cir.int<2> : !s32i
+// CHECK: cir.store{{.*}} %[[CONST_TWO]], %[[ELEM_1_PTR]] : !s32i, !cir.ptr<!s32i>
+// CHECK: %[[OFFSET1:.*]] = cir.const #cir.int<1> : !s32i
+// CHECK: %[[ELEM_2_PTR:.*]] = cir.ptr_stride %[[ELEM_1_PTR]], %[[OFFSET1]] : (!cir.ptr<!s32i>, !s32i) -> !cir.ptr<!s32i>
+// CHECK: %[[CONST_THREE:.*]] = cir.const #cir.int<3> : !s32i
+// CHECK: cir.store{{.*}} %[[CONST_THREE]], %[[ELEM_2_PTR]] : !s32i, !cir.ptr<!s32i>
+// CHECK: %[[OFFSET2:.*]] = cir.const #cir.int<1> : !s32i
+// CHECK: %[[ELEM_3_PTR:.*]] = cir.ptr_stride %[[ELEM_2_PTR]], %[[OFFSET2]] : (!cir.ptr<!s32i>, !s32i) -> !cir.ptr<!s32i>
+// CHECK: %[[CONST_FOUR:.*]] = cir.const #cir.int<4> : !s32i
+// CHECK: cir.store{{.*}} %[[CONST_FOUR]], %[[ELEM_3_PTR]] : !s32i, !cir.ptr<!s32i>
+
+// LLVM: define {{.*}} void @_Z25t_constant_size_full_initv()
+// LLVM: %[[P:.*]] = call ptr @_Znam(i64 16)
+// LLVM: store i32 1, ptr %[[CALL]]
+// LLVM: %[[ELEM_1:.*]] = getelementptr i32, ptr %[[P]], i64 1
+// LLVM: store i32 2, ptr %[[ELEM_1]]
+// LLVM: %[[ELEM_2:.*]] = getelementptr i32, ptr %[[ELEM_1]], i64 1
+// LLVM: store i32 3, ptr %[[ELEM_2]]
+// LLVM: %[[ELEM_3:.*]] = getelementptr i32, ptr %[[ELEM_2]], i64 1
+// LLVM: store i32 4, ptr %[[ELEM_3]]
+
+// OGCG: define {{.*}} void @_Z25t_constant_size_full_initv()
+// OGCG: %[[P:.*]] = call{{.*}} ptr @_Znam(i64{{.*}} 16)
+// OGCG: store i32 1, ptr %[[P]]
+// OGCG: %[[ELEM_1:.*]] = getelementptr inbounds i32, ptr %[[P]], i64 1
+// OGCG: store i32 2, ptr %[[ELEM_1]]
+// OGCG: %[[ELEM_2:.*]] = getelementptr inbounds i32, ptr %[[ELEM_1]], i64 1
+// OGCG: store i32 3, ptr %[[ELEM_2]]
+// OGCG: %[[ELEM_3:.*]] = getelementptr inbounds i32, ptr %[[ELEM_2]], i64 1
+// OGCG: store i32 4, ptr %[[ELEM_3]]
+
+void t_constant_size_partial_init() {
+ auto p = new int[16] { 1, 2, 3 };
+}
+
+// CHECK: cir.func {{.*}} @_Z28t_constant_size_partial_initv()
+// CHECK: %[[ALLOCATION_SIZE:.*]] = cir.const #cir.int<64> : !u64i
+// CHECK: %[[ALLOC_PTR:.*]] = cir.call @_Znam(%[[ALLOCATION_SIZE]]) : (!u64i) -> !cir.ptr<!void>
+// CHECK: %[[ELEM_0_PTR:.*]] = cir.cast bitcast %[[ALLOC_PTR]] : !cir.ptr<!void> -> !cir.ptr<!s32i>
+// CHECK: %[[CONST_ONE:.*]] = cir.const #cir.int<1> : !s32i
+// CHECK: cir.store{{.*}} %[[CONST_ONE]], %[[ELEM_0_PTR]] : !s32i, !cir.ptr<!s32i>
+// CHECK: %[[OFFSET:.*]] = cir.const #cir.int<1> : !s32i
+// CHECK: %[[ELEM_1_PTR:.*]] = cir.ptr_stride %[[ELEM_0_PTR]], %[[OFFSET]] : (!cir.ptr<!s32i>, !s32i) -> !cir.ptr<!s32i>
+// CHECK: %[[CONST_TWO:.*]] = cir.const #cir.int<2> : !s32i
+// CHECK: cir.store{{.*}} %[[CONST_TWO]], %[[ELEM_1_PTR]] : !s32i, !cir.ptr<!s32i>
+// CHECK: %[[OFFSET1:.*]] = cir.const #cir.int<1> : !s32i
+// CHECK: %[[ELEM_2_PTR:.*]] = cir.ptr_stride %[[ELEM_1_PTR]], %[[OFFSET1]] : (!cir.ptr<!s32i>, !s32i) -> !cir.ptr<!s32i>
+// CHECK: %[[CONST_THREE:.*]] = cir.const #cir.int<3> : !s32i
+// CHECK: cir.store{{.*}} %[[CONST_THREE]], %[[ELEM_2_PTR]] : !s32i, !cir.ptr<!s32i>
+// CHECK: %[[OFFSET2:.*]] = cir.const #cir.int<1> : !s32i
+// CHECK: %[[ELEM_3_PTR:.*]] = cir.ptr_stride %[[ELEM_2_PTR]], %[[OFFSET2]] : (!cir.ptr<!s32i>, !s32i) -> !cir.ptr<!s32i>
+// CHECK: %[[INIT_SIZE:.*]] = cir.const #cir.int<12> : !u64i
+// CHECK: %[[REMAINING_SIZE:.*]] = cir.binop(sub, %[[ALLOCATION_SIZE]], %[[INIT_SIZE]]) : !u64i
+// CHECK: %[[VOID_PTR:.*]] = cir.cast bitcast %[[ELEM_3_PTR]] : !cir.ptr<!s32i> -> !cir.ptr<!void>
+// CHECK: %[[ZERO:.*]] = cir.const #cir.int<0> : !u8i
+// CHECK: cir.libc.memset %[[REMAINING_SIZE]] bytes at %[[VOID_PTR]] to %[[ZERO]] : !cir.ptr<!void>, !u8i, !u64i
+
+// LLVM: define {{.*}} void @_Z28t_constant_size_partial_initv()
+// LLVM: %[[P:.*]] = call ptr @_Znam(i64 64)
+// LLVM: store i32 1, ptr %[[P]]
+// LLVM: %[[ELEM_1:.*]] = getelementptr i32, ptr %[[P]], i64 1
+// LLVM: store i32 2, ptr %[[ELEM_1]]
+// LLVM: %[[ELEM_2:.*]] = getelementptr i32, ptr %[[ELEM_1]], i64 1
+// LLVM: store i32 3, ptr %[[ELEM_2]]
+// LLVM: %[[ELEM_3:.*]] = getelementptr i32, ptr %[[ELEM_2]], i64 1
+// LLVM: call void @llvm.memset.p0.i64(ptr %[[ELEM_3]], i8 0, i64 52, i1 false)
+
+// OGCG: define {{.*}} void @_Z28t_constant_size_partial_initv()
+// OGCG: %[[P:.*]] = call{{.*}} ptr @_Znam(i64{{.*}} 64)
+// OGCG: store i32 1, ptr %[[P]]
+// OGCG: %[[ELEM_1:.*]] = getelementptr inbounds i32, ptr %[[P]], i64 1
+// OGCG: store i32 2, ptr %[[ELEM_1]]
+// OGCG: %[[ELEM_2:.*]] = getelementptr inbounds i32, ptr %[[ELEM_1]], i64 1
+// OGCG: store i32 3, ptr %[[ELEM_2]]
+// 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)
>From fbf24fec6d4c07dadf977439820f7a1d4a94a5c0 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Wed, 4 Feb 2026 11:32:21 -0800
Subject: [PATCH 2/2] Add checks for allocsize attribute
---
clang/test/CIR/CodeGen/new.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/test/CIR/CodeGen/new.cpp b/clang/test/CIR/CodeGen/new.cpp
index b6ccffe1cd86d..db2adbdce7f1c 100644
--- a/clang/test/CIR/CodeGen/new.cpp
+++ b/clang/test/CIR/CodeGen/new.cpp
@@ -368,7 +368,7 @@ void t_constant_size_full_init() {
// CHECK: cir.func {{.*}} @_Z25t_constant_size_full_initv()
// CHECK: %[[ALLOCATION_SIZE:.*]] = cir.const #cir.int<16> : !u64i
-// CHECK: %[[ALLOC_PTR:.*]] = cir.call @_Znam(%[[ALLOCATION_SIZE]]) : (!u64i) -> !cir.ptr<!void>
+// CHECK: %[[ALLOC_PTR:.*]] = cir.call @_Znam(%[[ALLOCATION_SIZE]]) {allocsize = array<i32: 0>} : (!u64i) -> !cir.ptr<!void>
// CHECK: %[[ELEM_0_PTR:.*]] = cir.cast bitcast %[[ALLOC_PTR]] : !cir.ptr<!void> -> !cir.ptr<!s32i>
// CHECK: %[[CONST_ONE:.*]] = cir.const #cir.int<1> : !s32i
// CHECK: cir.store{{.*}} %[[CONST_ONE]], %[[ELEM_0_PTR]] : !s32i, !cir.ptr<!s32i>
@@ -411,7 +411,7 @@ void t_constant_size_partial_init() {
// CHECK: cir.func {{.*}} @_Z28t_constant_size_partial_initv()
// CHECK: %[[ALLOCATION_SIZE:.*]] = cir.const #cir.int<64> : !u64i
-// CHECK: %[[ALLOC_PTR:.*]] = cir.call @_Znam(%[[ALLOCATION_SIZE]]) : (!u64i) -> !cir.ptr<!void>
+// CHECK: %[[ALLOC_PTR:.*]] = cir.call @_Znam(%[[ALLOCATION_SIZE]]) {allocsize = array<i32: 0>} : (!u64i) -> !cir.ptr<!void>
// CHECK: %[[ELEM_0_PTR:.*]] = cir.cast bitcast %[[ALLOC_PTR]] : !cir.ptr<!void> -> !cir.ptr<!s32i>
// CHECK: %[[CONST_ONE:.*]] = cir.const #cir.int<1> : !s32i
// CHECK: cir.store{{.*}} %[[CONST_ONE]], %[[ELEM_0_PTR]] : !s32i, !cir.ptr<!s32i>
More information about the cfe-commits
mailing list