Skip to content
Merged
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
85 changes: 81 additions & 4 deletions clang/lib/CIR/CodeGen/CIRGenTypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,16 +116,93 @@ std::string CIRGenTypes::getRecordTypeName(const clang::RecordDecl *recordDecl,
return builder.getUniqueRecordName(std::string(typeName));
}

/// Return true if the specified type is already completely laid out.
bool CIRGenTypes::isRecordLayoutComplete(const Type *ty) const {
const auto it = recordDeclTypes.find(ty);
return it != recordDeclTypes.end() && it->second.isComplete();
}

// We have multiple forms of this function that call each other, so we need to
// declare one in advance.
static bool
isSafeToConvert(QualType qt, CIRGenTypes &cgt,
llvm::SmallPtrSetImpl<const RecordDecl *> &alreadyChecked);

/// Return true if it is safe to convert the specified record decl to CIR and
/// lay it out, false if doing so would cause us to get into a recursive
/// compilation mess.
static bool
isSafeToConvert(const RecordDecl *rd, CIRGenTypes &cgt,
llvm::SmallPtrSetImpl<const RecordDecl *> &alreadyChecked) {
// If we have already checked this type (maybe the same type is used by-value
// multiple times in multiple record fields, don't check again.
if (!alreadyChecked.insert(rd).second)
return true;

const Type *key = cgt.getASTContext().getTagDeclType(rd).getTypePtr();

// If this type is already laid out, converting it is a noop.
if (cgt.isRecordLayoutComplete(key))
return true;

// If this type is currently being laid out, we can't recursively compile it.
if (cgt.isRecordBeingLaidOut(key))
return false;

// If this type would require laying out bases that are currently being laid
// out, don't do it. This includes virtual base classes which get laid out
// when a class is translated, even though they aren't embedded by-value into
// the class.
if (const CXXRecordDecl *crd = dyn_cast<CXXRecordDecl>(rd)) {
assert(!cir::MissingFeatures::cxxSupport());
cgt.getCGModule().errorNYI(rd->getSourceRange(),
"isSafeToConvert: CXXRecordDecl");
return false;
}

// If this type would require laying out members that are currently being laid
// out, don't do it.
for (const FieldDecl *field : rd->fields())
if (!isSafeToConvert(field->getType(), cgt, alreadyChecked))
return false;

// If there are no problems, lets do it.
return true;
}

/// Return true if it is safe to convert this field type, which requires the
/// record elements contained by-value to all be recursively safe to convert.
static bool
isSafeToConvert(QualType qt, CIRGenTypes &cgt,
llvm::SmallPtrSetImpl<const RecordDecl *> &alreadyChecked) {
// Strip off atomic type sugar.
if (const auto *at = qt->getAs<AtomicType>())
qt = at->getValueType();

// If this is a record, check it.
if (const auto *rt = qt->getAs<RecordType>())
return isSafeToConvert(rt->getDecl(), cgt, alreadyChecked);

// If this is an array, check the elements, which are embedded inline.
if (const auto *at = cgt.getASTContext().getAsArrayType(qt))
return isSafeToConvert(at->getElementType(), cgt, alreadyChecked);

// Otherwise, there is no concern about transforming this. We only care about
// things that are contained by-value in a record that can have another
// record as a member.
return true;
}

// Return true if it is safe to convert the specified record decl to CIR and lay
// it out, false if doing so would cause us to get into a recursive compilation
// mess.
static bool isSafeToConvert(const RecordDecl *RD, CIRGenTypes &CGT) {
static bool isSafeToConvert(const RecordDecl *rd, CIRGenTypes &cgt) {
// If no records are being laid out, we can certainly do this one.
if (CGT.noRecordsBeingLaidOut())
if (cgt.noRecordsBeingLaidOut())
return true;

assert(!cir::MissingFeatures::recursiveRecordLayout());
return false;
llvm::SmallPtrSet<const RecordDecl *, 16> alreadyChecked;
return isSafeToConvert(rd, cgt, alreadyChecked);
}

/// Lay out a tagged decl type like struct or union.
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,11 @@ class CIRGenTypes {
mlir::MLIRContext &getMLIRContext() const;
clang::ASTContext &getASTContext() const { return astContext; }

bool isRecordLayoutComplete(const clang::Type *ty) const;
bool noRecordsBeingLaidOut() const { return recordsBeingLaidOut.empty(); }
bool isRecordBeingLaidOut(const clang::Type *ty) const {
return recordsBeingLaidOut.count(ty);
}

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

Expand Down
21 changes: 21 additions & 0 deletions clang/test/CIR/CodeGen/struct.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,13 @@
// For LLVM IR checks, the structs are defined before the variables, so these
// checks are at the top.
// LLVM: %struct.CompleteS = type { i32, i8 }
// LLVM: %struct.OuterS = type { %struct.InnerS, i32 }
// LLVM: %struct.InnerS = type { i32, i8 }
// LLVM: %struct.PackedS = type <{ i32, i8 }>
// LLVM: %struct.PackedAndPaddedS = type <{ i32, i8, i8 }>
// OGCG: %struct.CompleteS = type { i32, i8 }
// OGCG: %struct.OuterS = type { %struct.InnerS, i32 }
// OGCG: %struct.InnerS = type { i32, i8 }
// OGCG: %struct.PackedS = type <{ i32, i8 }>
// OGCG: %struct.PackedAndPaddedS = type <{ i32, i8, i8 }>

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

struct InnerS {
int a;
char b;
};

struct OuterS {
struct InnerS is;
int c;
};

struct OuterS os;

// CIR: cir.global external @os = #cir.zero : !cir.record<struct
// CIR-SAME: "OuterS" {!cir.record<struct "InnerS" {!s32i, !s8i}>, !s32i}>
// LLVM: @os = dso_local global %struct.OuterS zeroinitializer
// OGCG: @os = global %struct.OuterS zeroinitializer, align 4

#pragma pack(push)
#pragma pack(1)

Expand Down
Loading