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
