-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[CIR] Upstream initial support for complete record types #135844
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
// See https://llvm.org/LICENSE.txt for license information. | ||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#ifndef LLVM_CLANG_LIB_CIR_CIRGENRECORDLAYOUT_H | ||
#define LLVM_CLANG_LIB_CIR_CIRGENRECORDLAYOUT_H | ||
|
||
#include "clang/AST/Decl.h" | ||
#include "clang/CIR/Dialect/IR/CIRTypes.h" | ||
|
||
namespace clang::CIRGen { | ||
|
||
/// This class handles record and union layout info while lowering AST types | ||
/// to CIR types. | ||
/// | ||
/// These layout objects are only created on demand as CIR generation requires. | ||
class CIRGenRecordLayout { | ||
friend class CIRGenTypes; | ||
|
||
CIRGenRecordLayout(const CIRGenRecordLayout &) = delete; | ||
void operator=(const CIRGenRecordLayout &) = delete; | ||
|
||
private: | ||
/// The CIR type corresponding to this record layout; used when laying it out | ||
/// as a complete object. | ||
cir::RecordType completeObjectType; | ||
|
||
/// Map from (non-bit-field) record field to the corresponding cir record type | ||
/// field no. This info is populated by the record builder. | ||
llvm::DenseMap<const clang::FieldDecl *, unsigned> fieldInfo; | ||
|
||
public: | ||
CIRGenRecordLayout(cir::RecordType completeObjectType) | ||
: completeObjectType(completeObjectType) {} | ||
|
||
/// Return the "complete object" LLVM type associated with | ||
/// this record. | ||
cir::RecordType getCIRType() const { return completeObjectType; } | ||
|
||
/// Return cir::RecordType element number that corresponds to the field FD. | ||
unsigned getCIRFieldNo(const clang::FieldDecl *FD) const { | ||
FD = FD->getCanonicalDecl(); | ||
assert(fieldInfo.count(FD) && "Invalid field for record!"); | ||
return fieldInfo.lookup(FD); | ||
} | ||
}; | ||
|
||
} // namespace clang::CIRGen | ||
|
||
#endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,223 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
// See https://llvm.org/LICENSE.txt for license information. | ||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
// | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This contains code to compute the layout of a record. | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#include "CIRGenBuilder.h" | ||
#include "CIRGenModule.h" | ||
#include "CIRGenTypes.h" | ||
|
||
#include "clang/AST/ASTContext.h" | ||
#include "clang/AST/Decl.h" | ||
#include "clang/AST/DeclCXX.h" | ||
#include "clang/AST/RecordLayout.h" | ||
#include "clang/CIR/Dialect/IR/CIRAttrs.h" | ||
#include "llvm/Support/Casting.h" | ||
|
||
#include <memory> | ||
|
||
using namespace llvm; | ||
using namespace clang; | ||
using namespace clang::CIRGen; | ||
|
||
namespace { | ||
/// The CIRRecordLowering is responsible for lowering an ASTRecordLayout to an | ||
/// mlir::Type. Some of the lowering is straightforward, some is not. | ||
// TODO: Detail some of the complexities and weirdnesses? | ||
// (See CGRecordLayoutBuilder.cpp) | ||
struct CIRRecordLowering final { | ||
|
||
// MemberInfo is a helper structure that contains information about a record | ||
// member. In addition to the standard member types, there exists a sentinel | ||
// member type that ensures correct rounding. | ||
struct MemberInfo final { | ||
CharUnits offset; | ||
enum class InfoKind { Field } kind; | ||
mlir::Type data; | ||
union { | ||
const FieldDecl *fieldDecl; | ||
// CXXRecordDecl will be used here when base types are supported. | ||
}; | ||
MemberInfo(CharUnits offset, InfoKind kind, mlir::Type data, | ||
const FieldDecl *fieldDecl = nullptr) | ||
: offset(offset), kind(kind), data(data), fieldDecl(fieldDecl) {}; | ||
// MemberInfos are sorted so we define a < operator. | ||
bool operator<(const MemberInfo &other) const { | ||
return offset < other.offset; | ||
} | ||
}; | ||
// The constructor. | ||
CIRRecordLowering(CIRGenTypes &cirGenTypes, const RecordDecl *recordDecl, | ||
bool isPacked); | ||
|
||
void lower(); | ||
|
||
void accumulateFields(); | ||
|
||
CharUnits bitsToCharUnits(uint64_t bitOffset) { | ||
return astContext.toCharUnitsFromBits(bitOffset); | ||
} | ||
|
||
CharUnits getSize(mlir::Type Ty) { | ||
assert(!cir::MissingFeatures::recordTypeLayoutInfo()); | ||
return CharUnits::One(); | ||
} | ||
CharUnits getAlignment(mlir::Type Ty) { | ||
assert(!cir::MissingFeatures::recordTypeLayoutInfo()); | ||
return CharUnits::One(); | ||
} | ||
|
||
mlir::Type getStorageType(const FieldDecl *fieldDecl) { | ||
mlir::Type type = cirGenTypes.convertTypeForMem(fieldDecl->getType()); | ||
if (fieldDecl->isBitField()) { | ||
cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(), | ||
"getStorageType for bitfields"); | ||
} | ||
return type; | ||
} | ||
|
||
uint64_t getFieldBitOffset(const FieldDecl *fieldDecl) { | ||
return astRecordLayout.getFieldOffset(fieldDecl->getFieldIndex()); | ||
} | ||
|
||
/// Fills out the structures that are ultimately consumed. | ||
void fillOutputFields(); | ||
|
||
CIRGenTypes &cirGenTypes; | ||
CIRGenBuilderTy &builder; | ||
const ASTContext &astContext; | ||
const RecordDecl *recordDecl; | ||
const ASTRecordLayout &astRecordLayout; | ||
// Helpful intermediate data-structures | ||
std::vector<MemberInfo> members; | ||
// Output fields, consumed by CIRGenTypes::computeRecordLayout | ||
llvm::SmallVector<mlir::Type, 16> fieldTypes; | ||
llvm::DenseMap<const FieldDecl *, unsigned> fields; | ||
|
||
LLVM_PREFERRED_TYPE(bool) | ||
unsigned zeroInitializable : 1; | ||
LLVM_PREFERRED_TYPE(bool) | ||
unsigned packed : 1; | ||
LLVM_PREFERRED_TYPE(bool) | ||
unsigned padded : 1; | ||
|
||
private: | ||
CIRRecordLowering(const CIRRecordLowering &) = delete; | ||
void operator=(const CIRRecordLowering &) = delete; | ||
}; // CIRRecordLowering | ||
} // namespace | ||
|
||
CIRRecordLowering::CIRRecordLowering(CIRGenTypes &cirGenTypes, | ||
const RecordDecl *recordDecl, | ||
bool isPacked) | ||
: cirGenTypes(cirGenTypes), builder(cirGenTypes.getBuilder()), | ||
astContext(cirGenTypes.getASTContext()), recordDecl(recordDecl), | ||
astRecordLayout( | ||
cirGenTypes.getASTContext().getASTRecordLayout(recordDecl)), | ||
zeroInitializable(true), packed(isPacked), padded(false) {} | ||
|
||
void CIRRecordLowering::lower() { | ||
if (recordDecl->isUnion()) { | ||
cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(), | ||
"lower: union"); | ||
return; | ||
} | ||
|
||
if (isa<CXXRecordDecl>(recordDecl)) { | ||
cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh my! This ends up being harsher than I expected! This means no record-types in C++ at all (even ones that don't do anything). But if that is the level of support we have, I'm ok with it. I presume a soon-to-be used patch will reduce this to '!virtual && !getNumBases` etc. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Eventually, yes. We aren't handling CXXRecordDecl in the code that gets here yet. I don't expect there to be much more than adding a switch statement for that, but everything in C++ ends up being more involved than I realized, so who knows. |
||
"lower: class"); | ||
return; | ||
} | ||
|
||
assert(!cir::MissingFeatures::cxxSupport()); | ||
|
||
accumulateFields(); | ||
|
||
llvm::stable_sort(members); | ||
// TODO: implement clipTailPadding once bitfields are implemented | ||
assert(!cir::MissingFeatures::bitfields()); | ||
// TODO: implemented packed records | ||
assert(!cir::MissingFeatures::packedRecords()); | ||
// TODO: implement padding | ||
assert(!cir::MissingFeatures::recordPadding()); | ||
// TODO: support zeroInit | ||
assert(!cir::MissingFeatures::recordZeroInit()); | ||
|
||
fillOutputFields(); | ||
} | ||
|
||
void CIRRecordLowering::fillOutputFields() { | ||
for (const MemberInfo &member : members) { | ||
if (member.data) | ||
fieldTypes.push_back(member.data); | ||
if (member.kind == MemberInfo::InfoKind::Field) { | ||
if (member.fieldDecl) | ||
fields[member.fieldDecl->getCanonicalDecl()] = fieldTypes.size() - 1; | ||
// A field without storage must be a bitfield. | ||
assert(!cir::MissingFeatures::bitfields()); | ||
} | ||
assert(!cir::MissingFeatures::cxxSupport()); | ||
} | ||
} | ||
|
||
void CIRRecordLowering::accumulateFields() { | ||
for (const FieldDecl *field : recordDecl->fields()) { | ||
if (field->isBitField()) { | ||
cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(), | ||
"accumulate bitfields"); | ||
++field; | ||
} else if (!field->isZeroSize(astContext)) { | ||
members.push_back(MemberInfo(bitsToCharUnits(getFieldBitOffset(field)), | ||
MemberInfo::InfoKind::Field, | ||
getStorageType(field), field)); | ||
++field; | ||
} else { | ||
// TODO(cir): do we want to do anything special about zero size members? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe add an assert on missing feature here? this will lead to LLVM IR lowering differences so might be good to track. |
||
assert(!cir::MissingFeatures::zeroSizeRecordMembers()); | ||
++field; | ||
} | ||
} | ||
} | ||
|
||
std::unique_ptr<CIRGenRecordLayout> | ||
CIRGenTypes::computeRecordLayout(const RecordDecl *rd, cir::RecordType *ty) { | ||
CIRRecordLowering lowering(*this, rd, /*packed=*/false); | ||
assert(ty->isIncomplete() && "recomputing record layout?"); | ||
lowering.lower(); | ||
|
||
// If we're in C++, compute the base subobject type. | ||
if (llvm::isa<CXXRecordDecl>(rd) && !rd->isUnion() && | ||
!rd->hasAttr<FinalAttr>()) { | ||
cgm.errorNYI(rd->getSourceRange(), "computeRecordLayout: CXXRecordDecl"); | ||
} | ||
|
||
// Fill in the record *after* computing the base type. Filling in the body | ||
// signifies that the type is no longer opaque and record layout is complete, | ||
// but we may need to recursively layout rd while laying D out as a base type. | ||
assert(!cir::MissingFeatures::astRecordDeclAttr()); | ||
ty->complete(lowering.fieldTypes, lowering.packed, lowering.padded); | ||
|
||
auto rl = std::make_unique<CIRGenRecordLayout>(ty ? *ty : cir::RecordType()); | ||
|
||
assert(!cir::MissingFeatures::recordZeroInit()); | ||
assert(!cir::MissingFeatures::cxxSupport()); | ||
assert(!cir::MissingFeatures::bitfields()); | ||
|
||
// Add all the field numbers. | ||
rl->fieldInfo.swap(lowering.fields); | ||
|
||
// Dump the layout, if requested. | ||
if (getASTContext().getLangOpts().DumpRecordLayouts) { | ||
cgm.errorNYI(rd->getSourceRange(), "computeRecordLayout: dump layout"); | ||
} | ||
|
||
// TODO: implement verification | ||
return rl; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we also NYI on base types? Particularly with virtual base types this gets ugly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I cut a lot of code out from here. Anything other than unions will fail somewhere else before we get here, but I guess it wouldn't hurt to add more checks here now.