Skip to content

Commit 3191cfd

Browse files
authored
[CIR] Upstream limited support for nested structures (#136331)
Previously when we checked to see if it was safe to create CIR for a structure type, we were conservatively saying no if any structure was in the process of being converted. That prevented handling nested structures, even when it would have actually been safe to handle them. Handling structures which recursively reference structures currently being processed requires deferring the handling of the recursively referenced structure, and that still isn't implemented after this change. This change adds the less conservative check that allows handling of non-recursive nested structures.
1 parent cab7538 commit 3191cfd

File tree

3 files changed

+106
-4
lines changed

3 files changed

+106
-4
lines changed

clang/lib/CIR/CodeGen/CIRGenTypes.cpp

+81-4
Original file line numberDiff line numberDiff line change
@@ -116,16 +116,93 @@ std::string CIRGenTypes::getRecordTypeName(const clang::RecordDecl *recordDecl,
116116
return builder.getUniqueRecordName(std::string(typeName));
117117
}
118118

119+
/// Return true if the specified type is already completely laid out.
120+
bool CIRGenTypes::isRecordLayoutComplete(const Type *ty) const {
121+
const auto it = recordDeclTypes.find(ty);
122+
return it != recordDeclTypes.end() && it->second.isComplete();
123+
}
124+
125+
// We have multiple forms of this function that call each other, so we need to
126+
// declare one in advance.
127+
static bool
128+
isSafeToConvert(QualType qt, CIRGenTypes &cgt,
129+
llvm::SmallPtrSetImpl<const RecordDecl *> &alreadyChecked);
130+
131+
/// Return true if it is safe to convert the specified record decl to CIR and
132+
/// lay it out, false if doing so would cause us to get into a recursive
133+
/// compilation mess.
134+
static bool
135+
isSafeToConvert(const RecordDecl *rd, CIRGenTypes &cgt,
136+
llvm::SmallPtrSetImpl<const RecordDecl *> &alreadyChecked) {
137+
// If we have already checked this type (maybe the same type is used by-value
138+
// multiple times in multiple record fields, don't check again.
139+
if (!alreadyChecked.insert(rd).second)
140+
return true;
141+
142+
const Type *key = cgt.getASTContext().getTagDeclType(rd).getTypePtr();
143+
144+
// If this type is already laid out, converting it is a noop.
145+
if (cgt.isRecordLayoutComplete(key))
146+
return true;
147+
148+
// If this type is currently being laid out, we can't recursively compile it.
149+
if (cgt.isRecordBeingLaidOut(key))
150+
return false;
151+
152+
// If this type would require laying out bases that are currently being laid
153+
// out, don't do it. This includes virtual base classes which get laid out
154+
// when a class is translated, even though they aren't embedded by-value into
155+
// the class.
156+
if (const CXXRecordDecl *crd = dyn_cast<CXXRecordDecl>(rd)) {
157+
assert(!cir::MissingFeatures::cxxSupport());
158+
cgt.getCGModule().errorNYI(rd->getSourceRange(),
159+
"isSafeToConvert: CXXRecordDecl");
160+
return false;
161+
}
162+
163+
// If this type would require laying out members that are currently being laid
164+
// out, don't do it.
165+
for (const FieldDecl *field : rd->fields())
166+
if (!isSafeToConvert(field->getType(), cgt, alreadyChecked))
167+
return false;
168+
169+
// If there are no problems, lets do it.
170+
return true;
171+
}
172+
173+
/// Return true if it is safe to convert this field type, which requires the
174+
/// record elements contained by-value to all be recursively safe to convert.
175+
static bool
176+
isSafeToConvert(QualType qt, CIRGenTypes &cgt,
177+
llvm::SmallPtrSetImpl<const RecordDecl *> &alreadyChecked) {
178+
// Strip off atomic type sugar.
179+
if (const auto *at = qt->getAs<AtomicType>())
180+
qt = at->getValueType();
181+
182+
// If this is a record, check it.
183+
if (const auto *rt = qt->getAs<RecordType>())
184+
return isSafeToConvert(rt->getDecl(), cgt, alreadyChecked);
185+
186+
// If this is an array, check the elements, which are embedded inline.
187+
if (const auto *at = cgt.getASTContext().getAsArrayType(qt))
188+
return isSafeToConvert(at->getElementType(), cgt, alreadyChecked);
189+
190+
// Otherwise, there is no concern about transforming this. We only care about
191+
// things that are contained by-value in a record that can have another
192+
// record as a member.
193+
return true;
194+
}
195+
119196
// Return true if it is safe to convert the specified record decl to CIR and lay
120197
// it out, false if doing so would cause us to get into a recursive compilation
121198
// mess.
122-
static bool isSafeToConvert(const RecordDecl *RD, CIRGenTypes &CGT) {
199+
static bool isSafeToConvert(const RecordDecl *rd, CIRGenTypes &cgt) {
123200
// If no records are being laid out, we can certainly do this one.
124-
if (CGT.noRecordsBeingLaidOut())
201+
if (cgt.noRecordsBeingLaidOut())
125202
return true;
126203

127-
assert(!cir::MissingFeatures::recursiveRecordLayout());
128-
return false;
204+
llvm::SmallPtrSet<const RecordDecl *, 16> alreadyChecked;
205+
return isSafeToConvert(rd, cgt, alreadyChecked);
129206
}
130207

131208
/// Lay out a tagged decl type like struct or union.

clang/lib/CIR/CodeGen/CIRGenTypes.h

+4
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,11 @@ class CIRGenTypes {
8989
mlir::MLIRContext &getMLIRContext() const;
9090
clang::ASTContext &getASTContext() const { return astContext; }
9191

92+
bool isRecordLayoutComplete(const clang::Type *ty) const;
9293
bool noRecordsBeingLaidOut() const { return recordsBeingLaidOut.empty(); }
94+
bool isRecordBeingLaidOut(const clang::Type *ty) const {
95+
return recordsBeingLaidOut.count(ty);
96+
}
9397

9498
const ABIInfo &getABIInfo() const { return theABIInfo; }
9599

clang/test/CIR/CodeGen/struct.c

+21
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,13 @@
88
// For LLVM IR checks, the structs are defined before the variables, so these
99
// checks are at the top.
1010
// LLVM: %struct.CompleteS = type { i32, i8 }
11+
// LLVM: %struct.OuterS = type { %struct.InnerS, i32 }
12+
// LLVM: %struct.InnerS = type { i32, i8 }
1113
// LLVM: %struct.PackedS = type <{ i32, i8 }>
1214
// LLVM: %struct.PackedAndPaddedS = type <{ i32, i8, i8 }>
1315
// OGCG: %struct.CompleteS = type { i32, i8 }
16+
// OGCG: %struct.OuterS = type { %struct.InnerS, i32 }
17+
// OGCG: %struct.InnerS = type { i32, i8 }
1418
// OGCG: %struct.PackedS = type <{ i32, i8 }>
1519
// OGCG: %struct.PackedAndPaddedS = type <{ i32, i8, i8 }>
1620

@@ -31,6 +35,23 @@ struct CompleteS {
3135
// LLVM: @cs = dso_local global %struct.CompleteS zeroinitializer
3236
// OGCG: @cs = global %struct.CompleteS zeroinitializer, align 4
3337

38+
struct InnerS {
39+
int a;
40+
char b;
41+
};
42+
43+
struct OuterS {
44+
struct InnerS is;
45+
int c;
46+
};
47+
48+
struct OuterS os;
49+
50+
// CIR: cir.global external @os = #cir.zero : !cir.record<struct
51+
// CIR-SAME: "OuterS" {!cir.record<struct "InnerS" {!s32i, !s8i}>, !s32i}>
52+
// LLVM: @os = dso_local global %struct.OuterS zeroinitializer
53+
// OGCG: @os = global %struct.OuterS zeroinitializer, align 4
54+
3455
#pragma pack(push)
3556
#pragma pack(1)
3657

0 commit comments

Comments
 (0)