
This PR adds support for loading and storing volatile bit-field members according to the AAPCS specification. > A volatile bit-field must always be accessed using an access width appropriate to the type of its container, except when any of the following are true: > > * The bit-field container overlaps with a zero-length bit-field. > * The bit-field container overlaps with a non-bit-field member. For example, if a bit-field is declared as `int`, the load/store must use a 32-bit access, even if the field itself is only 3 bits wide.
915 lines
35 KiB
C++
915 lines
35 KiB
C++
//===----------------------------------------------------------------------===//
|
|
//
|
|
// 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 { VFPtr, 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);
|
|
|
|
mlir::Type getVFPtrType();
|
|
|
|
bool isAAPCS() const {
|
|
return astContext.getTargetInfo().getABI().starts_with("aapcs");
|
|
}
|
|
|
|
/// Helper function to check if the target machine is BigEndian.
|
|
bool isBigEndian() const { return astContext.getTargetInfo().isBigEndian(); }
|
|
|
|
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) {
|
|
installBest = true;
|
|
} 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()) {
|
|
field = accumulateBitFields(field, fieldEnd);
|
|
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;
|
|
|
|
for (auto &[field, info] : bitFields) {
|
|
mlir::Type resLTy = cirGenTypes.convertTypeForMem(field->getType());
|
|
|
|
if (astContext.toBits(astRecordLayout.getAlignment()) <
|
|
getSizeInBits(resLTy).getQuantity())
|
|
continue;
|
|
|
|
// CIRRecordLowering::setBitFieldInfo() pre-adjusts the bit-field offsets
|
|
// for big-endian targets, but it assumes a container of width
|
|
// info.storageSize. Since AAPCS uses a different container size (width
|
|
// of the type), we first undo that calculation here and redo it once
|
|
// the bit-field offset within the new container is calculated.
|
|
const unsigned oldOffset =
|
|
isBigEndian() ? info.storageSize - (info.offset + info.size)
|
|
: info.offset;
|
|
// Offset to the bit-field from the beginning of the struct.
|
|
const unsigned absoluteOffset =
|
|
astContext.toBits(info.storageOffset) + oldOffset;
|
|
|
|
// Container size is the width of the bit-field type.
|
|
const unsigned storageSize = getSizeInBits(resLTy).getQuantity();
|
|
// Nothing to do if the access uses the desired
|
|
// container width and is naturally aligned.
|
|
if (info.storageSize == storageSize && (oldOffset % storageSize == 0))
|
|
continue;
|
|
|
|
// Offset within the container.
|
|
unsigned offset = absoluteOffset & (storageSize - 1);
|
|
// Bail out if an aligned load of the container cannot cover the entire
|
|
// bit-field. This can happen for example, if the bit-field is part of a
|
|
// packed struct. AAPCS does not define access rules for such cases, we let
|
|
// clang to follow its own rules.
|
|
if (offset + info.size > storageSize)
|
|
continue;
|
|
|
|
// Re-adjust offsets for big-endian targets.
|
|
if (isBigEndian())
|
|
offset = storageSize - (offset + info.size);
|
|
|
|
const CharUnits storageOffset =
|
|
astContext.toCharUnitsFromBits(absoluteOffset & ~(storageSize - 1));
|
|
const CharUnits end = storageOffset +
|
|
astContext.toCharUnitsFromBits(storageSize) -
|
|
CharUnits::One();
|
|
|
|
const ASTRecordLayout &layout =
|
|
astContext.getASTRecordLayout(field->getParent());
|
|
// If we access outside memory outside the record, than bail out.
|
|
const CharUnits recordSize = layout.getSize();
|
|
if (end >= recordSize)
|
|
continue;
|
|
|
|
// Bail out if performing this load would access non-bit-fields members.
|
|
bool conflict = false;
|
|
for (const auto *f : recordDecl->fields()) {
|
|
// Allow sized bit-fields overlaps.
|
|
if (f->isBitField() && !f->isZeroLengthBitField())
|
|
continue;
|
|
|
|
const CharUnits fOffset = astContext.toCharUnitsFromBits(
|
|
layout.getFieldOffset(f->getFieldIndex()));
|
|
|
|
// As C11 defines, a zero sized bit-field defines a barrier, so
|
|
// fields after and before it should be race condition free.
|
|
// The AAPCS acknowledges it and imposes no restritions when the
|
|
// natural container overlaps a zero-length bit-field.
|
|
if (f->isZeroLengthBitField()) {
|
|
if (end > fOffset && storageOffset < fOffset) {
|
|
conflict = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
const CharUnits fEnd =
|
|
fOffset +
|
|
astContext.toCharUnitsFromBits(
|
|
getSizeInBits(cirGenTypes.convertTypeForMem(f->getType()))
|
|
.getQuantity()) -
|
|
CharUnits::One();
|
|
// If no overlap, continue.
|
|
if (end < fOffset || fEnd < storageOffset)
|
|
continue;
|
|
|
|
// The desired load overlaps a non-bit-field member, bail out.
|
|
conflict = true;
|
|
break;
|
|
}
|
|
|
|
if (conflict)
|
|
continue;
|
|
// Write the new bit-field access parameters.
|
|
// As the storage offset now is defined as the number of elements from the
|
|
// start of the structure, we should divide the Offset by the element size.
|
|
info.volatileStorageOffset =
|
|
storageOffset /
|
|
astContext.toCharUnitsFromBits(storageSize).getQuantity();
|
|
info.volatileStorageSize = storageSize;
|
|
info.volatileOffset = offset;
|
|
}
|
|
}
|
|
|
|
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())
|
|
members.push_back(MemberInfo(CharUnits::Zero(), MemberInfo::InfoKind::VFPtr,
|
|
getVFPtrType()));
|
|
|
|
if (astRecordLayout.hasOwnVBPtr())
|
|
cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(),
|
|
"accumulateVPtrs: hasOwnVBPtr");
|
|
}
|
|
|
|
mlir::Type CIRRecordLowering::getVFPtrType() {
|
|
return cir::VPtrType::get(builder.getContext());
|
|
}
|