Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 25 additions & 2 deletions clang/lib/CIR/CodeGen/CIRGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -389,6 +388,15 @@ LValue CIRGenFunction::emitLValueForField(LValue base, const FieldDecl *field) {
(!getDebugInfo() || !rec->hasAttr<BPFPreserveAccessIndexAttr>())) {
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<T*, EmptyDeleter>.
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))
Expand Down Expand Up @@ -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,
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenExprConst.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
Expand Down
7 changes: 7 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenRecordLayout.h
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,13 @@ class CIRGenRecordLayout {
/// this record.
cir::RecordType getBaseSubobjectCIRType() const { return BaseSubobjectType; }

/// Check if this record layout contains information about the given field.
/// Fields may be absent from the layout due to Empty Base Optimization (EBO),
/// where empty-type members are stored as base classes instead of fields.
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();
Expand Down
66 changes: 66 additions & 0 deletions clang/test/CIR/CodeGen/ebo-tuple.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// 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=CIR
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll
// RUN: FileCheck --input-file=%t.ll %s --check-prefix=LLVM
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.og.ll
// RUN: FileCheck --input-file=%t.og.ll %s --check-prefix=OGCG

// 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
}

// CIR: cir.func{{.*}}@_Z24test_ebo_inheriting_ctorv
// CIR: %{{.*}} = cir.alloca !rec_DerivedWithInheriting
// CIR: cir.return

// LLVM: define {{.*}}@_Z24test_ebo_inheriting_ctorv
// LLVM: alloca %struct.DerivedWithInheriting
// LLVM: ret void

// OGCG: define {{.*}}@_Z24test_ebo_inheriting_ctorv
// OGCG: alloca %struct.DerivedWithInheriting
// OGCG: ret void

int main() {
test_ebo_inheriting_ctor();
return 0;
}

// CIR: cir.func{{.*}}@main
// CIR: cir.call @_Z24test_ebo_inheriting_ctorv()
// CIR: cir.return

// LLVM: define {{.*}}@main
// LLVM: call {{.*}}@_Z24test_ebo_inheriting_ctorv()
// LLVM: ret i32

// OGCG: define {{.*}}@main
// OGCG: call {{.*}}@_Z24test_ebo_inheriting_ctorv()
// OGCG: ret i32