From 11ecd305b1bcbed03fb55c4cd19c344e4c981f7f Mon Sep 17 00:00:00 2001 From: Nathan Lanza Date: Fri, 21 Nov 2025 20:40:54 -0800 Subject: [PATCH] Update [ghstack-poisoned] --- clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 27 ++++++++++++- clang/lib/CIR/CodeGen/CIRGenExprConst.cpp | 3 ++ clang/lib/CIR/CodeGen/CIRGenRecordLayout.h | 5 +++ clang/test/CIR/CodeGen/ebo-tuple.cpp | 46 ++++++++++++++++++++++ 4 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 clang/test/CIR/CodeGen/ebo-tuple.cpp diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index 28a988cc08a5..2fddd56588c5 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -88,9 +88,8 @@ static Address emitAddrOfFieldStorage(CIRGenFunction &CGF, Address Base, // address. const RecordDecl *rec = field->getParent(); auto &layout = CGF.CGM.getTypes().getCIRGenRecordLayout(rec); - unsigned idx = layout.getCIRFieldNo(field); auto offset = CharUnits::fromQuantity(layout.getCIRType().getElementOffset( - CGF.CGM.getDataLayout().layout, idx)); + CGF.CGM.getDataLayout().layout, fieldIndex)); auto addr = Address(memberAddr, Base.getAlignment().alignmentAtOffset(offset)); return addr; @@ -389,6 +388,15 @@ LValue CIRGenFunction::emitLValueForField(LValue base, const FieldDecl *field) { (!getDebugInfo() || !rec->hasAttr())) { llvm::StringRef fieldName = field->getName(); auto &layout = CGM.getTypes().getCIRGenRecordLayout(field->getParent()); + + // Check if the field exists in the record layout. This could fail if + // the field has a struct type that's empty and subject to Empty Base + // Optimization (EBO), such as with std::tuple. + if (!layout.containsFieldDecl(field)) + // Field doesn't exist in the CIR record layout (e.g., EBO case). + // Use the AST field index as a fallback. + return makeAddrLValue(addr, FieldType, FieldBaseInfo, FieldTBAAInfo); + unsigned fieldIndex = layout.getCIRFieldNo(field); if (CGM.LambdaFieldToName.count(field)) @@ -441,6 +449,21 @@ LValue CIRGenFunction::emitLValueForFieldInitialization( return emitLValueForField(Base, Field); auto &layout = CGM.getTypes().getCIRGenRecordLayout(Field->getParent()); + + // Check if the field exists in the record layout. This could fail if + // the field has a struct type that's empty and subject to Empty Base + // Optimization (EBO). + if (!layout.containsFieldDecl(Field)) { + // Field doesn't exist in the CIR record layout (e.g., EBO case). + // For reference fields that don't exist in the layout, we can't + // initialize them through the normal path. Return the base address. + LValueBaseInfo BaseInfo = Base.getBaseInfo(); + AlignmentSource FieldAlignSource = BaseInfo.getAlignmentSource(); + LValueBaseInfo FieldBaseInfo(getFieldAlignmentSource(FieldAlignSource)); + return makeAddrLValue(Base.getAddress(), FieldType, FieldBaseInfo, + CGM.getTBAAInfoForSubobject(Base, FieldType)); + } + unsigned FieldIndex = layout.getCIRFieldNo(Field); Address V = emitAddrOfFieldStorage(*this, Base.getAddress(), Field, FieldName, diff --git a/clang/lib/CIR/CodeGen/CIRGenExprConst.cpp b/clang/lib/CIR/CodeGen/CIRGenExprConst.cpp index d725aa3c5c7a..134f9e1bbc2f 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprConst.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprConst.cpp @@ -1770,6 +1770,9 @@ static mlir::TypedAttr emitNullConstant(CIRGenModule &CGM, const RecordDecl *rd, // will fill in later.) if (!field->isBitField() && !isEmptyFieldForLayout(CGM.getASTContext(), field)) { + // Check if field exists in layout (could be missing due to EBO). + if (!layout.containsFieldDecl(field)) + continue; unsigned fieldIndex = layout.getCIRFieldNo(field); elements[fieldIndex] = CGM.emitNullConstant(field->getType()); } diff --git a/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h b/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h index 0ef57279c6ab..04bd29404898 100644 --- a/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h +++ b/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h @@ -181,6 +181,11 @@ class CIRGenRecordLayout { /// this record. cir::RecordType getBaseSubobjectCIRType() const { return BaseSubobjectType; } + /// Check if this record layout contains information about the given field. + bool containsFieldDecl(const clang::FieldDecl *FD) const { + return FieldInfo.count(FD) != 0; + } + /// Return cir::RecordType element number that corresponds to the field FD. unsigned getCIRFieldNo(const clang::FieldDecl *FD) const { FD = FD->getCanonicalDecl(); diff --git a/clang/test/CIR/CodeGen/ebo-tuple.cpp b/clang/test/CIR/CodeGen/ebo-tuple.cpp new file mode 100644 index 000000000000..8c3abc98c668 --- /dev/null +++ b/clang/test/CIR/CodeGen/ebo-tuple.cpp @@ -0,0 +1,46 @@ +// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CHECK-EBO-BASIC + +// Test for Empty Base Optimization (EBO) with inheriting constructors +// This was causing assertion failure in CIRGenRecordLayout::getCIRFieldNo() +// Bug: Fields optimized away by EBO don't exist in FieldInfo map +// +// The issue occurs when all three conditions are met: +// 1. A class has a member that's an empty type +// 2. The class uses inheriting constructors (using Base::Base) +// 3. The inherited constructor is instantiated +// +// Without the fix, this would crash with: +// Assertion `FieldInfo.count(FD) && "Invalid field for record!"' failed +// at CIRGenRecordLayout.h:187 + +struct Empty { + constexpr Empty() noexcept = default; +}; + +struct HasEmptyMember { + int* ptr; + Empty e; + HasEmptyMember() = default; +}; + +struct DerivedWithInheriting : HasEmptyMember { + using HasEmptyMember::HasEmptyMember; // Inheriting constructor +}; + +void test_ebo_inheriting_ctor() { + DerivedWithInheriting d; // This used to crash +} + +// CHECK-EBO-BASIC: cir.func{{.*}}@_Z24test_ebo_inheriting_ctorv +// CHECK-EBO-BASIC: %{{.*}} = cir.alloca !rec_DerivedWithInheriting +// CHECK-EBO-BASIC: cir.return + +int main() { + test_ebo_inheriting_ctor(); + return 0; +} + +// CHECK-EBO-BASIC: cir.func{{.*}}@main +// CHECK-EBO-BASIC: cir.call @_Z24test_ebo_inheriting_ctorv() +// CHECK-EBO-BASIC: cir.return