https://github.com/adams381 created 
https://github.com/llvm/llvm-project/pull/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. The helper to do this already existed:
`tryMemsetInitialization()` (used by the string-init path) emits one zeroing
memset over the remaining elements, guarded by `isZeroInitializable`.

This routes the trivial-ctor branch through it, following classic CodeGen in
`CGExprCXX.cpp`. A zero-initializable element gets the `operator new[]` call
plus a single `memset` to 0. When the element isn't zero-initializable (an
array of pointers-to-data-member, whose null value is -1, not 0), the guard
declines the memset and we fall through to the existing constructor loop,
which value-initializes each element to `{0, -1}`. I also added the
`getParent()->isEmpty()` early return, since an empty class has nothing to
zero.

The test's lowered LLVM and OGCG checks are split only where CIR still omits
`align` on the memset and `noalias` on `operator new[]`; those are pre-existing
gaps, not part of this change.

Found building the SPEC CPU 2026 LLVM benchmark (723.llvm_r / 823.llvm_s) with
ClangIR.


>From 1f00cb3b6a71f51cb571210d381c7cedfeabdb46 Mon Sep 17 00:00:00 2001
From: Adam Smith <[email protected]>
Date: Wed, 24 Jun 2026 10:32:54 -0700
Subject: [PATCH] [CIR] Zero-init array-new with trivial ctor

emitNewArrayInitializer reported errorNYI when an array new of a
trivially-constructible element requested value-initialization
(`new T[n]()`), where the trailing parentheses mean zero-initialization.

Route that case through the existing tryMemsetInitialization() helper,
mirroring classic CodeGen in CGExprCXX.cpp: for a zero-initializable
element this emits the operator new[] call plus a single memset to 0,
and otherwise falls through to the aggregate-constructor loop.  Also add
the ctor->getParent()->isEmpty() early-return clause that classic has,
since an empty class needs no zero-initialization.

Unblocks the SPEC CPU 2026 LLVM benchmark (723.llvm_r / 823.llvm_s),
which hit this NYI while building.
---
 clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp   |  7 +-
 clang/test/CIR/CodeGen/new-array-init.cpp | 94 +++++++++++++++++++++++
 2 files changed, 97 insertions(+), 4 deletions(-)
 create mode 100644 clang/test/CIR/CodeGen/new-array-init.cpp

diff --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
index 1a565b077a2eb..3f394b8f1da64 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
@@ -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..e4f592dd0da64
--- /dev/null
+++ b/clang/test/CIR/CodeGen/new-array-init.cpp
@@ -0,0 +1,94 @@
+// 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-prefix=LLVM --input-file=%t-cir.ll %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefix=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)
+
+// OGCG-LABEL: @_Z7makeVarj
+// OGCG:   call {{.*}} ptr @_Znam(
+// OGCG:   call void @llvm.memset.p0.i64(ptr align 8 %{{.*}}, 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)
+
+// OGCG-LABEL: @_Z9makeConstv
+// OGCG:   call {{.*}} ptr @_Znam(i64 noundef 32)
+// OGCG:   call void @llvm.memset.p0.i64(ptr align 8 %{{.*}}, 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
+
+// OGCG-LABEL: @_Z10makeNoInitj
+// OGCG:   call {{.*}} ptr @_Znam(
+// OGCG-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)
+
+// OGCG-LABEL: @_Z10makeBracedj
+// OGCG:   call {{.*}} ptr @_Znam(
+// OGCG:   call void @llvm.memset.p0.i64(ptr align 8 %{{.*}}, 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(
+// LLVM:   store %struct.M { i32 0, i64 -1 }, ptr %{{.*}}
+
+// OGCG-LABEL: @_Z10makeMemberj
+// OGCG:   call {{.*}} ptr @_Znam(
+// OGCG:   call void @llvm.memcpy.p0.p0.i64(ptr align 16 %{{.*}}, ptr align 16 
@{{.*}}

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

Reply via email to