blob: 05e8848884f7ea90ed3b735a243bc37d57b3e4e2 [file] [log] [blame]
//===----------------------------------------------------------------------===//
//
// 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 "clang/CIR/Dialect/IR/CIRDataLayout.h"
#include "clang/CIR/MissingFeatures.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, Base } kind;
mlir::Type data;
union {
const FieldDecl *fieldDecl;
const CXXRecordDecl *cxxRecordDecl;
};
MemberInfo(CharUnits offset, InfoKind kind, mlir::Type data,
const FieldDecl *fieldDecl = nullptr)
: offset{offset}, kind{kind}, data{data}, fieldDecl{fieldDecl} {}
MemberInfo(CharUnits offset, InfoKind kind, mlir::Type data,
const CXXRecordDecl *rd)
: offset{offset}, kind{kind}, data{data}, cxxRecordDecl{rd} {}
// 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 packed);
/// Constructs a MemberInfo instance from an offset and mlir::Type.
MemberInfo makeStorageInfo(CharUnits offset, mlir::Type data) {
return MemberInfo(offset, MemberInfo::InfoKind::Field, data);
}
// Layout routines.
void setBitFieldInfo(const FieldDecl *fd, CharUnits startOffset,
mlir::Type storageType);
void lower();
void lowerUnion();
/// Determines if we need a packed llvm struct.
void determinePacked();
/// Inserts padding everywhere it's needed.
void insertPadding();
void computeVolatileBitfields();
void accumulateBases(const CXXRecordDecl *cxxRecordDecl);
void accumulateVPtrs();
void accumulateFields();
RecordDecl::field_iterator
accumulateBitFields(RecordDecl::field_iterator field,
RecordDecl::field_iterator fieldEnd);
bool isAAPCS() const {
return astContext.getTargetInfo().getABI().starts_with("aapcs");
}
CharUnits bitsToCharUnits(uint64_t bitOffset) {
return astContext.toCharUnitsFromBits(bitOffset);
}
void calculateZeroInit();
CharUnits getSize(mlir::Type Ty) {
return CharUnits::fromQuantity(dataLayout.layout.getTypeSize(Ty));
}
CharUnits getSizeInBits(mlir::Type ty) {
return CharUnits::fromQuantity(dataLayout.layout.getTypeSizeInBits(ty));
}
CharUnits getAlignment(mlir::Type Ty) {
return CharUnits::fromQuantity(dataLayout.layout.getTypeABIAlignment(Ty));
}
bool isZeroInitializable(const FieldDecl *fd) {
return cirGenTypes.isZeroInitializable(fd->getType());
}
bool isZeroInitializable(const RecordDecl *rd) {
return cirGenTypes.isZeroInitializable(rd);
}
/// Wraps cir::IntType with some implicit arguments.
mlir::Type getUIntNType(uint64_t numBits) {
unsigned alignedBits = llvm::PowerOf2Ceil(numBits);
alignedBits = std::max(8u, alignedBits);
return cir::IntType::get(&cirGenTypes.getMLIRContext(), alignedBits,
/*isSigned=*/false);
}
mlir::Type getCharType() {
return cir::IntType::get(&cirGenTypes.getMLIRContext(),
astContext.getCharWidth(),
/*isSigned=*/false);
}
mlir::Type getByteArrayType(CharUnits numberOfChars) {
assert(!numberOfChars.isZero() && "Empty byte arrays aren't allowed.");
mlir::Type type = getCharType();
return numberOfChars == CharUnits::One()
? type
: cir::ArrayType::get(type, numberOfChars.getQuantity());
}
// Gets the CIR BaseSubobject type from a CXXRecordDecl.
mlir::Type getStorageType(const CXXRecordDecl *RD) {
return cirGenTypes.getCIRGenRecordLayout(RD).getBaseSubobjectCIRType();
}
// This is different from LLVM traditional codegen because CIRGen uses arrays
// of bytes instead of arbitrary-sized integers. This is important for packed
// structures support.
mlir::Type getBitfieldStorageType(unsigned numBits) {
unsigned alignedBits = llvm::alignTo(numBits, astContext.getCharWidth());
if (cir::isValidFundamentalIntWidth(alignedBits))
return builder.getUIntNTy(alignedBits);
mlir::Type type = getCharType();
return cir::ArrayType::get(type, alignedBits / astContext.getCharWidth());
}
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();
void appendPaddingBytes(CharUnits size) {
if (!size.isZero()) {
fieldTypes.push_back(getByteArrayType(size));
padded = true;
}
}
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 *, CIRGenBitFieldInfo> bitFields;
llvm::DenseMap<const FieldDecl *, unsigned> fieldIdxMap;
llvm::DenseMap<const CXXRecordDecl *, unsigned> nonVirtualBases;
cir::CIRDataLayout dataLayout;
LLVM_PREFERRED_TYPE(bool)
unsigned zeroInitializable : 1;
LLVM_PREFERRED_TYPE(bool)
unsigned zeroInitializableAsBase : 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 packed)
: cirGenTypes(cirGenTypes), builder(cirGenTypes.getBuilder()),
astContext(cirGenTypes.getASTContext()), recordDecl(recordDecl),
astRecordLayout(
cirGenTypes.getASTContext().getASTRecordLayout(recordDecl)),
dataLayout(cirGenTypes.getCGModule().getModule()),
zeroInitializable(true), zeroInitializableAsBase(true), packed(packed),
padded(false) {}
void CIRRecordLowering::setBitFieldInfo(const FieldDecl *fd,
CharUnits startOffset,
mlir::Type storageType) {
CIRGenBitFieldInfo &info = bitFields[fd->getCanonicalDecl()];
info.isSigned = fd->getType()->isSignedIntegerOrEnumerationType();
info.offset =
(unsigned)(getFieldBitOffset(fd) - astContext.toBits(startOffset));
info.size = fd->getBitWidthValue();
info.storageSize = getSizeInBits(storageType).getQuantity();
info.storageOffset = startOffset;
info.storageType = storageType;
info.name = fd->getName();
if (info.size > info.storageSize)
info.size = info.storageSize;
// Reverse the bit offsets for big endian machines. Since bitfields are laid
// out as packed bits within an integer-sized unit, we can imagine the bits
// counting from the most-significant-bit instead of the
// least-significant-bit.
if (dataLayout.isBigEndian())
info.offset = info.storageSize - (info.offset + info.size);
info.volatileStorageSize = 0;
info.volatileOffset = 0;
info.volatileStorageOffset = CharUnits::Zero();
}
void CIRRecordLowering::lower() {
if (recordDecl->isUnion()) {
lowerUnion();
computeVolatileBitfields();
return;
}
assert(!cir::MissingFeatures::recordLayoutVirtualBases());
CharUnits size = astRecordLayout.getSize();
accumulateFields();
if (const auto *cxxRecordDecl = dyn_cast<CXXRecordDecl>(recordDecl)) {
accumulateVPtrs();
accumulateBases(cxxRecordDecl);
if (members.empty()) {
appendPaddingBytes(size);
computeVolatileBitfields();
return;
}
assert(!cir::MissingFeatures::recordLayoutVirtualBases());
}
llvm::stable_sort(members);
// TODO: implement clipTailPadding once bitfields are implemented
assert(!cir::MissingFeatures::bitfields());
assert(!cir::MissingFeatures::recordZeroInit());
members.push_back(makeStorageInfo(size, getUIntNType(8)));
determinePacked();
insertPadding();
members.pop_back();
calculateZeroInit();
fillOutputFields();
computeVolatileBitfields();
}
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)
fieldIdxMap[member.fieldDecl->getCanonicalDecl()] =
fieldTypes.size() - 1;
// A field without storage must be a bitfield.
assert(!cir::MissingFeatures::bitfields());
if (!member.data)
setBitFieldInfo(member.fieldDecl, member.offset, fieldTypes.back());
} else if (member.kind == MemberInfo::InfoKind::Base) {
nonVirtualBases[member.cxxRecordDecl] = fieldTypes.size() - 1;
}
assert(!cir::MissingFeatures::recordLayoutVirtualBases());
}
}
RecordDecl::field_iterator
CIRRecordLowering::accumulateBitFields(RecordDecl::field_iterator field,
RecordDecl::field_iterator fieldEnd) {
assert(!cir::MissingFeatures::isDiscreteBitFieldABI());
CharUnits regSize =
bitsToCharUnits(astContext.getTargetInfo().getRegisterWidth());
unsigned charBits = astContext.getCharWidth();
// Data about the start of the span we're accumulating to create an access
// unit from. 'Begin' is the first bitfield of the span. If 'begin' is
// 'fieldEnd', we've not got a current span. The span starts at the
// 'beginOffset' character boundary. 'bitSizeSinceBegin' is the size (in bits)
// of the span -- this might include padding when we've advanced to a
// subsequent bitfield run.
RecordDecl::field_iterator begin = fieldEnd;
CharUnits beginOffset;
uint64_t bitSizeSinceBegin;
// The (non-inclusive) end of the largest acceptable access unit we've found
// since 'begin'. If this is 'begin', we're gathering the initial set of
// bitfields of a new span. 'bestEndOffset' is the end of that acceptable
// access unit -- it might extend beyond the last character of the bitfield
// run, using available padding characters.
RecordDecl::field_iterator bestEnd = begin;
CharUnits bestEndOffset;
bool bestClipped; // Whether the representation must be in a byte array.
for (;;) {
// atAlignedBoundary is true if 'field' is the (potential) start of a new
// span (or the end of the bitfields). When true, limitOffset is the
// character offset of that span and barrier indicates whether the new
// span cannot be merged into the current one.
bool atAlignedBoundary = false;
bool barrier = false; // a barrier can be a zero Bit Width or non bit member
if (field != fieldEnd && field->isBitField()) {
uint64_t bitOffset = getFieldBitOffset(*field);
if (begin == fieldEnd) {
// Beginning a new span.
begin = field;
bestEnd = begin;
assert((bitOffset % charBits) == 0 && "Not at start of char");
beginOffset = bitsToCharUnits(bitOffset);
bitSizeSinceBegin = 0;
} else if ((bitOffset % charBits) != 0) {
// Bitfield occupies the same character as previous bitfield, it must be
// part of the same span. This can include zero-length bitfields, should
// the target not align them to character boundaries. Such non-alignment
// is at variance with the standards, which require zero-length
// bitfields be a barrier between access units. But of course we can't
// achieve that in the middle of a character.
assert(bitOffset ==
astContext.toBits(beginOffset) + bitSizeSinceBegin &&
"Concatenating non-contiguous bitfields");
} else {
// Bitfield potentially begins a new span. This includes zero-length
// bitfields on non-aligning targets that lie at character boundaries
// (those are barriers to merging).
if (field->isZeroLengthBitField())
barrier = true;
atAlignedBoundary = true;
}
} else {
// We've reached the end of the bitfield run. Either we're done, or this
// is a barrier for the current span.
if (begin == fieldEnd)
break;
barrier = true;
atAlignedBoundary = true;
}
// 'installBest' indicates whether we should create an access unit for the
// current best span: fields ['begin', 'bestEnd') occupying characters
// ['beginOffset', 'bestEndOffset').
bool installBest = false;
if (atAlignedBoundary) {
// 'field' is the start of a new span or the end of the bitfields. The
// just-seen span now extends to 'bitSizeSinceBegin'.
// Determine if we can accumulate that just-seen span into the current
// accumulation.
CharUnits accessSize = bitsToCharUnits(bitSizeSinceBegin + charBits - 1);
if (bestEnd == begin) {
// This is the initial run at the start of a new span. By definition,
// this is the best seen so far.
bestEnd = field;
bestEndOffset = beginOffset + accessSize;
// Assume clipped until proven not below.
bestClipped = true;
if (!bitSizeSinceBegin)
// A zero-sized initial span -- this will install nothing and reset
// for another.
installBest = true;
} else if (accessSize > regSize) {
// Accumulating the just-seen span would create a multi-register access
// unit, which would increase register pressure.
installBest = true;
}
if (!installBest) {
// Determine if accumulating the just-seen span will create an expensive
// access unit or not.
mlir::Type type = getUIntNType(astContext.toBits(accessSize));
if (!astContext.getTargetInfo().hasCheapUnalignedBitFieldAccess())
cirGenTypes.getCGModule().errorNYI(
field->getSourceRange(), "NYI CheapUnalignedBitFieldAccess");
if (!installBest) {
// Find the next used storage offset to determine what the limit of
// the current span is. That's either the offset of the next field
// with storage (which might be field itself) or the end of the
// non-reusable tail padding.
CharUnits limitOffset;
for (auto probe = field; probe != fieldEnd; ++probe)
if (!isEmptyFieldForLayout(astContext, *probe)) {
// A member with storage sets the limit.
assert((getFieldBitOffset(*probe) % charBits) == 0 &&
"Next storage is not byte-aligned");
limitOffset = bitsToCharUnits(getFieldBitOffset(*probe));
goto FoundLimit;
}
assert(!cir::MissingFeatures::cxxSupport());
limitOffset = astRecordLayout.getDataSize();
FoundLimit:
CharUnits typeSize = getSize(type);
if (beginOffset + typeSize <= limitOffset) {
// There is space before limitOffset to create a naturally-sized
// access unit.
bestEndOffset = beginOffset + typeSize;
bestEnd = field;
bestClipped = false;
}
if (barrier) {
// The next field is a barrier that we cannot merge across.
installBest = true;
} else if (cirGenTypes.getCGModule()
.getCodeGenOpts()
.FineGrainedBitfieldAccesses) {
assert(!cir::MissingFeatures::nonFineGrainedBitfields());
cirGenTypes.getCGModule().errorNYI(field->getSourceRange(),
"NYI FineGrainedBitfield");
} else {
// Otherwise, we're not installing. Update the bit size
// of the current span to go all the way to limitOffset, which is
// the (aligned) offset of next bitfield to consider.
bitSizeSinceBegin = astContext.toBits(limitOffset - beginOffset);
}
}
}
}
if (installBest) {
assert((field == fieldEnd || !field->isBitField() ||
(getFieldBitOffset(*field) % charBits) == 0) &&
"Installing but not at an aligned bitfield or limit");
CharUnits accessSize = bestEndOffset - beginOffset;
if (!accessSize.isZero()) {
// Add the storage member for the access unit to the record. The
// bitfields get the offset of their storage but come afterward and
// remain there after a stable sort.
mlir::Type type;
if (bestClipped) {
assert(getSize(getUIntNType(astContext.toBits(accessSize))) >
accessSize &&
"Clipped access need not be clipped");
type = getByteArrayType(accessSize);
} else {
type = getUIntNType(astContext.toBits(accessSize));
assert(getSize(type) == accessSize &&
"Unclipped access must be clipped");
}
members.push_back(makeStorageInfo(beginOffset, type));
for (; begin != bestEnd; ++begin)
if (!begin->isZeroLengthBitField())
members.push_back(MemberInfo(
beginOffset, MemberInfo::InfoKind::Field, nullptr, *begin));
}
// Reset to start a new span.
field = bestEnd;
begin = fieldEnd;
} else {
assert(field != fieldEnd && field->isBitField() &&
"Accumulating past end of bitfields");
assert(!barrier && "Accumulating across barrier");
// Accumulate this bitfield into the current (potential) span.
bitSizeSinceBegin += field->getBitWidthValue();
++field;
}
}
return field;
}
void CIRRecordLowering::accumulateFields() {
for (RecordDecl::field_iterator field = recordDecl->field_begin(),
fieldEnd = recordDecl->field_end();
field != fieldEnd;) {
if (field->isBitField()) {
RecordDecl::field_iterator start = field;
// Iterate to gather the list of bitfields.
for (++field; field != fieldEnd && field->isBitField(); ++field)
;
field = accumulateBitFields(start, field);
assert((field == fieldEnd || !field->isBitField()) &&
"Failed to accumulate all the bitfields");
} 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?
assert(!cir::MissingFeatures::zeroSizeRecordMembers());
++field;
}
}
}
void CIRRecordLowering::calculateZeroInit() {
for (const MemberInfo &member : members) {
if (member.kind == MemberInfo::InfoKind::Field) {
if (!member.fieldDecl || isZeroInitializable(member.fieldDecl))
continue;
zeroInitializable = zeroInitializableAsBase = false;
return;
} else if (member.kind == MemberInfo::InfoKind::Base) {
if (isZeroInitializable(member.cxxRecordDecl))
continue;
zeroInitializable = false;
if (member.kind == MemberInfo::InfoKind::Base)
zeroInitializableAsBase = false;
}
assert(!cir::MissingFeatures::recordLayoutVirtualBases());
}
}
void CIRRecordLowering::determinePacked() {
if (packed)
return;
CharUnits alignment = CharUnits::One();
// TODO(cir): handle non-virtual base types
assert(!cir::MissingFeatures::cxxSupport());
for (const MemberInfo &member : members) {
if (!member.data)
continue;
// If any member falls at an offset that it not a multiple of its alignment,
// then the entire record must be packed.
if (member.offset % getAlignment(member.data))
packed = true;
alignment = std::max(alignment, getAlignment(member.data));
}
// If the size of the record (the capstone's offset) is not a multiple of the
// record's alignment, it must be packed.
if (members.back().offset % alignment)
packed = true;
// Update the alignment of the sentinel.
if (!packed)
members.back().data = getUIntNType(astContext.toBits(alignment));
}
void CIRRecordLowering::insertPadding() {
std::vector<std::pair<CharUnits, CharUnits>> padding;
CharUnits size = CharUnits::Zero();
for (const MemberInfo &member : members) {
if (!member.data)
continue;
CharUnits offset = member.offset;
assert(offset >= size);
// Insert padding if we need to.
if (offset !=
size.alignTo(packed ? CharUnits::One() : getAlignment(member.data)))
padding.push_back(std::make_pair(size, offset - size));
size = offset + getSize(member.data);
}
if (padding.empty())
return;
padded = true;
// Add the padding to the Members list and sort it.
for (const std::pair<CharUnits, CharUnits> &paddingPair : padding)
members.push_back(makeStorageInfo(paddingPair.first,
getByteArrayType(paddingPair.second)));
llvm::stable_sort(members);
}
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.
cir::RecordType baseTy;
if (llvm::isa<CXXRecordDecl>(rd) && !rd->isUnion() &&
!rd->hasAttr<FinalAttr>()) {
baseTy = *ty;
if (lowering.astRecordLayout.getNonVirtualSize() !=
lowering.astRecordLayout.getSize()) {
CIRRecordLowering baseLowering(*this, rd, /*Packed=*/lowering.packed);
baseLowering.lower();
std::string baseIdentifier = getRecordTypeName(rd, ".base");
baseTy =
builder.getCompleteRecordTy(baseLowering.fieldTypes, baseIdentifier,
baseLowering.packed, baseLowering.padded);
// TODO(cir): add something like addRecordTypeName
// BaseTy and Ty must agree on their packedness for getCIRFieldNo to work
// on both of them with the same index.
assert(lowering.packed == baseLowering.packed &&
"Non-virtual and complete types must agree on packedness");
}
}
// 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{}, baseTy ? baseTy : cir::RecordType{},
(bool)lowering.zeroInitializable, (bool)lowering.zeroInitializableAsBase);
assert(!cir::MissingFeatures::recordZeroInit());
rl->nonVirtualBases.swap(lowering.nonVirtualBases);
assert(!cir::MissingFeatures::cxxSupport());
assert(!cir::MissingFeatures::bitfields());
// Add all the field numbers.
rl->fieldIdxMap.swap(lowering.fieldIdxMap);
rl->bitFields.swap(lowering.bitFields);
// Dump the layout, if requested.
if (getASTContext().getLangOpts().DumpRecordLayouts) {
llvm::outs() << "\n*** Dumping CIRgen Record Layout\n";
llvm::outs() << "Record: ";
rd->dump(llvm::outs());
llvm::outs() << "\nLayout: ";
rl->print(llvm::outs());
}
// TODO: implement verification
return rl;
}
void CIRGenRecordLayout::print(raw_ostream &os) const {
os << "<CIRecordLayout\n";
os << " CIR Type:" << completeObjectType << "\n";
if (baseSubobjectType)
os << " NonVirtualBaseCIRType:" << baseSubobjectType << "\n";
os << " IsZeroInitializable:" << zeroInitializable << "\n";
os << " BitFields:[\n";
std::vector<std::pair<unsigned, const CIRGenBitFieldInfo *>> bitInfo;
for (auto &[decl, info] : bitFields) {
const RecordDecl *rd = decl->getParent();
unsigned index = 0;
for (RecordDecl::field_iterator it = rd->field_begin(); *it != decl; ++it)
++index;
bitInfo.push_back(std::make_pair(index, &info));
}
llvm::array_pod_sort(bitInfo.begin(), bitInfo.end());
for (std::pair<unsigned, const CIRGenBitFieldInfo *> &info : bitInfo) {
os.indent(4);
info.second->print(os);
os << "\n";
}
os << " ]>\n";
}
void CIRGenBitFieldInfo::print(raw_ostream &os) const {
os << "<CIRBitFieldInfo" << " name:" << name << " offset:" << offset
<< " size:" << size << " isSigned:" << isSigned
<< " storageSize:" << storageSize
<< " storageOffset:" << storageOffset.getQuantity()
<< " volatileOffset:" << volatileOffset
<< " volatileStorageSize:" << volatileStorageSize
<< " volatileStorageOffset:" << volatileStorageOffset.getQuantity() << ">";
}
void CIRGenRecordLayout::dump() const { print(llvm::errs()); }
void CIRGenBitFieldInfo::dump() const { print(llvm::errs()); }
void CIRRecordLowering::lowerUnion() {
CharUnits layoutSize = astRecordLayout.getSize();
mlir::Type storageType = nullptr;
bool seenNamedMember = false;
// Iterate through the fields setting bitFieldInfo and the Fields array. Also
// locate the "most appropriate" storage type.
for (const FieldDecl *field : recordDecl->fields()) {
mlir::Type fieldType;
if (field->isBitField()) {
if (field->isZeroLengthBitField())
continue;
fieldType = getBitfieldStorageType(field->getBitWidthValue());
setBitFieldInfo(field, CharUnits::Zero(), fieldType);
} else {
fieldType = getStorageType(field);
}
// This maps a field to its index. For unions, the index is always 0.
fieldIdxMap[field->getCanonicalDecl()] = 0;
// Compute zero-initializable status.
// This union might not be zero initialized: it may contain a pointer to
// data member which might have some exotic initialization sequence.
// If this is the case, then we ought not to try and come up with a "better"
// type, it might not be very easy to come up with a Constant which
// correctly initializes it.
if (!seenNamedMember) {
seenNamedMember = field->getIdentifier();
if (!seenNamedMember)
if (const RecordDecl *fieldRD = field->getType()->getAsRecordDecl())
seenNamedMember = fieldRD->findFirstNamedDataMember();
if (seenNamedMember && !isZeroInitializable(field)) {
zeroInitializable = zeroInitializableAsBase = false;
storageType = fieldType;
}
}
// Because our union isn't zero initializable, we won't be getting a better
// storage type.
if (!zeroInitializable)
continue;
// Conditionally update our storage type if we've got a new "better" one.
if (!storageType || getAlignment(fieldType) > getAlignment(storageType) ||
(getAlignment(fieldType) == getAlignment(storageType) &&
getSize(fieldType) > getSize(storageType)))
storageType = fieldType;
// NOTE(cir): Track all union member's types, not just the largest one. It
// allows for proper type-checking and retain more info for analisys.
fieldTypes.push_back(fieldType);
}
if (!storageType)
cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(),
"No-storage Union NYI");
if (layoutSize < getSize(storageType))
storageType = getByteArrayType(layoutSize);
else
appendPaddingBytes(layoutSize - getSize(storageType));
// Set packed if we need it.
if (layoutSize % getAlignment(storageType))
packed = true;
}
/// The AAPCS that defines that, when possible, bit-fields should
/// be accessed using containers of the declared type width:
/// When a volatile bit-field is read, and its container does not overlap with
/// any non-bit-field member or any zero length bit-field member, its container
/// must be read exactly once using the access width appropriate to the type of
/// the container. When a volatile bit-field is written, and its container does
/// not overlap with any non-bit-field member or any zero-length bit-field
/// member, its container must be read exactly once and written exactly once
/// using the access width appropriate to the type of the container. The two
/// accesses are not atomic.
///
/// Enforcing the width restriction can be disabled using
/// -fno-aapcs-bitfield-width.
void CIRRecordLowering::computeVolatileBitfields() {
if (!isAAPCS() ||
!cirGenTypes.getCGModule().getCodeGenOpts().AAPCSBitfieldWidth)
return;
assert(!cir::MissingFeatures::armComputeVolatileBitfields());
}
void CIRRecordLowering::accumulateBases(const CXXRecordDecl *cxxRecordDecl) {
// If we've got a primary virtual base, we need to add it with the bases.
if (astRecordLayout.isPrimaryBaseVirtual()) {
cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(),
"accumulateBases: primary virtual base");
}
// Accumulate the non-virtual bases.
for ([[maybe_unused]] const auto &base : cxxRecordDecl->bases()) {
if (base.isVirtual()) {
cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(),
"accumulateBases: virtual base");
continue;
}
// Bases can be zero-sized even if not technically empty if they
// contain only a trailing array member.
const CXXRecordDecl *baseDecl = base.getType()->getAsCXXRecordDecl();
if (!baseDecl->isEmpty() &&
!astContext.getASTRecordLayout(baseDecl).getNonVirtualSize().isZero()) {
members.push_back(MemberInfo(astRecordLayout.getBaseClassOffset(baseDecl),
MemberInfo::InfoKind::Base,
getStorageType(baseDecl), baseDecl));
}
}
}
void CIRRecordLowering::accumulateVPtrs() {
if (astRecordLayout.hasOwnVFPtr())
cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(),
"accumulateVPtrs: hasOwnVFPtr");
if (astRecordLayout.hasOwnVBPtr())
cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(),
"accumulateVPtrs: hasOwnVBPtr");
}