| //===----------------------------------------------------------------------===// |
| // |
| // 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"); |
| } |