https://github.com/adams381 updated https://github.com/llvm/llvm-project/pull/201654
>From dffed1eb7570356d7e053bbe7e6c2c2af2314170 Mon Sep 17 00:00:00 2001 From: Adam Smith <[email protected]> Date: Thu, 4 Jun 2026 10:41:58 -0700 Subject: [PATCH 1/2] [CIR] Handle non-zero-initializable types in emitNullInitialization Value-initializing an aggregate containing a pointer-to-data-member (e.g. 'new Inner()' where Inner has an 'int Inner::*' field) crashed with "type is not zero initializable" because emitNullInitialization unconditionally called errorNYI for types where isZeroInitializable returns false. Member pointers use -1 as the null/invalid sentinel, so a plain zero store is incorrect. Replace the errorNYI with a call to emitNullConstant, which already builds the correct per-field pattern (-1 for member-pointer fields, zero elsewhere), and store the result. Types with virtual bases are still guarded with errorNYI since emitNullConstant does not yet handle them. --- clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 15 ++++-- .../CIR/CodeGen/member-pointer-null-init.cpp | 47 +++++++++++++++++++ 2 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 clang/test/CIR/CodeGen/member-pointer-null-init.cpp diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index 4ecb47a864146..f16a5e6eb1574 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -1302,10 +1302,19 @@ void CIRGenFunction::emitNullInitialization(mlir::Location loc, Address destPtr, // If the type contains a pointer to data member we can't memset it to zero. // Instead, create a null constant and copy it to the destination. - // TODO: there are other patterns besides zero that we can usefully memset, - // like -1, which happens to be the pattern used by member-pointers. + // Member pointers use -1 as the null value, so a plain zero store would be + // incorrect; emitNullConstant produces the right per-field pattern. if (!cgm.getTypes().isZeroInitializable(ty)) { - cgm.errorNYI(loc, "type is not zero initializable"); + // emitNullConstant does not yet handle types with virtual bases. + if (const auto *rd = ty->getAsCXXRecordDecl(); rd && rd->getNumVBases()) { + cgm.errorNYI(loc, + "emitNullInitialization: non-zero-init type with virtual " + "bases"); + return; + } + mlir::Value nullVal = cgm.emitNullConstant(ty, loc); + builder.createStore(loc, nullVal, destPtr); + return; } // In LLVM Codegen: otherwise, just memset the whole thing to zero using diff --git a/clang/test/CIR/CodeGen/member-pointer-null-init.cpp b/clang/test/CIR/CodeGen/member-pointer-null-init.cpp new file mode 100644 index 0000000000000..74ee344c6f407 --- /dev/null +++ b/clang/test/CIR/CodeGen/member-pointer-null-init.cpp @@ -0,0 +1,47 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -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 -std=c++17 -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --check-prefix=LLVM,LLVMCIR --input-file=%t-cir.ll %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -emit-llvm %s -o %t.ll +// RUN: FileCheck --check-prefix=LLVM,OGCG --input-file=%t.ll %s + +struct Inner { + int Inner::*p; +}; + +struct Outer { + Inner a; + int b; +}; + +// Value-init of a heap-allocated struct containing a pointer-to-data-member. +// The member pointer is null (-1), so the stored constant must carry -1. + +// CIR-LABEL: cir.func {{.*}}@_Z8make_newv +// CIR: [[NULL:%.*]] = cir.const #cir.const_record<{#cir.int<-1> : !s64i}> : !rec_Inner +// CIR: cir.store align(8) [[NULL]], {{%.*}} : !rec_Inner, !cir.ptr<!rec_Inner> + +// LLVMCIR-LABEL: define {{.*}} ptr @_Z8make_newv +// LLVMCIR: call {{.*}} @_Znwm +// LLVMCIR: store %struct.Inner { i64 -1 }, ptr %{{.*}}, align 8 + +// OGCG: @{{.*}} = private constant %struct.Inner { i64 -1 } +// OGCG-LABEL: define {{.*}} ptr @_Z8make_newv +// OGCG: call {{.*}} @llvm.memcpy{{.*}}i64 8 + +Inner *make_new() { return new Inner(); } + +// Partial aggregate init: Inner subobject 'a' is value-initialized because +// it has no designated initializer. + +// CIR-LABEL: cir.func {{.*}}@_Z11runtime_aggi +// CIR: cir.const #cir.int<-1> : !s64i +// CIR: cir.store align(8) {{%.*}}, {{%.*}} : !s64i + +// LLVM-LABEL: define {{.*}} void @_Z11runtime_aggi +// LLVM: store i64 -1, ptr %{{.*}}, align 8 + +void runtime_agg(int x) { + Outer o = {.b = x}; + (void)o; +} >From 6bb221ecfc707b4ff6cc62f678d4282803b4e7d6 Mon Sep 17 00:00:00 2001 From: Adam Smith <[email protected]> Date: Tue, 9 Jun 2026 09:41:31 -0700 Subject: [PATCH 2/2] [CIR] Fix null-init for non-zero-init aggregates Value-initializing an aggregate containing a pointer-to-data-member (e.g. 'new Inner()' where Inner has an 'int Inner::*' field) crashed with "type is not zero initializable" because emitNullInitialization unconditionally called errorNYI for types where isZeroInitializable returns false. Member pointers use -1 as the null/invalid sentinel, so a plain zero store is incorrect. Replace the errorNYI with a call to emitNullConstant, which already builds the correct per-field pattern (-1 for member-pointer fields, zero elsewhere), and store the result. Types with virtual bases are still guarded with errorNYI since emitNullConstant does not yet handle them. --- clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index f16a5e6eb1574..0a392b4202625 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -1302,10 +1302,15 @@ void CIRGenFunction::emitNullInitialization(mlir::Location loc, Address destPtr, // If the type contains a pointer to data member we can't memset it to zero. // Instead, create a null constant and copy it to the destination. - // Member pointers use -1 as the null value, so a plain zero store would be - // incorrect; emitNullConstant produces the right per-field pattern. + // TODO: there are other patterns besides zero that we can usefully memset, + // like -1, which happens to be the pattern used by member-pointers. if (!cgm.getTypes().isZeroInitializable(ty)) { - // emitNullConstant does not yet handle types with virtual bases. + // Classic codegen handles non-zero-init VLAs here via emitNonZeroVLAInit. + // In CIR, getTypeSizeInChars returns 0 for VLAs, so they are caught by + // the errorNYI above. + // + // Guard: emitNullConstant calls errorNYI for virtual bases and returns {}, + // which would crash builder.getConstant; report the NYI here instead. if (const auto *rd = ty->getAsCXXRecordDecl(); rd && rd->getNumVBases()) { cgm.errorNYI(loc, "emitNullInitialization: non-zero-init type with virtual " _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
