Skip to content

Commit 80c19b3

Browse files
authored
[CIR] Upstream initial support for complete record types (#135844)
This adds basic support for populating record types. In order to keep the change small, everything non-essential was deferred to a later change set. Only non-recursive structures are handled. Structures padding is not yet implemented. Bitfields are not supported. No attempt is made to handle ABI requirements for passing structure arguments.
1 parent ce7466f commit 80c19b3

File tree

12 files changed

+421
-3
lines changed

12 files changed

+421
-3
lines changed

clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
9595
return getZeroAttr(arrTy);
9696
if (auto ptrTy = mlir::dyn_cast<cir::PointerType>(ty))
9797
return getConstNullPtrAttr(ptrTy);
98+
if (auto recordTy = mlir::dyn_cast<cir::RecordType>(ty))
99+
return getZeroAttr(recordTy);
98100
if (mlir::isa<cir::BoolType>(ty)) {
99101
return getCIRBoolAttr(false);
100102
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,9 @@ def CIR_RecordType : CIR_Type<"Record", "record",
499499
std::string getPrefixedName() {
500500
return getKindAsStr() + "." + getName().getValue().str();
501501
}
502+
503+
void complete(llvm::ArrayRef<mlir::Type> members, bool packed,
504+
bool isPadded);
502505
}];
503506

504507
let hasCustomAssemblyFormat = 1;

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,14 @@ struct MissingFeatures {
103103

104104
// RecordType
105105
static bool recordTypeLayoutInfo() { return false; }
106+
static bool recursiveRecordLayout() { return false; }
107+
static bool skippedLayout() { return false; }
108+
static bool astRecordDeclAttr() { return false; }
109+
static bool cxxSupport() { return false; }
110+
static bool packedRecords() { return false; }
111+
static bool recordPadding() { return false; }
112+
static bool recordZeroInit() { return false; }
113+
static bool zeroSizeRecordMembers() { return false; }
106114

107115
// Misc
108116
static bool cxxABI() { return false; }
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_CLANG_LIB_CIR_CIRGENRECORDLAYOUT_H
10+
#define LLVM_CLANG_LIB_CIR_CIRGENRECORDLAYOUT_H
11+
12+
#include "clang/AST/Decl.h"
13+
#include "clang/CIR/Dialect/IR/CIRTypes.h"
14+
15+
namespace clang::CIRGen {
16+
17+
/// This class handles record and union layout info while lowering AST types
18+
/// to CIR types.
19+
///
20+
/// These layout objects are only created on demand as CIR generation requires.
21+
class CIRGenRecordLayout {
22+
friend class CIRGenTypes;
23+
24+
CIRGenRecordLayout(const CIRGenRecordLayout &) = delete;
25+
void operator=(const CIRGenRecordLayout &) = delete;
26+
27+
private:
28+
/// The CIR type corresponding to this record layout; used when laying it out
29+
/// as a complete object.
30+
cir::RecordType completeObjectType;
31+
32+
/// Map from (non-bit-field) record field to the corresponding cir record type
33+
/// field no. This info is populated by the record builder.
34+
llvm::DenseMap<const clang::FieldDecl *, unsigned> fieldInfo;
35+
36+
public:
37+
CIRGenRecordLayout(cir::RecordType completeObjectType)
38+
: completeObjectType(completeObjectType) {}
39+
40+
/// Return the "complete object" LLVM type associated with
41+
/// this record.
42+
cir::RecordType getCIRType() const { return completeObjectType; }
43+
44+
/// Return cir::RecordType element number that corresponds to the field FD.
45+
unsigned getCIRFieldNo(const clang::FieldDecl *FD) const {
46+
FD = FD->getCanonicalDecl();
47+
assert(fieldInfo.count(FD) && "Invalid field for record!");
48+
return fieldInfo.lookup(FD);
49+
}
50+
};
51+
52+
} // namespace clang::CIRGen
53+
54+
#endif
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This contains code to compute the layout of a record.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "CIRGenBuilder.h"
14+
#include "CIRGenModule.h"
15+
#include "CIRGenTypes.h"
16+
17+
#include "clang/AST/ASTContext.h"
18+
#include "clang/AST/Decl.h"
19+
#include "clang/AST/DeclCXX.h"
20+
#include "clang/AST/RecordLayout.h"
21+
#include "clang/CIR/Dialect/IR/CIRAttrs.h"
22+
#include "llvm/Support/Casting.h"
23+
24+
#include <memory>
25+
26+
using namespace llvm;
27+
using namespace clang;
28+
using namespace clang::CIRGen;
29+
30+
namespace {
31+
/// The CIRRecordLowering is responsible for lowering an ASTRecordLayout to an
32+
/// mlir::Type. Some of the lowering is straightforward, some is not.
33+
// TODO: Detail some of the complexities and weirdnesses?
34+
// (See CGRecordLayoutBuilder.cpp)
35+
struct CIRRecordLowering final {
36+
37+
// MemberInfo is a helper structure that contains information about a record
38+
// member. In addition to the standard member types, there exists a sentinel
39+
// member type that ensures correct rounding.
40+
struct MemberInfo final {
41+
CharUnits offset;
42+
enum class InfoKind { Field } kind;
43+
mlir::Type data;
44+
union {
45+
const FieldDecl *fieldDecl;
46+
// CXXRecordDecl will be used here when base types are supported.
47+
};
48+
MemberInfo(CharUnits offset, InfoKind kind, mlir::Type data,
49+
const FieldDecl *fieldDecl = nullptr)
50+
: offset(offset), kind(kind), data(data), fieldDecl(fieldDecl) {};
51+
// MemberInfos are sorted so we define a < operator.
52+
bool operator<(const MemberInfo &other) const {
53+
return offset < other.offset;
54+
}
55+
};
56+
// The constructor.
57+
CIRRecordLowering(CIRGenTypes &cirGenTypes, const RecordDecl *recordDecl,
58+
bool isPacked);
59+
60+
void lower();
61+
62+
void accumulateFields();
63+
64+
CharUnits bitsToCharUnits(uint64_t bitOffset) {
65+
return astContext.toCharUnitsFromBits(bitOffset);
66+
}
67+
68+
CharUnits getSize(mlir::Type Ty) {
69+
assert(!cir::MissingFeatures::recordTypeLayoutInfo());
70+
return CharUnits::One();
71+
}
72+
CharUnits getAlignment(mlir::Type Ty) {
73+
assert(!cir::MissingFeatures::recordTypeLayoutInfo());
74+
return CharUnits::One();
75+
}
76+
77+
mlir::Type getStorageType(const FieldDecl *fieldDecl) {
78+
mlir::Type type = cirGenTypes.convertTypeForMem(fieldDecl->getType());
79+
if (fieldDecl->isBitField()) {
80+
cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(),
81+
"getStorageType for bitfields");
82+
}
83+
return type;
84+
}
85+
86+
uint64_t getFieldBitOffset(const FieldDecl *fieldDecl) {
87+
return astRecordLayout.getFieldOffset(fieldDecl->getFieldIndex());
88+
}
89+
90+
/// Fills out the structures that are ultimately consumed.
91+
void fillOutputFields();
92+
93+
CIRGenTypes &cirGenTypes;
94+
CIRGenBuilderTy &builder;
95+
const ASTContext &astContext;
96+
const RecordDecl *recordDecl;
97+
const ASTRecordLayout &astRecordLayout;
98+
// Helpful intermediate data-structures
99+
std::vector<MemberInfo> members;
100+
// Output fields, consumed by CIRGenTypes::computeRecordLayout
101+
llvm::SmallVector<mlir::Type, 16> fieldTypes;
102+
llvm::DenseMap<const FieldDecl *, unsigned> fields;
103+
104+
LLVM_PREFERRED_TYPE(bool)
105+
unsigned zeroInitializable : 1;
106+
LLVM_PREFERRED_TYPE(bool)
107+
unsigned packed : 1;
108+
LLVM_PREFERRED_TYPE(bool)
109+
unsigned padded : 1;
110+
111+
private:
112+
CIRRecordLowering(const CIRRecordLowering &) = delete;
113+
void operator=(const CIRRecordLowering &) = delete;
114+
}; // CIRRecordLowering
115+
} // namespace
116+
117+
CIRRecordLowering::CIRRecordLowering(CIRGenTypes &cirGenTypes,
118+
const RecordDecl *recordDecl,
119+
bool isPacked)
120+
: cirGenTypes(cirGenTypes), builder(cirGenTypes.getBuilder()),
121+
astContext(cirGenTypes.getASTContext()), recordDecl(recordDecl),
122+
astRecordLayout(
123+
cirGenTypes.getASTContext().getASTRecordLayout(recordDecl)),
124+
zeroInitializable(true), packed(isPacked), padded(false) {}
125+
126+
void CIRRecordLowering::lower() {
127+
if (recordDecl->isUnion()) {
128+
cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(),
129+
"lower: union");
130+
return;
131+
}
132+
133+
if (isa<CXXRecordDecl>(recordDecl)) {
134+
cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(),
135+
"lower: class");
136+
return;
137+
}
138+
139+
assert(!cir::MissingFeatures::cxxSupport());
140+
141+
accumulateFields();
142+
143+
llvm::stable_sort(members);
144+
// TODO: implement clipTailPadding once bitfields are implemented
145+
assert(!cir::MissingFeatures::bitfields());
146+
// TODO: implemented packed records
147+
assert(!cir::MissingFeatures::packedRecords());
148+
// TODO: implement padding
149+
assert(!cir::MissingFeatures::recordPadding());
150+
// TODO: support zeroInit
151+
assert(!cir::MissingFeatures::recordZeroInit());
152+
153+
fillOutputFields();
154+
}
155+
156+
void CIRRecordLowering::fillOutputFields() {
157+
for (const MemberInfo &member : members) {
158+
if (member.data)
159+
fieldTypes.push_back(member.data);
160+
if (member.kind == MemberInfo::InfoKind::Field) {
161+
if (member.fieldDecl)
162+
fields[member.fieldDecl->getCanonicalDecl()] = fieldTypes.size() - 1;
163+
// A field without storage must be a bitfield.
164+
assert(!cir::MissingFeatures::bitfields());
165+
}
166+
assert(!cir::MissingFeatures::cxxSupport());
167+
}
168+
}
169+
170+
void CIRRecordLowering::accumulateFields() {
171+
for (const FieldDecl *field : recordDecl->fields()) {
172+
if (field->isBitField()) {
173+
cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(),
174+
"accumulate bitfields");
175+
++field;
176+
} else if (!field->isZeroSize(astContext)) {
177+
members.push_back(MemberInfo(bitsToCharUnits(getFieldBitOffset(field)),
178+
MemberInfo::InfoKind::Field,
179+
getStorageType(field), field));
180+
++field;
181+
} else {
182+
// TODO(cir): do we want to do anything special about zero size members?
183+
assert(!cir::MissingFeatures::zeroSizeRecordMembers());
184+
++field;
185+
}
186+
}
187+
}
188+
189+
std::unique_ptr<CIRGenRecordLayout>
190+
CIRGenTypes::computeRecordLayout(const RecordDecl *rd, cir::RecordType *ty) {
191+
CIRRecordLowering lowering(*this, rd, /*packed=*/false);
192+
assert(ty->isIncomplete() && "recomputing record layout?");
193+
lowering.lower();
194+
195+
// If we're in C++, compute the base subobject type.
196+
if (llvm::isa<CXXRecordDecl>(rd) && !rd->isUnion() &&
197+
!rd->hasAttr<FinalAttr>()) {
198+
cgm.errorNYI(rd->getSourceRange(), "computeRecordLayout: CXXRecordDecl");
199+
}
200+
201+
// Fill in the record *after* computing the base type. Filling in the body
202+
// signifies that the type is no longer opaque and record layout is complete,
203+
// but we may need to recursively layout rd while laying D out as a base type.
204+
assert(!cir::MissingFeatures::astRecordDeclAttr());
205+
ty->complete(lowering.fieldTypes, lowering.packed, lowering.padded);
206+
207+
auto rl = std::make_unique<CIRGenRecordLayout>(ty ? *ty : cir::RecordType());
208+
209+
assert(!cir::MissingFeatures::recordZeroInit());
210+
assert(!cir::MissingFeatures::cxxSupport());
211+
assert(!cir::MissingFeatures::bitfields());
212+
213+
// Add all the field numbers.
214+
rl->fieldInfo.swap(lowering.fields);
215+
216+
// Dump the layout, if requested.
217+
if (getASTContext().getLangOpts().DumpRecordLayouts) {
218+
cgm.errorNYI(rd->getSourceRange(), "computeRecordLayout: dump layout");
219+
}
220+
221+
// TODO: implement verification
222+
return rl;
223+
}

clang/lib/CIR/CodeGen/CIRGenTypes.cpp

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,18 @@ std::string CIRGenTypes::getRecordTypeName(const clang::RecordDecl *recordDecl,
112112
return builder.getUniqueRecordName(std::string(typeName));
113113
}
114114

115+
// Return true if it is safe to convert the specified record decl to CIR and lay
116+
// it out, false if doing so would cause us to get into a recursive compilation
117+
// mess.
118+
static bool isSafeToConvert(const RecordDecl *RD, CIRGenTypes &CGT) {
119+
// If no records are being laid out, we can certainly do this one.
120+
if (CGT.noRecordsBeingLaidOut())
121+
return true;
122+
123+
assert(!cir::MissingFeatures::recursiveRecordLayout());
124+
return false;
125+
}
126+
115127
/// Lay out a tagged decl type like struct or union.
116128
mlir::Type CIRGenTypes::convertRecordDeclType(const clang::RecordDecl *rd) {
117129
// TagDecl's are not necessarily unique, instead use the (clang) type
@@ -132,7 +144,40 @@ mlir::Type CIRGenTypes::convertRecordDeclType(const clang::RecordDecl *rd) {
132144
if (!rd || !rd->isCompleteDefinition() || entry.isComplete())
133145
return entry;
134146

135-
cgm.errorNYI(rd->getSourceRange(), "Complete record type");
147+
// If converting this type would cause us to infinitely loop, don't do it!
148+
if (!isSafeToConvert(rd, *this)) {
149+
cgm.errorNYI(rd->getSourceRange(), "recursive record layout");
150+
return entry;
151+
}
152+
153+
// Okay, this is a definition of a type. Compile the implementation now.
154+
bool insertResult = recordsBeingLaidOut.insert(key).second;
155+
(void)insertResult;
156+
assert(insertResult && "isSafeToCovert() should have caught this.");
157+
158+
// Force conversion of non-virtual base classes recursively.
159+
if (const auto *cxxRecordDecl = dyn_cast<CXXRecordDecl>(rd)) {
160+
cgm.errorNYI(rd->getSourceRange(), "CXXRecordDecl");
161+
}
162+
163+
// Layout fields.
164+
std::unique_ptr<CIRGenRecordLayout> layout = computeRecordLayout(rd, &entry);
165+
recordDeclTypes[key] = entry;
166+
cirGenRecordLayouts[key] = std::move(layout);
167+
168+
// We're done laying out this record.
169+
bool eraseResult = recordsBeingLaidOut.erase(key);
170+
(void)eraseResult;
171+
assert(eraseResult && "record not in RecordsBeingLaidOut set?");
172+
173+
// If this record blocked a FunctionType conversion, then recompute whatever
174+
// was derived from that.
175+
assert(!cir::MissingFeatures::skippedLayout());
176+
177+
// If we're done converting the outer-most record, then convert any deferred
178+
// records as well.
179+
assert(!cir::MissingFeatures::recursiveRecordLayout());
180+
136181
return entry;
137182
}
138183

0 commit comments

Comments
 (0)