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]]


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to