Skip to content

Commit 599b2a3

Browse files
authored
[CIR] Add support for derived class declarations (#142823)
This adds the minimal support for declaring a pointer to a derived class. This includes only the changes necessary to compute the record layout for the derived class and declare a variable that points to it. Support for accessing members of either the derived or base class is deferred until a later change, as is support for declaring a variable that is an instance of the derived class.
1 parent 6324493 commit 599b2a3

File tree

7 files changed

+171
-20
lines changed

7 files changed

+171
-20
lines changed

clang/include/clang/CIR/Dialect/IR/CIRTypes.td

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,18 @@ def CIR_RecordType : CIR_Type<"Record", "record",
474474
let genVerifyDecl = 1;
475475

476476
let builders = [
477+
// Create an identified and complete record type.
478+
TypeBuilder<(ins
479+
"llvm::ArrayRef<mlir::Type>":$members,
480+
"mlir::StringAttr":$name,
481+
"bool":$packed,
482+
"bool":$padded,
483+
"RecordKind":$kind
484+
), [{
485+
return $_get($_ctxt, members, name, /*complete=*/true, packed, padded,
486+
kind);
487+
}]>,
488+
477489
// Create an identified and incomplete record type.
478490
TypeBuilder<(ins
479491
"mlir::StringAttr":$name,

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ struct MissingFeatures {
136136
static bool cxxSupport() { return false; }
137137
static bool recordZeroInit() { return false; }
138138
static bool zeroSizeRecordMembers() { return false; }
139+
static bool recordLayoutVirtualBases() { return false; }
139140

140141
// Various handling of deferred processing in CIRGenModule.
141142
static bool cgmRelease() { return false; }

clang/lib/CIR/CodeGen/CIRGenBuilder.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,34 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
9696
llvm_unreachable("Unsupported record kind");
9797
}
9898

99+
/// Get a CIR named record type.
100+
///
101+
/// If a record already exists and is complete, but the client tries to fetch
102+
/// it with a different set of attributes, this method will crash.
103+
cir::RecordType getCompleteRecordTy(llvm::ArrayRef<mlir::Type> members,
104+
llvm::StringRef name, bool packed,
105+
bool padded) {
106+
const auto nameAttr = getStringAttr(name);
107+
auto kind = cir::RecordType::RecordKind::Struct;
108+
assert(!cir::MissingFeatures::astRecordDeclAttr());
109+
110+
// Create or get the record.
111+
auto type =
112+
getType<cir::RecordType>(members, nameAttr, packed, padded, kind);
113+
114+
// If we found an existing type, verify that either it is incomplete or
115+
// it matches the requested attributes.
116+
assert(!type.isIncomplete() ||
117+
(type.getMembers() == members && type.getPacked() == packed &&
118+
type.getPadded() == padded));
119+
120+
// Complete an incomplete record or ensure the existing complete record
121+
// matches the requested attributes.
122+
type.complete(members, packed, padded);
123+
124+
return type;
125+
}
126+
99127
/// Get an incomplete CIR struct type. If we have a complete record
100128
/// declaration, we may create an incomplete type and then add the
101129
/// members, so \p rd here may be complete.

clang/lib/CIR/CodeGen/CIRGenRecordLayout.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,18 @@ class CIRGenRecordLayout {
2929
/// as a complete object.
3030
cir::RecordType completeObjectType;
3131

32+
/// The CIR type for the non-virtual part of this record layout; used when
33+
/// laying it out as a base subobject.
34+
cir::RecordType baseSubobjectType;
35+
3236
/// Map from (non-bit-field) record field to the corresponding cir record type
3337
/// field no. This info is populated by the record builder.
3438
llvm::DenseMap<const clang::FieldDecl *, unsigned> fieldIdxMap;
3539

40+
// FIXME: Maybe we could use CXXBaseSpecifier as the key and use a single map
41+
// for both virtual and non-virtual bases.
42+
llvm::DenseMap<const clang::CXXRecordDecl *, unsigned> nonVirtualBases;
43+
3644
/// False if any direct or indirect subobject of this class, when considered
3745
/// as a complete object, requires a non-zero bitpattern when
3846
/// zero-initialized.
@@ -45,16 +53,22 @@ class CIRGenRecordLayout {
4553
unsigned zeroInitializableAsBase : 1;
4654

4755
public:
48-
CIRGenRecordLayout(cir::RecordType completeObjectType, bool zeroInitializable,
56+
CIRGenRecordLayout(cir::RecordType completeObjectType,
57+
cir::RecordType baseSubobjectType, bool zeroInitializable,
4958
bool zeroInitializableAsBase)
5059
: completeObjectType(completeObjectType),
60+
baseSubobjectType(baseSubobjectType),
5161
zeroInitializable(zeroInitializable),
5262
zeroInitializableAsBase(zeroInitializableAsBase) {}
5363

5464
/// Return the "complete object" LLVM type associated with
5565
/// this record.
5666
cir::RecordType getCIRType() const { return completeObjectType; }
5767

68+
/// Return the "base subobject" LLVM type associated with
69+
/// this record.
70+
cir::RecordType getBaseSubobjectCIRType() const { return baseSubobjectType; }
71+
5872
/// Return cir::RecordType element number that corresponds to the field FD.
5973
unsigned getCIRFieldNo(const clang::FieldDecl *fd) const {
6074
fd = fd->getCanonicalDecl();

clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp

Lines changed: 92 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,18 @@ struct CIRRecordLowering final {
4040
// member type that ensures correct rounding.
4141
struct MemberInfo final {
4242
CharUnits offset;
43-
enum class InfoKind { Field } kind;
43+
enum class InfoKind { Field, Base } kind;
4444
mlir::Type data;
4545
union {
4646
const FieldDecl *fieldDecl;
47-
// CXXRecordDecl will be used here when base types are supported.
47+
const CXXRecordDecl *cxxRecordDecl;
4848
};
4949
MemberInfo(CharUnits offset, InfoKind kind, mlir::Type data,
5050
const FieldDecl *fieldDecl = nullptr)
51-
: offset(offset), kind(kind), data(data), fieldDecl(fieldDecl) {};
51+
: offset{offset}, kind{kind}, data{data}, fieldDecl{fieldDecl} {}
52+
MemberInfo(CharUnits offset, InfoKind kind, mlir::Type data,
53+
const CXXRecordDecl *rd)
54+
: offset{offset}, kind{kind}, data{data}, cxxRecordDecl{rd} {}
5255
// MemberInfos are sorted so we define a < operator.
5356
bool operator<(const MemberInfo &other) const {
5457
return offset < other.offset;
@@ -71,6 +74,8 @@ struct CIRRecordLowering final {
7174
/// Inserts padding everywhere it's needed.
7275
void insertPadding();
7376

77+
void accumulateBases(const CXXRecordDecl *cxxRecordDecl);
78+
void accumulateVPtrs();
7479
void accumulateFields();
7580

7681
CharUnits bitsToCharUnits(uint64_t bitOffset) {
@@ -89,6 +94,9 @@ struct CIRRecordLowering final {
8994
bool isZeroInitializable(const FieldDecl *fd) {
9095
return cirGenTypes.isZeroInitializable(fd->getType());
9196
}
97+
bool isZeroInitializable(const RecordDecl *rd) {
98+
return cirGenTypes.isZeroInitializable(rd);
99+
}
92100

93101
/// Wraps cir::IntType with some implicit arguments.
94102
mlir::Type getUIntNType(uint64_t numBits) {
@@ -112,6 +120,11 @@ struct CIRRecordLowering final {
112120
: cir::ArrayType::get(type, numberOfChars.getQuantity());
113121
}
114122

123+
// Gets the CIR BaseSubobject type from a CXXRecordDecl.
124+
mlir::Type getStorageType(const CXXRecordDecl *RD) {
125+
return cirGenTypes.getCIRGenRecordLayout(RD).getBaseSubobjectCIRType();
126+
}
127+
115128
mlir::Type getStorageType(const FieldDecl *fieldDecl) {
116129
mlir::Type type = cirGenTypes.convertTypeForMem(fieldDecl->getType());
117130
if (fieldDecl->isBitField()) {
@@ -145,6 +158,7 @@ struct CIRRecordLowering final {
145158
// Output fields, consumed by CIRGenTypes::computeRecordLayout
146159
llvm::SmallVector<mlir::Type, 16> fieldTypes;
147160
llvm::DenseMap<const FieldDecl *, unsigned> fieldIdxMap;
161+
llvm::DenseMap<const CXXRecordDecl *, unsigned> nonVirtualBases;
148162
cir::CIRDataLayout dataLayout;
149163

150164
LLVM_PREFERRED_TYPE(bool)
@@ -179,24 +193,20 @@ void CIRRecordLowering::lower() {
179193
return;
180194
}
181195

182-
assert(!cir::MissingFeatures::cxxSupport());
183-
196+
assert(!cir::MissingFeatures::recordLayoutVirtualBases());
184197
CharUnits size = astRecordLayout.getSize();
185198

186199
accumulateFields();
187200

188201
if (const auto *cxxRecordDecl = dyn_cast<CXXRecordDecl>(recordDecl)) {
189-
if (cxxRecordDecl->getNumBases() > 0) {
190-
CIRGenModule &cgm = cirGenTypes.getCGModule();
191-
cgm.errorNYI(recordDecl->getSourceRange(),
192-
"CIRRecordLowering::lower: derived CXXRecordDecl");
193-
return;
194-
}
202+
accumulateVPtrs();
203+
accumulateBases(cxxRecordDecl);
195204
if (members.empty()) {
196205
appendPaddingBytes(size);
197206
assert(!cir::MissingFeatures::bitfields());
198207
return;
199208
}
209+
assert(!cir::MissingFeatures::recordLayoutVirtualBases());
200210
}
201211

202212
llvm::stable_sort(members);
@@ -223,8 +233,10 @@ void CIRRecordLowering::fillOutputFields() {
223233
fieldTypes.size() - 1;
224234
// A field without storage must be a bitfield.
225235
assert(!cir::MissingFeatures::bitfields());
236+
} else if (member.kind == MemberInfo::InfoKind::Base) {
237+
nonVirtualBases[member.cxxRecordDecl] = fieldTypes.size() - 1;
226238
}
227-
assert(!cir::MissingFeatures::cxxSupport());
239+
assert(!cir::MissingFeatures::recordLayoutVirtualBases());
228240
}
229241
}
230242

@@ -254,9 +266,14 @@ void CIRRecordLowering::calculateZeroInit() {
254266
continue;
255267
zeroInitializable = zeroInitializableAsBase = false;
256268
return;
269+
} else if (member.kind == MemberInfo::InfoKind::Base) {
270+
if (isZeroInitializable(member.cxxRecordDecl))
271+
continue;
272+
zeroInitializable = false;
273+
if (member.kind == MemberInfo::InfoKind::Base)
274+
zeroInitializableAsBase = false;
257275
}
258-
// TODO(cir): handle base types
259-
assert(!cir::MissingFeatures::cxxSupport());
276+
assert(!cir::MissingFeatures::recordLayoutVirtualBases());
260277
}
261278
}
262279

@@ -317,6 +334,27 @@ CIRGenTypes::computeRecordLayout(const RecordDecl *rd, cir::RecordType *ty) {
317334
lowering.lower();
318335

319336
// If we're in C++, compute the base subobject type.
337+
cir::RecordType baseTy;
338+
if (llvm::isa<CXXRecordDecl>(rd) && !rd->isUnion() &&
339+
!rd->hasAttr<FinalAttr>()) {
340+
baseTy = *ty;
341+
if (lowering.astRecordLayout.getNonVirtualSize() !=
342+
lowering.astRecordLayout.getSize()) {
343+
CIRRecordLowering baseLowering(*this, rd, /*Packed=*/lowering.packed);
344+
baseLowering.lower();
345+
std::string baseIdentifier = getRecordTypeName(rd, ".base");
346+
baseTy =
347+
builder.getCompleteRecordTy(baseLowering.fieldTypes, baseIdentifier,
348+
baseLowering.packed, baseLowering.padded);
349+
// TODO(cir): add something like addRecordTypeName
350+
351+
// BaseTy and Ty must agree on their packedness for getCIRFieldNo to work
352+
// on both of them with the same index.
353+
assert(lowering.packed == baseLowering.packed &&
354+
"Non-virtual and complete types must agree on packedness");
355+
}
356+
}
357+
320358
if (llvm::isa<CXXRecordDecl>(rd) && !rd->isUnion() &&
321359
!rd->hasAttr<FinalAttr>()) {
322360
if (lowering.astRecordLayout.getNonVirtualSize() !=
@@ -332,10 +370,13 @@ CIRGenTypes::computeRecordLayout(const RecordDecl *rd, cir::RecordType *ty) {
332370
ty->complete(lowering.fieldTypes, lowering.packed, lowering.padded);
333371

334372
auto rl = std::make_unique<CIRGenRecordLayout>(
335-
ty ? *ty : cir::RecordType(), (bool)lowering.zeroInitializable,
336-
(bool)lowering.zeroInitializableAsBase);
373+
ty ? *ty : cir::RecordType{}, baseTy ? baseTy : cir::RecordType{},
374+
(bool)lowering.zeroInitializable, (bool)lowering.zeroInitializableAsBase);
337375

338376
assert(!cir::MissingFeatures::recordZeroInit());
377+
378+
rl->nonVirtualBases.swap(lowering.nonVirtualBases);
379+
339380
assert(!cir::MissingFeatures::cxxSupport());
340381
assert(!cir::MissingFeatures::bitfields());
341382

@@ -415,3 +456,38 @@ void CIRRecordLowering::lowerUnion() {
415456
if (layoutSize % getAlignment(storageType))
416457
packed = true;
417458
}
459+
460+
void CIRRecordLowering::accumulateBases(const CXXRecordDecl *cxxRecordDecl) {
461+
// If we've got a primary virtual base, we need to add it with the bases.
462+
if (astRecordLayout.isPrimaryBaseVirtual()) {
463+
cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(),
464+
"accumulateBases: primary virtual base");
465+
}
466+
467+
// Accumulate the non-virtual bases.
468+
for ([[maybe_unused]] const auto &base : cxxRecordDecl->bases()) {
469+
if (base.isVirtual()) {
470+
cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(),
471+
"accumulateBases: virtual base");
472+
continue;
473+
}
474+
// Bases can be zero-sized even if not technically empty if they
475+
// contain only a trailing array member.
476+
const CXXRecordDecl *baseDecl = base.getType()->getAsCXXRecordDecl();
477+
if (!baseDecl->isEmpty() &&
478+
!astContext.getASTRecordLayout(baseDecl).getNonVirtualSize().isZero()) {
479+
members.push_back(MemberInfo(astRecordLayout.getBaseClassOffset(baseDecl),
480+
MemberInfo::InfoKind::Base,
481+
getStorageType(baseDecl), baseDecl));
482+
}
483+
}
484+
}
485+
486+
void CIRRecordLowering::accumulateVPtrs() {
487+
if (astRecordLayout.hasOwnVFPtr())
488+
cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(),
489+
"accumulateVPtrs: hasOwnVFPtr");
490+
if (astRecordLayout.hasOwnVBPtr())
491+
cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(),
492+
"accumulateVPtrs: hasOwnVBPtr");
493+
}

clang/lib/CIR/CodeGen/CIRGenTypes.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -239,9 +239,10 @@ mlir::Type CIRGenTypes::convertRecordDeclType(const clang::RecordDecl *rd) {
239239

240240
// Force conversion of non-virtual base classes recursively.
241241
if (const auto *cxxRecordDecl = dyn_cast<CXXRecordDecl>(rd)) {
242-
if (cxxRecordDecl->getNumBases() > 0) {
243-
cgm.errorNYI(rd->getSourceRange(),
244-
"convertRecordDeclType: derived CXXRecordDecl");
242+
for (const auto &base : cxxRecordDecl->bases()) {
243+
if (base.isVirtual())
244+
continue;
245+
convertRecordDeclType(base.getType()->castAs<RecordType>()->getDecl());
245246
}
246247
}
247248

clang/test/CIR/CodeGen/class.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,19 @@
66
// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
77

88
// CIR: !rec_IncompleteC = !cir.record<class "IncompleteC" incomplete>
9+
// CIR: !rec_Base = !cir.record<class "Base" {!s32i}>
910
// CIR: !rec_CompleteC = !cir.record<class "CompleteC" {!s32i, !s8i}>
11+
// CIR: !rec_Derived = !cir.record<class "Derived" {!rec_Base, !s32i}>
1012

1113
// Note: LLVM and OGCG do not emit the type for incomplete classes.
1214

1315
// LLVM: %class.CompleteC = type { i32, i8 }
16+
// LLVM: %class.Derived = type { %class.Base, i32 }
17+
// LLVM: %class.Base = type { i32 }
1418

1519
// OGCG: %class.CompleteC = type { i32, i8 }
20+
// OGCG: %class.Derived = type { %class.Base, i32 }
21+
// OGCG: %class.Base = type { i32 }
1622

1723
class IncompleteC;
1824
IncompleteC *p;
@@ -32,3 +38,16 @@ CompleteC cc;
3238
// CIR: cir.global external @cc = #cir.zero : !rec_CompleteC
3339
// LLVM: @cc = global %class.CompleteC zeroinitializer
3440
// OGCG: @cc = global %class.CompleteC zeroinitializer
41+
42+
class Base {
43+
public:
44+
int a;
45+
};
46+
47+
class Derived : public Base {
48+
public:
49+
int b;
50+
};
51+
52+
int use(Derived *d) { return d->b; }
53+

0 commit comments

Comments
 (0)