[clang] 0e84786 - [CIR] Zero-init array-new with trivial ctor (#205611)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Jun 25 09:02:29 PDT 2026
Author: adams381
Date: 2026-06-25T11:02:24-05:00
New Revision: 0e847869ab8dde02815d82b67c7f0bb834653b51
URL: https://github.com/llvm/llvm-project/commit/0e847869ab8dde02815d82b67c7f0bb834653b51
DIFF: https://github.com/llvm/llvm-project/commit/0e847869ab8dde02815d82b67c7f0bb834653b51.diff
LOG: [CIR] Zero-init array-new with trivial ctor (#205611)
emitNewArrayInitializer hit errorNYI for a value-initialized array new of
a trivially-constructible element (new T[n]()), where the trailing
parentheses mean zero-initialization. Route the trivial-ctor branch
through the existing tryMemsetInitialization() helper, following classic
CodeGen: a zero-initializable element gets operator new[] plus a single
memset to 0, while a non-zero-initializable element (an array of
pointers-to-data-member, whose null value is -1) declines the memset and
falls through to the constructor loop value-initializing each element.
Also add the getParent()->isEmpty() early return. tryMemsetInitialization
builds the memset through the Address overload of createMemSet so the
destination alignment is preserved.
Found building the SPEC CPU 2026 LLVM benchmark (723.llvm_r / 823.llvm_s)
with ClangIR.
Added:
clang/test/CIR/CodeGen/new-array-init.cpp
Modified:
clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
clang/test/CIR/CodeGen/new.cpp
clang/test/CIR/CodeGen/paren-list-agg-init.cpp
clang/test/CIR/CodeGenCXX/new-array-init.cpp
Removed:
################################################################################
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
index 1a565b077a2eb..df60005d77259 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
@@ -1068,10 +1068,10 @@ void CIRGenFunction::emitNewArrayInitializer(
remainingSize = builder.createSub(loc, remainingSize, initSizeOp);
}
- // Create the memset.
- mlir::Value castOp =
- builder.createPtrBitcast(curPtr.getPointer(), cgm.voidTy);
- builder.createMemSet(loc, castOp, builder.getConstInt(loc, cgm.uInt8Ty, 0),
+ // Create the memset. Use the Address overload so the destination
+ // alignment from curPtr is carried onto the memset.
+ Address voidPtr = curPtr.withElementType(builder, cgm.voidTy);
+ builder.createMemSet(loc, voidPtr, builder.getConstInt(loc, cgm.uInt8Ty, 0),
remainingSize);
return true;
};
@@ -1228,12 +1228,11 @@ void CIRGenFunction::emitNewArrayInitializer(
if (ctor->isTrivial()) {
// If new expression did not specify value-initialization, then there
// is no initialization.
- if (!cce->requiresZeroInitialization())
+ if (!cce->requiresZeroInitialization() || ctor->getParent()->isEmpty())
return;
- cgm.errorNYI(cce->getSourceRange(),
- "emitNewArrayInitializer: trivial ctor zero-init");
- return;
+ if (tryMemsetInitialization())
+ return;
}
// Store the new Cleanup position for irregular Cleanups.
diff --git a/clang/test/CIR/CodeGen/new-array-init.cpp b/clang/test/CIR/CodeGen/new-array-init.cpp
new file mode 100644
index 0000000000000..a44131aad87a6
--- /dev/null
+++ b/clang/test/CIR/CodeGen/new-array-init.cpp
@@ -0,0 +1,75 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --check-prefixes=LLVM,LLVMCIR --input-file=%t-cir.ll %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefixes=LLVM,OGCG --input-file=%t.ll %s
+
+struct S {
+ int a;
+ int b;
+};
+
+// Value-init array new of a trivial type: () means zero-init via memset.
+S *makeVar(unsigned n) { return new S[n](); }
+
+// CIR-LABEL: cir.func{{.*}}@_Z7makeVarj
+// CIR: cir.call @_Znam(
+// CIR: cir.libc.memset {{.*}} bytes at {{.*}} to
+
+// LLVM-LABEL: @_Z7makeVarj
+// LLVM: call {{.*}} ptr @_Znam(
+// LLVM: call void @llvm.memset.p0.i64(ptr{{.*}} %{{.*}}, i8 0, i64 %{{.*}}, i1 false)
+
+// Constant element count: the size is folded to a constant.
+S *makeConst() { return new S[4](); }
+
+// CIR-LABEL: cir.func{{.*}}@_Z9makeConstv
+// CIR: cir.call @_Znam(
+// CIR: cir.libc.memset
+
+// LLVM-LABEL: @_Z9makeConstv
+// LLVM: call {{.*}} ptr @_Znam(i64 noundef 32)
+// LLVM: call void @llvm.memset.p0.i64(ptr{{.*}} %{{.*}}, i8 0, i64 32, i1 false)
+
+// No parens: default-init of a trivial type is a no-op (no memset).
+S *makeNoInit(unsigned n) { return new S[n]; }
+
+// CIR-LABEL: cir.func{{.*}}@_Z10makeNoInitj
+// CIR: cir.call @_Znam(
+// CIR-NOT: cir.libc.memset
+
+// LLVM-LABEL: @_Z10makeNoInitj
+// LLVM: call {{.*}} ptr @_Znam(
+// LLVM-NOT: memset
+
+// Braced-empty value-init goes through the InitListExpr path (also memset).
+S *makeBraced(unsigned n) { return new S[n]{}; }
+
+// CIR-LABEL: cir.func{{.*}}@_Z10makeBracedj
+// CIR: cir.call @_Znam(
+// CIR: cir.libc.memset
+
+// LLVM-LABEL: @_Z10makeBracedj
+// LLVM: call {{.*}} ptr @_Znam(
+// LLVM: call void @llvm.memset.p0.i64(ptr{{.*}} %{{.*}}, i8 0, i64 %{{.*}}, i1 false)
+
+// Non-zero-initializable element (pointer to data member): the null member
+// representation is -1, not 0, so memset is not used; the constructor-loop
+// path value-initializes each element to {0, -1}.
+struct M {
+ int x;
+ int M::*p;
+};
+
+M *makeMember(unsigned n) { return new M[n](); }
+
+// CIR-LABEL: cir.func{{.*}}@_Z10makeMemberj
+// CIR: cir.call @_Znam(
+// CIR-NOT: cir.libc.memset
+// CIR: cir.const #cir.const_record<{#cir.int<0> : !s32i, #cir.int<-1> : !s64i}>
+
+// LLVM-LABEL: @_Z10makeMemberj
+// LLVM: call {{.*}} ptr @_Znam(
+// LLVMCIR: store %struct.M { i32 0, i64 -1 }, ptr %{{.*}}
+// OGCG: call void @llvm.memcpy.p0.p0.i64(ptr align 16 %{{.*}}, ptr align 16 @{{.*}}
diff --git a/clang/test/CIR/CodeGen/new.cpp b/clang/test/CIR/CodeGen/new.cpp
index b198fb1e3d0d0..56f2d1f23179a 100644
--- a/clang/test/CIR/CodeGen/new.cpp
+++ b/clang/test/CIR/CodeGen/new.cpp
@@ -355,11 +355,11 @@ void t_constant_size_memset_init() {
// CHECK: %[[ELEM_PTR:.*]] = cir.cast bitcast %[[ALLOC_PTR]] : !cir.ptr<!void> -> !cir.ptr<!s32i>
// CHECK: %[[VOID_PTR:.*]] = cir.cast bitcast %[[ELEM_PTR]] : !cir.ptr<!s32i> -> !cir.ptr<!void>
// CHECK: %[[ZERO:.*]] = cir.const #cir.int<0> : !u8i
-// CHECK: cir.libc.memset %[[ALLOCATION_SIZE]] bytes at %[[VOID_PTR]] to %[[ZERO]] : !cir.ptr<!void>, !u8i, !u64i
+// CHECK: cir.libc.memset %[[ALLOCATION_SIZE]] bytes at %[[VOID_PTR]]{{.*}} to %[[ZERO]] : !cir.ptr<!void>, !u8i, !u64i
// LLVM: define {{.*}} void @_Z27t_constant_size_memset_initv()
// LLVM: %[[P:.*]] = call noundef nonnull ptr @_Znam(i64 noundef 64)
-// LLVM: call void @llvm.memset.p0.i64(ptr %[[P]], i8 0, i64 64, i1 false)
+// LLVM: call void @llvm.memset.p0.i64(ptr{{.*}} %[[P]], i8 0, i64 64, i1 false)
// OGCG: define {{.*}} void @_Z27t_constant_size_memset_initv()
// OGCG: %[[P:.*]] = call{{.*}} ptr @_Znam(i64{{.*}} 64)
@@ -432,7 +432,7 @@ void t_constant_size_partial_init() {
// CHECK: %[[REMAINING_SIZE:.*]] = cir.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
+// 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 noundef nonnull ptr @_Znam(i64 {{.*}} 64)
@@ -442,7 +442,7 @@ void t_constant_size_partial_init() {
// 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)
+// 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)
@@ -614,7 +614,7 @@ void t_new_var_size6(int n) {
// CHECK: %[[REMAINING_SIZE:.*]] = cir.sub %[[ALLOC_SIZE]], %[[INIT_SIZE]] : !u64i
// CHECK: %[[PTR_DOUBLE_3_VOID:.*]] = cir.cast bitcast %[[PTR_DOUBLE_3]] : !cir.ptr<!cir.double> -> !cir.ptr<!void>
// CHECK: %[[ZERO:.*]] = cir.const #cir.int<0> : !u8i
-// CHECK: cir.libc.memset{{.*}} bytes at %[[PTR_DOUBLE_3_VOID]] to %[[ZERO]]
+// CHECK: cir.libc.memset{{.*}} bytes at %[[PTR_DOUBLE_3_VOID]]{{.*}} to %[[ZERO]]
// LLVM: define{{.*}} void @_Z15t_new_var_size6i
// LLVM: %[[N:.*]] = load i32, ptr %{{.+}}
@@ -633,7 +633,7 @@ void t_new_var_size6(int n) {
// LLVM: store double 3.000000e+00, ptr %[[ELEM_2]], align 8
// LLVM: %[[ELEM_3:.*]] = getelementptr double, ptr %[[ELEM_2]], i64 1
// LLVM: %[[REMAINING_SIZE:.*]] = sub i64 %[[ALLOC_SIZE]], 24
-// LLVM: call void @llvm.memset.p0.i64(ptr %[[ELEM_3]], i8 0, i64 %[[REMAINING_SIZE]], i1 false)
+// LLVM: call void @llvm.memset.p0.i64(ptr{{.*}} %[[ELEM_3]], i8 0, i64 %[[REMAINING_SIZE]], i1 false)
// OGCG: define{{.*}} void @_Z15t_new_var_size6i
// OGCG: %[[N:.*]] = load i32, ptr %{{.+}}
diff --git a/clang/test/CIR/CodeGen/paren-list-agg-init.cpp b/clang/test/CIR/CodeGen/paren-list-agg-init.cpp
index 01e045f376a4f..2b64dd60af9df 100644
--- a/clang/test/CIR/CodeGen/paren-list-agg-init.cpp
+++ b/clang/test/CIR/CodeGen/paren-list-agg-init.cpp
@@ -875,7 +875,7 @@ namespace gh68198 {
// CIR: %[[ALLOC_DIFF:.*]] = cir.sub %[[SIZE]], %[[INIT_SIZE]] : !u64i
// CIR: %[[ELT2_DECAY:.*]] = cir.cast bitcast %[[ELT2]] : !cir.ptr<!cir.array<!s32i x 2>> -> !cir.ptr<!void>
// CIR: %[[ZERO:.*]] = cir.const #cir.int<0> : !u8i
- // CIR: cir.libc.memset %[[ALLOC_DIFF]] bytes at %[[ELT2_DECAY]] to %[[ZERO]] : !cir.ptr<!void>, !u8i, !u64i
+ // CIR: cir.libc.memset %[[ALLOC_DIFF]] bytes at %[[ELT2_DECAY]]{{.*}} to %[[ZERO]] : !cir.ptr<!void>, !u8i, !u64i
// CIR: %[[ARR_TO_VOID:.*]] = cir.cast bitcast %[[ALLOC_TO_ARR]] : !cir.ptr<!cir.array<!s32i x 2>> -> !cir.ptr<!void>
// CIR: cir.store{{.*}} %[[ARR_TO_VOID]], %[[ARR_ALLOCA]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
void foo27() {
diff --git a/clang/test/CIR/CodeGenCXX/new-array-init.cpp b/clang/test/CIR/CodeGenCXX/new-array-init.cpp
index 6e94b8bc275d7..fd595351573cd 100644
--- a/clang/test/CIR/CodeGenCXX/new-array-init.cpp
+++ b/clang/test/CIR/CodeGenCXX/new-array-init.cpp
@@ -45,7 +45,7 @@ void fn(int n) {
// CIR: %[[REST_SIZE:.*]] = cir.sub %[[ADJ_SIZE]], %[[TWELVE]] : !u64i
// CIR: %[[REST_ALLOC_AS_VOID:.*]] = cir.cast bitcast %[[NEXT_ELT3]] : !cir.ptr<!s32i> -> !cir.ptr<!void>
// CIR: %[[ZERO:.*]] = cir.const #cir.int<0> : !u8i
- // CIR: cir.libc.memset %[[REST_SIZE]] bytes at %[[REST_ALLOC_AS_VOID]] to %[[ZERO]] : !cir.ptr<!void>, !u8i, !u64i
+ // CIR: cir.libc.memset %[[REST_SIZE]] bytes at %[[REST_ALLOC_AS_VOID]]{{.*}} to %[[ZERO]] : !cir.ptr<!void>, !u8i, !u64i
// LLVM: %[[N_ALLOCA:.*]] = alloca i32
// LLVM: %[[N_LOAD:.*]] = load i32, ptr %[[N_ALLOCA]]
@@ -100,7 +100,7 @@ void fn_paren(int n) {
// CIR: %[[REST_SIZE:.*]] = cir.sub %[[ADJ_SIZE]], %[[TWELVE]] : !u64i
// CIR: %[[REST_ALLOC_AS_VOID:.*]] = cir.cast bitcast %[[NEXT_ELT3]] : !cir.ptr<!s32i> -> !cir.ptr<!void>
// CIR: %[[ZERO:.*]] = cir.const #cir.int<0> : !u8i
- // CIR: cir.libc.memset %[[REST_SIZE]] bytes at %[[REST_ALLOC_AS_VOID]] to %[[ZERO]] : !cir.ptr<!void>, !u8i, !u64i
+ // CIR: cir.libc.memset %[[REST_SIZE]] bytes at %[[REST_ALLOC_AS_VOID]]{{.*}} to %[[ZERO]] : !cir.ptr<!void>, !u8i, !u64i
// LLVM: %[[N_ALLOCA:.*]] = alloca i32
// LLVM: %[[N_LOAD:.*]] = load i32, ptr %[[N_ALLOCA]]
@@ -206,7 +206,7 @@ void const_sufficient() {
// CIR: %[[REST_SIZE:.*]] = cir.sub %[[SIZE]], %[[INIT_SIZE]] : !u64i
// CIR: %[[REST_PTR_DECAY:.*]] = cir.cast bitcast %[[ELT3]] : !cir.ptr<!s32i> -> !cir.ptr<!void>
// CIR: %[[ZERO:.*]] = cir.const #cir.int<0> : !u8i
- // CIR: cir.libc.memset %[[REST_SIZE]] bytes at %[[REST_PTR_DECAY]] to %[[ZERO]] : !cir.ptr<!void>, !u8i, !u64i
+ // CIR: cir.libc.memset %[[REST_SIZE]] bytes at %[[REST_PTR_DECAY]]{{.*}} to %[[ZERO]] : !cir.ptr<!void>, !u8i, !u64i
// CIR: cir.return
// LLVM: %[[ALLOC:.*]] = call{{.*}}nonnull ptr @_Znam(i64 noundef 16)
@@ -243,7 +243,7 @@ void const_sufficient_paren() {
// CIR: %[[REST_SIZE:.*]] = cir.sub %[[SIZE]], %[[INIT_SIZE]] : !u64i
// CIR: %[[REST_PTR_DECAY:.*]] = cir.cast bitcast %[[ELT3]] : !cir.ptr<!s32i> -> !cir.ptr<!void>
// CIR: %[[ZERO:.*]] = cir.const #cir.int<0> : !u8i
- // CIR: cir.libc.memset %[[REST_SIZE]] bytes at %[[REST_PTR_DECAY]] to %[[ZERO]] : !cir.ptr<!void>, !u8i, !u64i
+ // CIR: cir.libc.memset %[[REST_SIZE]] bytes at %[[REST_PTR_DECAY]]{{.*}} to %[[ZERO]] : !cir.ptr<!void>, !u8i, !u64i
// CIR: cir.return
// LLVM: %[[ALLOC:.*]] = call{{.*}}nonnull ptr @_Znam(i64 noundef 16)
@@ -298,7 +298,7 @@ void string_nonconst(int n) {
// CIR: %[[SIZE_LEFT:.*]] = cir.sub %[[SIZE]], %[[CONST_STR_SIZE]] : !u64i
// CIR: %[[AFTER_COPY_CAST:.*]] = cir.cast bitcast %[[AFTER_COPY]] : !cir.ptr<!s8i> -> !cir.ptr<!void>
// CIR: %[[ZERO:.*]] = cir.const #cir.int<0> : !u8i
- // CIR: cir.libc.memset %[[SIZE_LEFT]] bytes at %[[AFTER_COPY_CAST]] to %[[ZERO]] : !cir.ptr<!void>, !u8i, !u64i
+ // CIR: cir.libc.memset %[[SIZE_LEFT]] bytes at %[[AFTER_COPY_CAST]]{{.*}} to %[[ZERO]] : !cir.ptr<!void>, !u8i, !u64i
// LLVM: %[[ARG_ALLOCA:.*]] = alloca i32
// LLVM: %[[ARG_LOAD:.*]] = load i32, ptr %[[ARG_ALLOCA]]
@@ -334,7 +334,7 @@ void string_nonconst_paren(int n) {
// CIR: %[[SIZE_LEFT:.*]] = cir.sub %[[SIZE]], %[[CONST_STR_SIZE]] : !u64i
// CIR: %[[AFTER_COPY_CAST:.*]] = cir.cast bitcast %[[AFTER_COPY]] : !cir.ptr<!s8i> -> !cir.ptr<!void>
// CIR: %[[ZERO:.*]] = cir.const #cir.int<0> : !u8i
- // CIR: cir.libc.memset %[[SIZE_LEFT]] bytes at %[[AFTER_COPY_CAST]] to %[[ZERO]] : !cir.ptr<!void>, !u8i, !u64i
+ // CIR: cir.libc.memset %[[SIZE_LEFT]] bytes at %[[AFTER_COPY_CAST]]{{.*}} to %[[ZERO]] : !cir.ptr<!void>, !u8i, !u64i
// LLVM: %[[ARG_ALLOCA:.*]] = alloca i32
// LLVM: %[[ARG_LOAD:.*]] = load i32, ptr %[[ARG_ALLOCA]]
@@ -370,7 +370,7 @@ void string_nonconst_paren_extra_paren(int n) {
// CIR: %[[SIZE_LEFT:.*]] = cir.sub %[[SIZE]], %[[CONST_STR_SIZE]] : !u64i
// CIR: %[[AFTER_COPY_CAST:.*]] = cir.cast bitcast %[[AFTER_COPY]] : !cir.ptr<!s8i> -> !cir.ptr<!void>
// CIR: %[[ZERO:.*]] = cir.const #cir.int<0> : !u8i
- // CIR: cir.libc.memset %[[SIZE_LEFT]] bytes at %[[AFTER_COPY_CAST]] to %[[ZERO]] : !cir.ptr<!void>, !u8i, !u64i
+ // CIR: cir.libc.memset %[[SIZE_LEFT]] bytes at %[[AFTER_COPY_CAST]]{{.*}} to %[[ZERO]] : !cir.ptr<!void>, !u8i, !u64i
// LLVM: %[[ARG_ALLOCA:.*]] = alloca i32
// LLVM: %[[ARG_LOAD:.*]] = load i32, ptr %[[ARG_ALLOCA]]
@@ -540,7 +540,7 @@ void aggr_sufficient(int n) {
// CIR: %[[REST_SIZE:.*]] = cir.sub %[[SIZE]], %[[TWO_ELTS_SIZE]] : !u64i
// CIR: %[[REST_DECAY:.*]] = cir.cast bitcast %[[ELT2]] : !cir.ptr<!rec_Aggr2E0> -> !cir.ptr<!void>
// CIR: %[[ZERO:.*]] = cir.const #cir.int<0> : !u8i
- // CIR: cir.libc.memset %[[REST_SIZE]] bytes at %[[REST_DECAY]] to %[[ZERO]] : !cir.ptr<!void>, !u8i, !u64i
+ // CIR: cir.libc.memset %[[REST_SIZE]] bytes at %[[REST_DECAY]]{{.*}} to %[[ZERO]] : !cir.ptr<!void>, !u8i, !u64i
// LLVM: %[[ARG:.*]] = alloca i32
// LLVM: %[[GET_N:.*]] = load i32, ptr %[[ARG]]
More information about the cfe-commits
mailing list