RecordLayoutBuilder.cpp [plain text]
#include "clang/AST/RecordLayout.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Attr.h"
#include "clang/AST/CXXInheritance.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/Expr.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Sema/SemaDiagnostic.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/MathExtras.h"
using namespace clang;
namespace {
struct BaseSubobjectInfo {
const CXXRecordDecl *Class;
bool IsVirtual;
SmallVector<BaseSubobjectInfo*, 4> Bases;
BaseSubobjectInfo *PrimaryVirtualBaseInfo;
const BaseSubobjectInfo *Derived;
};
struct ExternalLayout {
ExternalLayout() : Size(0), Align(0) {}
uint64_t Size;
uint64_t Align;
llvm::DenseMap<const FieldDecl *, uint64_t> FieldOffsets;
llvm::DenseMap<const CXXRecordDecl *, CharUnits> BaseOffsets;
llvm::DenseMap<const CXXRecordDecl *, CharUnits> VirtualBaseOffsets;
uint64_t getExternalFieldOffset(const FieldDecl *FD) {
assert(FieldOffsets.count(FD) &&
"Field does not have an external offset");
return FieldOffsets[FD];
}
bool getExternalNVBaseOffset(const CXXRecordDecl *RD, CharUnits &BaseOffset) {
auto Known = BaseOffsets.find(RD);
if (Known == BaseOffsets.end())
return false;
BaseOffset = Known->second;
return true;
}
bool getExternalVBaseOffset(const CXXRecordDecl *RD, CharUnits &BaseOffset) {
auto Known = VirtualBaseOffsets.find(RD);
if (Known == VirtualBaseOffsets.end())
return false;
BaseOffset = Known->second;
return true;
}
};
class EmptySubobjectMap {
const ASTContext &Context;
uint64_t CharWidth;
const CXXRecordDecl *Class;
typedef llvm::TinyPtrVector<const CXXRecordDecl *> ClassVectorTy;
typedef llvm::DenseMap<CharUnits, ClassVectorTy> EmptyClassOffsetsMapTy;
EmptyClassOffsetsMapTy EmptyClassOffsets;
CharUnits MaxEmptyClassOffset;
void ComputeEmptySubobjectSizes();
void AddSubobjectAtOffset(const CXXRecordDecl *RD, CharUnits Offset);
void UpdateEmptyBaseSubobjects(const BaseSubobjectInfo *Info,
CharUnits Offset, bool PlacingEmptyBase);
void UpdateEmptyFieldSubobjects(const CXXRecordDecl *RD,
const CXXRecordDecl *Class,
CharUnits Offset);
void UpdateEmptyFieldSubobjects(const FieldDecl *FD, CharUnits Offset);
bool AnyEmptySubobjectsBeyondOffset(CharUnits Offset) const {
return Offset <= MaxEmptyClassOffset;
}
CharUnits
getFieldOffset(const ASTRecordLayout &Layout, unsigned FieldNo) const {
uint64_t FieldOffset = Layout.getFieldOffset(FieldNo);
assert(FieldOffset % CharWidth == 0 &&
"Field offset not at char boundary!");
return Context.toCharUnitsFromBits(FieldOffset);
}
protected:
bool CanPlaceSubobjectAtOffset(const CXXRecordDecl *RD,
CharUnits Offset) const;
bool CanPlaceBaseSubobjectAtOffset(const BaseSubobjectInfo *Info,
CharUnits Offset);
bool CanPlaceFieldSubobjectAtOffset(const CXXRecordDecl *RD,
const CXXRecordDecl *Class,
CharUnits Offset) const;
bool CanPlaceFieldSubobjectAtOffset(const FieldDecl *FD,
CharUnits Offset) const;
public:
CharUnits SizeOfLargestEmptySubobject;
EmptySubobjectMap(const ASTContext &Context, const CXXRecordDecl *Class)
: Context(Context), CharWidth(Context.getCharWidth()), Class(Class) {
ComputeEmptySubobjectSizes();
}
bool CanPlaceBaseAtOffset(const BaseSubobjectInfo *Info,
CharUnits Offset);
bool CanPlaceFieldAtOffset(const FieldDecl *FD, CharUnits Offset);
};
void EmptySubobjectMap::ComputeEmptySubobjectSizes() {
for (const CXXBaseSpecifier &Base : Class->bases()) {
const CXXRecordDecl *BaseDecl = Base.getType()->getAsCXXRecordDecl();
CharUnits EmptySize;
const ASTRecordLayout &Layout = Context.getASTRecordLayout(BaseDecl);
if (BaseDecl->isEmpty()) {
EmptySize = Layout.getSize();
} else {
EmptySize = Layout.getSizeOfLargestEmptySubobject();
}
if (EmptySize > SizeOfLargestEmptySubobject)
SizeOfLargestEmptySubobject = EmptySize;
}
for (const FieldDecl *FD : Class->fields()) {
const RecordType *RT =
Context.getBaseElementType(FD->getType())->getAs<RecordType>();
if (!RT)
continue;
CharUnits EmptySize;
const CXXRecordDecl *MemberDecl = RT->getAsCXXRecordDecl();
const ASTRecordLayout &Layout = Context.getASTRecordLayout(MemberDecl);
if (MemberDecl->isEmpty()) {
EmptySize = Layout.getSize();
} else {
EmptySize = Layout.getSizeOfLargestEmptySubobject();
}
if (EmptySize > SizeOfLargestEmptySubobject)
SizeOfLargestEmptySubobject = EmptySize;
}
}
bool
EmptySubobjectMap::CanPlaceSubobjectAtOffset(const CXXRecordDecl *RD,
CharUnits Offset) const {
if (!RD->isEmpty())
return true;
EmptyClassOffsetsMapTy::const_iterator I = EmptyClassOffsets.find(Offset);
if (I == EmptyClassOffsets.end())
return true;
const ClassVectorTy &Classes = I->second;
if (std::find(Classes.begin(), Classes.end(), RD) == Classes.end())
return true;
return false;
}
void EmptySubobjectMap::AddSubobjectAtOffset(const CXXRecordDecl *RD,
CharUnits Offset) {
if (!RD->isEmpty())
return;
ClassVectorTy &Classes = EmptyClassOffsets[Offset];
if (std::find(Classes.begin(), Classes.end(), RD) != Classes.end())
return;
Classes.push_back(RD);
if (Offset > MaxEmptyClassOffset)
MaxEmptyClassOffset = Offset;
}
bool
EmptySubobjectMap::CanPlaceBaseSubobjectAtOffset(const BaseSubobjectInfo *Info,
CharUnits Offset) {
if (!AnyEmptySubobjectsBeyondOffset(Offset))
return true;
if (!CanPlaceSubobjectAtOffset(Info->Class, Offset))
return false;
const ASTRecordLayout &Layout = Context.getASTRecordLayout(Info->Class);
for (const BaseSubobjectInfo *Base : Info->Bases) {
if (Base->IsVirtual)
continue;
CharUnits BaseOffset = Offset + Layout.getBaseClassOffset(Base->Class);
if (!CanPlaceBaseSubobjectAtOffset(Base, BaseOffset))
return false;
}
if (Info->PrimaryVirtualBaseInfo) {
BaseSubobjectInfo *PrimaryVirtualBaseInfo = Info->PrimaryVirtualBaseInfo;
if (Info == PrimaryVirtualBaseInfo->Derived) {
if (!CanPlaceBaseSubobjectAtOffset(PrimaryVirtualBaseInfo, Offset))
return false;
}
}
unsigned FieldNo = 0;
for (CXXRecordDecl::field_iterator I = Info->Class->field_begin(),
E = Info->Class->field_end(); I != E; ++I, ++FieldNo) {
if (I->isBitField())
continue;
CharUnits FieldOffset = Offset + getFieldOffset(Layout, FieldNo);
if (!CanPlaceFieldSubobjectAtOffset(*I, FieldOffset))
return false;
}
return true;
}
void EmptySubobjectMap::UpdateEmptyBaseSubobjects(const BaseSubobjectInfo *Info,
CharUnits Offset,
bool PlacingEmptyBase) {
if (!PlacingEmptyBase && Offset >= SizeOfLargestEmptySubobject) {
return;
}
AddSubobjectAtOffset(Info->Class, Offset);
const ASTRecordLayout &Layout = Context.getASTRecordLayout(Info->Class);
for (const BaseSubobjectInfo *Base : Info->Bases) {
if (Base->IsVirtual)
continue;
CharUnits BaseOffset = Offset + Layout.getBaseClassOffset(Base->Class);
UpdateEmptyBaseSubobjects(Base, BaseOffset, PlacingEmptyBase);
}
if (Info->PrimaryVirtualBaseInfo) {
BaseSubobjectInfo *PrimaryVirtualBaseInfo = Info->PrimaryVirtualBaseInfo;
if (Info == PrimaryVirtualBaseInfo->Derived)
UpdateEmptyBaseSubobjects(PrimaryVirtualBaseInfo, Offset,
PlacingEmptyBase);
}
unsigned FieldNo = 0;
for (CXXRecordDecl::field_iterator I = Info->Class->field_begin(),
E = Info->Class->field_end(); I != E; ++I, ++FieldNo) {
if (I->isBitField())
continue;
CharUnits FieldOffset = Offset + getFieldOffset(Layout, FieldNo);
UpdateEmptyFieldSubobjects(*I, FieldOffset);
}
}
bool EmptySubobjectMap::CanPlaceBaseAtOffset(const BaseSubobjectInfo *Info,
CharUnits Offset) {
if (SizeOfLargestEmptySubobject.isZero())
return true;
if (!CanPlaceBaseSubobjectAtOffset(Info, Offset))
return false;
UpdateEmptyBaseSubobjects(Info, Offset, Info->Class->isEmpty());
return true;
}
bool
EmptySubobjectMap::CanPlaceFieldSubobjectAtOffset(const CXXRecordDecl *RD,
const CXXRecordDecl *Class,
CharUnits Offset) const {
if (!AnyEmptySubobjectsBeyondOffset(Offset))
return true;
if (!CanPlaceSubobjectAtOffset(RD, Offset))
return false;
const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD);
for (const CXXBaseSpecifier &Base : RD->bases()) {
if (Base.isVirtual())
continue;
const CXXRecordDecl *BaseDecl = Base.getType()->getAsCXXRecordDecl();
CharUnits BaseOffset = Offset + Layout.getBaseClassOffset(BaseDecl);
if (!CanPlaceFieldSubobjectAtOffset(BaseDecl, Class, BaseOffset))
return false;
}
if (RD == Class) {
for (const CXXBaseSpecifier &Base : RD->vbases()) {
const CXXRecordDecl *VBaseDecl = Base.getType()->getAsCXXRecordDecl();
CharUnits VBaseOffset = Offset + Layout.getVBaseClassOffset(VBaseDecl);
if (!CanPlaceFieldSubobjectAtOffset(VBaseDecl, Class, VBaseOffset))
return false;
}
}
unsigned FieldNo = 0;
for (CXXRecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end();
I != E; ++I, ++FieldNo) {
if (I->isBitField())
continue;
CharUnits FieldOffset = Offset + getFieldOffset(Layout, FieldNo);
if (!CanPlaceFieldSubobjectAtOffset(*I, FieldOffset))
return false;
}
return true;
}
bool
EmptySubobjectMap::CanPlaceFieldSubobjectAtOffset(const FieldDecl *FD,
CharUnits Offset) const {
if (!AnyEmptySubobjectsBeyondOffset(Offset))
return true;
QualType T = FD->getType();
if (const CXXRecordDecl *RD = T->getAsCXXRecordDecl())
return CanPlaceFieldSubobjectAtOffset(RD, RD, Offset);
if (const ConstantArrayType *AT = Context.getAsConstantArrayType(T)) {
QualType ElemTy = Context.getBaseElementType(AT);
const RecordType *RT = ElemTy->getAs<RecordType>();
if (!RT)
return true;
const CXXRecordDecl *RD = RT->getAsCXXRecordDecl();
const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD);
uint64_t NumElements = Context.getConstantArrayElementCount(AT);
CharUnits ElementOffset = Offset;
for (uint64_t I = 0; I != NumElements; ++I) {
if (!AnyEmptySubobjectsBeyondOffset(ElementOffset))
return true;
if (!CanPlaceFieldSubobjectAtOffset(RD, RD, ElementOffset))
return false;
ElementOffset += Layout.getSize();
}
}
return true;
}
bool
EmptySubobjectMap::CanPlaceFieldAtOffset(const FieldDecl *FD,
CharUnits Offset) {
if (!CanPlaceFieldSubobjectAtOffset(FD, Offset))
return false;
UpdateEmptyFieldSubobjects(FD, Offset);
return true;
}
void EmptySubobjectMap::UpdateEmptyFieldSubobjects(const CXXRecordDecl *RD,
const CXXRecordDecl *Class,
CharUnits Offset) {
if (Offset >= SizeOfLargestEmptySubobject)
return;
AddSubobjectAtOffset(RD, Offset);
const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD);
for (const CXXBaseSpecifier &Base : RD->bases()) {
if (Base.isVirtual())
continue;
const CXXRecordDecl *BaseDecl = Base.getType()->getAsCXXRecordDecl();
CharUnits BaseOffset = Offset + Layout.getBaseClassOffset(BaseDecl);
UpdateEmptyFieldSubobjects(BaseDecl, Class, BaseOffset);
}
if (RD == Class) {
for (const CXXBaseSpecifier &Base : RD->vbases()) {
const CXXRecordDecl *VBaseDecl = Base.getType()->getAsCXXRecordDecl();
CharUnits VBaseOffset = Offset + Layout.getVBaseClassOffset(VBaseDecl);
UpdateEmptyFieldSubobjects(VBaseDecl, Class, VBaseOffset);
}
}
unsigned FieldNo = 0;
for (CXXRecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end();
I != E; ++I, ++FieldNo) {
if (I->isBitField())
continue;
CharUnits FieldOffset = Offset + getFieldOffset(Layout, FieldNo);
UpdateEmptyFieldSubobjects(*I, FieldOffset);
}
}
void EmptySubobjectMap::UpdateEmptyFieldSubobjects(const FieldDecl *FD,
CharUnits Offset) {
QualType T = FD->getType();
if (const CXXRecordDecl *RD = T->getAsCXXRecordDecl()) {
UpdateEmptyFieldSubobjects(RD, RD, Offset);
return;
}
if (const ConstantArrayType *AT = Context.getAsConstantArrayType(T)) {
QualType ElemTy = Context.getBaseElementType(AT);
const RecordType *RT = ElemTy->getAs<RecordType>();
if (!RT)
return;
const CXXRecordDecl *RD = RT->getAsCXXRecordDecl();
const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD);
uint64_t NumElements = Context.getConstantArrayElementCount(AT);
CharUnits ElementOffset = Offset;
for (uint64_t I = 0; I != NumElements; ++I) {
if (ElementOffset >= SizeOfLargestEmptySubobject)
return;
UpdateEmptyFieldSubobjects(RD, RD, ElementOffset);
ElementOffset += Layout.getSize();
}
}
}
typedef llvm::SmallPtrSet<const CXXRecordDecl*, 4> ClassSetTy;
class ItaniumRecordLayoutBuilder {
protected:
friend class clang::ASTContext;
const ASTContext &Context;
EmptySubobjectMap *EmptySubobjects;
uint64_t Size;
CharUnits Alignment;
CharUnits UnpackedAlignment;
SmallVector<uint64_t, 16> FieldOffsets;
unsigned UseExternalLayout : 1;
unsigned InferAlignment : 1;
unsigned Packed : 1;
unsigned IsUnion : 1;
unsigned IsMac68kAlign : 1;
unsigned IsMsStruct : 1;
unsigned char UnfilledBitsInLastUnit;
unsigned char LastBitfieldTypeSize;
CharUnits MaxFieldAlignment;
uint64_t DataSize;
CharUnits NonVirtualSize;
CharUnits NonVirtualAlignment;
const CXXRecordDecl *PrimaryBase;
bool PrimaryBaseIsVirtual;
bool HasOwnVFPtr;
typedef llvm::DenseMap<const CXXRecordDecl *, CharUnits> BaseOffsetsMapTy;
BaseOffsetsMapTy Bases;
ASTRecordLayout::VBaseOffsetsMapTy VBases;
CXXIndirectPrimaryBaseSet IndirectPrimaryBases;
const CXXRecordDecl *FirstNearlyEmptyVBase;
llvm::SmallPtrSet<const CXXRecordDecl *, 4> VisitedVirtualBases;
ExternalLayout External;
ItaniumRecordLayoutBuilder(const ASTContext &Context,
EmptySubobjectMap *EmptySubobjects)
: Context(Context), EmptySubobjects(EmptySubobjects), Size(0),
Alignment(CharUnits::One()), UnpackedAlignment(CharUnits::One()),
UseExternalLayout(false), InferAlignment(false), Packed(false),
IsUnion(false), IsMac68kAlign(false), IsMsStruct(false),
UnfilledBitsInLastUnit(0), LastBitfieldTypeSize(0),
MaxFieldAlignment(CharUnits::Zero()), DataSize(0),
NonVirtualSize(CharUnits::Zero()),
NonVirtualAlignment(CharUnits::One()), PrimaryBase(nullptr),
PrimaryBaseIsVirtual(false), HasOwnVFPtr(false),
FirstNearlyEmptyVBase(nullptr) {}
void Layout(const RecordDecl *D);
void Layout(const CXXRecordDecl *D);
void Layout(const ObjCInterfaceDecl *D);
void LayoutFields(const RecordDecl *D);
void LayoutField(const FieldDecl *D, bool InsertExtraPadding);
void LayoutWideBitField(uint64_t FieldSize, uint64_t TypeSize,
bool FieldPacked, const FieldDecl *D);
void LayoutBitField(const FieldDecl *D);
TargetCXXABI getCXXABI() const {
return Context.getTargetInfo().getCXXABI();
}
llvm::SpecificBumpPtrAllocator<BaseSubobjectInfo> BaseSubobjectInfoAllocator;
typedef llvm::DenseMap<const CXXRecordDecl *, BaseSubobjectInfo *>
BaseSubobjectInfoMapTy;
BaseSubobjectInfoMapTy VirtualBaseInfo;
BaseSubobjectInfoMapTy NonVirtualBaseInfo;
void ComputeBaseSubobjectInfo(const CXXRecordDecl *RD);
BaseSubobjectInfo *ComputeBaseSubobjectInfo(const CXXRecordDecl *RD,
bool IsVirtual,
BaseSubobjectInfo *Derived);
void DeterminePrimaryBase(const CXXRecordDecl *RD);
void SelectPrimaryVBase(const CXXRecordDecl *RD);
void EnsureVTablePointerAlignment(CharUnits UnpackedBaseAlign);
void LayoutNonVirtualBases(const CXXRecordDecl *RD);
void LayoutNonVirtualBase(const BaseSubobjectInfo *Base);
void AddPrimaryVirtualBaseOffsets(const BaseSubobjectInfo *Info,
CharUnits Offset);
void LayoutVirtualBases(const CXXRecordDecl *RD,
const CXXRecordDecl *MostDerivedClass);
void LayoutVirtualBase(const BaseSubobjectInfo *Base);
CharUnits LayoutBase(const BaseSubobjectInfo *Base);
void InitializeLayout(const Decl *D);
void FinishLayout(const NamedDecl *D);
void UpdateAlignment(CharUnits NewAlignment, CharUnits UnpackedNewAlignment);
void UpdateAlignment(CharUnits NewAlignment) {
UpdateAlignment(NewAlignment, NewAlignment);
}
uint64_t updateExternalFieldOffset(const FieldDecl *Field,
uint64_t ComputedOffset);
void CheckFieldPadding(uint64_t Offset, uint64_t UnpaddedOffset,
uint64_t UnpackedOffset, unsigned UnpackedAlign,
bool isPacked, const FieldDecl *D);
DiagnosticBuilder Diag(SourceLocation Loc, unsigned DiagID);
CharUnits getSize() const {
assert(Size % Context.getCharWidth() == 0);
return Context.toCharUnitsFromBits(Size);
}
uint64_t getSizeInBits() const { return Size; }
void setSize(CharUnits NewSize) { Size = Context.toBits(NewSize); }
void setSize(uint64_t NewSize) { Size = NewSize; }
CharUnits getAligment() const { return Alignment; }
CharUnits getDataSize() const {
assert(DataSize % Context.getCharWidth() == 0);
return Context.toCharUnitsFromBits(DataSize);
}
uint64_t getDataSizeInBits() const { return DataSize; }
void setDataSize(CharUnits NewSize) { DataSize = Context.toBits(NewSize); }
void setDataSize(uint64_t NewSize) { DataSize = NewSize; }
ItaniumRecordLayoutBuilder(const ItaniumRecordLayoutBuilder &) = delete;
void operator=(const ItaniumRecordLayoutBuilder &) = delete;
};
}
void ItaniumRecordLayoutBuilder::SelectPrimaryVBase(const CXXRecordDecl *RD) {
for (const auto &I : RD->bases()) {
assert(!I.getType()->isDependentType() &&
"Cannot layout class with dependent bases.");
const CXXRecordDecl *Base = I.getType()->getAsCXXRecordDecl();
if (I.isVirtual() && Context.isNearlyEmpty(Base)) {
if (!IndirectPrimaryBases.count(Base)) {
PrimaryBase = Base;
PrimaryBaseIsVirtual = true;
return;
}
if (!FirstNearlyEmptyVBase)
FirstNearlyEmptyVBase = Base;
}
SelectPrimaryVBase(Base);
if (PrimaryBase)
return;
}
}
void ItaniumRecordLayoutBuilder::DeterminePrimaryBase(const CXXRecordDecl *RD) {
if (!RD->isDynamicClass())
return;
RD->getIndirectPrimaryBases(IndirectPrimaryBases);
for (const auto &I : RD->bases()) {
if (I.isVirtual())
continue;
const CXXRecordDecl *Base = I.getType()->getAsCXXRecordDecl();
if (Base->isDynamicClass()) {
PrimaryBase = Base;
PrimaryBaseIsVirtual = false;
return;
}
}
if (RD->getNumVBases() != 0) {
SelectPrimaryVBase(RD);
if (PrimaryBase)
return;
}
if (FirstNearlyEmptyVBase) {
PrimaryBase = FirstNearlyEmptyVBase;
PrimaryBaseIsVirtual = true;
return;
}
assert(!PrimaryBase && "Should not get here with a primary base!");
}
BaseSubobjectInfo *ItaniumRecordLayoutBuilder::ComputeBaseSubobjectInfo(
const CXXRecordDecl *RD, bool IsVirtual, BaseSubobjectInfo *Derived) {
BaseSubobjectInfo *Info;
if (IsVirtual) {
BaseSubobjectInfo *&InfoSlot = VirtualBaseInfo[RD];
if (InfoSlot) {
assert(InfoSlot->Class == RD && "Wrong class for virtual base info!");
return InfoSlot;
}
InfoSlot = new (BaseSubobjectInfoAllocator.Allocate()) BaseSubobjectInfo;
Info = InfoSlot;
} else {
Info = new (BaseSubobjectInfoAllocator.Allocate()) BaseSubobjectInfo;
}
Info->Class = RD;
Info->IsVirtual = IsVirtual;
Info->Derived = nullptr;
Info->PrimaryVirtualBaseInfo = nullptr;
const CXXRecordDecl *PrimaryVirtualBase = nullptr;
BaseSubobjectInfo *PrimaryVirtualBaseInfo = nullptr;
if (RD->getNumVBases()) {
const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD);
if (Layout.isPrimaryBaseVirtual()) {
PrimaryVirtualBase = Layout.getPrimaryBase();
assert(PrimaryVirtualBase && "Didn't have a primary virtual base!");
PrimaryVirtualBaseInfo = VirtualBaseInfo.lookup(PrimaryVirtualBase);
if (PrimaryVirtualBaseInfo) {
if (PrimaryVirtualBaseInfo->Derived) {
PrimaryVirtualBase = nullptr;
} else {
Info->PrimaryVirtualBaseInfo = PrimaryVirtualBaseInfo;
PrimaryVirtualBaseInfo->Derived = Info;
}
}
}
}
for (const auto &I : RD->bases()) {
bool IsVirtual = I.isVirtual();
const CXXRecordDecl *BaseDecl = I.getType()->getAsCXXRecordDecl();
Info->Bases.push_back(ComputeBaseSubobjectInfo(BaseDecl, IsVirtual, Info));
}
if (PrimaryVirtualBase && !PrimaryVirtualBaseInfo) {
PrimaryVirtualBaseInfo = VirtualBaseInfo.lookup(PrimaryVirtualBase);
assert(PrimaryVirtualBaseInfo &&
"Did not create a primary virtual base!");
Info->PrimaryVirtualBaseInfo = PrimaryVirtualBaseInfo;
PrimaryVirtualBaseInfo->Derived = Info;
}
return Info;
}
void ItaniumRecordLayoutBuilder::ComputeBaseSubobjectInfo(
const CXXRecordDecl *RD) {
for (const auto &I : RD->bases()) {
bool IsVirtual = I.isVirtual();
const CXXRecordDecl *BaseDecl = I.getType()->getAsCXXRecordDecl();
BaseSubobjectInfo *Info = ComputeBaseSubobjectInfo(BaseDecl, IsVirtual,
nullptr);
if (IsVirtual) {
assert(VirtualBaseInfo.count(BaseDecl) &&
"Did not add virtual base!");
} else {
assert(!NonVirtualBaseInfo.count(BaseDecl) &&
"Non-virtual base already exists!");
NonVirtualBaseInfo.insert(std::make_pair(BaseDecl, Info));
}
}
}
void ItaniumRecordLayoutBuilder::EnsureVTablePointerAlignment(
CharUnits UnpackedBaseAlign) {
CharUnits BaseAlign = (Packed) ? CharUnits::One() : UnpackedBaseAlign;
if (!MaxFieldAlignment.isZero()) {
BaseAlign = std::min(BaseAlign, MaxFieldAlignment);
UnpackedBaseAlign = std::min(UnpackedBaseAlign, MaxFieldAlignment);
}
setSize(getSize().alignTo(BaseAlign));
setDataSize(getSize());
UpdateAlignment(BaseAlign, UnpackedBaseAlign);
}
void ItaniumRecordLayoutBuilder::LayoutNonVirtualBases(
const CXXRecordDecl *RD) {
DeterminePrimaryBase(RD);
ComputeBaseSubobjectInfo(RD);
if (PrimaryBase) {
if (PrimaryBaseIsVirtual) {
BaseSubobjectInfo *PrimaryBaseInfo = VirtualBaseInfo.lookup(PrimaryBase);
PrimaryBaseInfo->Derived = nullptr;
IndirectPrimaryBases.insert(PrimaryBase);
assert(!VisitedVirtualBases.count(PrimaryBase) &&
"vbase already visited!");
VisitedVirtualBases.insert(PrimaryBase);
LayoutVirtualBase(PrimaryBaseInfo);
} else {
BaseSubobjectInfo *PrimaryBaseInfo =
NonVirtualBaseInfo.lookup(PrimaryBase);
assert(PrimaryBaseInfo &&
"Did not find base info for non-virtual primary base!");
LayoutNonVirtualBase(PrimaryBaseInfo);
}
} else if (RD->isDynamicClass()) {
assert(DataSize == 0 && "Vtable pointer must be at offset zero!");
CharUnits PtrWidth =
Context.toCharUnitsFromBits(Context.getTargetInfo().getPointerWidth(0));
CharUnits PtrAlign =
Context.toCharUnitsFromBits(Context.getTargetInfo().getPointerAlign(0));
EnsureVTablePointerAlignment(PtrAlign);
HasOwnVFPtr = true;
setSize(getSize() + PtrWidth);
setDataSize(getSize());
}
for (const auto &I : RD->bases()) {
if (I.isVirtual())
continue;
const CXXRecordDecl *BaseDecl = I.getType()->getAsCXXRecordDecl();
if (BaseDecl == PrimaryBase && !PrimaryBaseIsVirtual)
continue;
BaseSubobjectInfo *BaseInfo = NonVirtualBaseInfo.lookup(BaseDecl);
assert(BaseInfo && "Did not find base info for non-virtual base!");
LayoutNonVirtualBase(BaseInfo);
}
}
void ItaniumRecordLayoutBuilder::LayoutNonVirtualBase(
const BaseSubobjectInfo *Base) {
CharUnits Offset = LayoutBase(Base);
assert(!Bases.count(Base->Class) && "base offset already exists!");
Bases.insert(std::make_pair(Base->Class, Offset));
AddPrimaryVirtualBaseOffsets(Base, Offset);
}
void ItaniumRecordLayoutBuilder::AddPrimaryVirtualBaseOffsets(
const BaseSubobjectInfo *Info, CharUnits Offset) {
if (!Info->Class->getNumVBases())
return;
if (Info->PrimaryVirtualBaseInfo) {
assert(Info->PrimaryVirtualBaseInfo->IsVirtual &&
"Primary virtual base is not virtual!");
if (Info->PrimaryVirtualBaseInfo->Derived == Info) {
assert(!VBases.count(Info->PrimaryVirtualBaseInfo->Class) &&
"primary vbase offset already exists!");
VBases.insert(std::make_pair(Info->PrimaryVirtualBaseInfo->Class,
ASTRecordLayout::VBaseInfo(Offset, false)));
AddPrimaryVirtualBaseOffsets(Info->PrimaryVirtualBaseInfo, Offset);
}
}
const ASTRecordLayout &Layout = Context.getASTRecordLayout(Info->Class);
for (const BaseSubobjectInfo *Base : Info->Bases) {
if (Base->IsVirtual)
continue;
CharUnits BaseOffset = Offset + Layout.getBaseClassOffset(Base->Class);
AddPrimaryVirtualBaseOffsets(Base, BaseOffset);
}
}
void ItaniumRecordLayoutBuilder::LayoutVirtualBases(
const CXXRecordDecl *RD, const CXXRecordDecl *MostDerivedClass) {
const CXXRecordDecl *PrimaryBase;
bool PrimaryBaseIsVirtual;
if (MostDerivedClass == RD) {
PrimaryBase = this->PrimaryBase;
PrimaryBaseIsVirtual = this->PrimaryBaseIsVirtual;
} else {
const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD);
PrimaryBase = Layout.getPrimaryBase();
PrimaryBaseIsVirtual = Layout.isPrimaryBaseVirtual();
}
for (const CXXBaseSpecifier &Base : RD->bases()) {
assert(!Base.getType()->isDependentType() &&
"Cannot layout class with dependent bases.");
const CXXRecordDecl *BaseDecl = Base.getType()->getAsCXXRecordDecl();
if (Base.isVirtual()) {
if (PrimaryBase != BaseDecl || !PrimaryBaseIsVirtual) {
bool IndirectPrimaryBase = IndirectPrimaryBases.count(BaseDecl);
if (!IndirectPrimaryBase) {
if (!VisitedVirtualBases.insert(BaseDecl).second)
continue;
const BaseSubobjectInfo *BaseInfo = VirtualBaseInfo.lookup(BaseDecl);
assert(BaseInfo && "Did not find virtual base info!");
LayoutVirtualBase(BaseInfo);
}
}
}
if (!BaseDecl->getNumVBases()) {
continue;
}
LayoutVirtualBases(BaseDecl, MostDerivedClass);
}
}
void ItaniumRecordLayoutBuilder::LayoutVirtualBase(
const BaseSubobjectInfo *Base) {
assert(!Base->Derived && "Trying to lay out a primary virtual base!");
CharUnits Offset = LayoutBase(Base);
assert(!VBases.count(Base->Class) && "vbase offset already exists!");
VBases.insert(std::make_pair(Base->Class,
ASTRecordLayout::VBaseInfo(Offset, false)));
AddPrimaryVirtualBaseOffsets(Base, Offset);
}
CharUnits
ItaniumRecordLayoutBuilder::LayoutBase(const BaseSubobjectInfo *Base) {
const ASTRecordLayout &Layout = Context.getASTRecordLayout(Base->Class);
CharUnits Offset;
bool HasExternalLayout = false;
if (UseExternalLayout) {
llvm::DenseMap<const CXXRecordDecl *, CharUnits>::iterator Known;
if (Base->IsVirtual)
HasExternalLayout = External.getExternalNVBaseOffset(Base->Class, Offset);
else
HasExternalLayout = External.getExternalVBaseOffset(Base->Class, Offset);
}
CharUnits UnpackedBaseAlign = Layout.getNonVirtualAlignment();
CharUnits BaseAlign = (Packed) ? CharUnits::One() : UnpackedBaseAlign;
if (Base->Class->isEmpty() &&
(!HasExternalLayout || Offset == CharUnits::Zero()) &&
EmptySubobjects->CanPlaceBaseAtOffset(Base, CharUnits::Zero())) {
setSize(std::max(getSize(), Layout.getSize()));
UpdateAlignment(BaseAlign, UnpackedBaseAlign);
return CharUnits::Zero();
}
if (!MaxFieldAlignment.isZero()) {
BaseAlign = std::min(BaseAlign, MaxFieldAlignment);
UnpackedBaseAlign = std::min(UnpackedBaseAlign, MaxFieldAlignment);
}
if (!HasExternalLayout) {
Offset = getDataSize().alignTo(BaseAlign);
while (!EmptySubobjects->CanPlaceBaseAtOffset(Base, Offset))
Offset += BaseAlign;
} else {
bool Allowed = EmptySubobjects->CanPlaceBaseAtOffset(Base, Offset);
(void)Allowed;
assert(Allowed && "Base subobject externally placed at overlapping offset");
if (InferAlignment && Offset < getDataSize().alignTo(BaseAlign)) {
Alignment = CharUnits::One();
InferAlignment = false;
}
}
if (!Base->Class->isEmpty()) {
setDataSize(Offset + Layout.getNonVirtualSize());
setSize(std::max(getSize(), getDataSize()));
} else
setSize(std::max(getSize(), Offset + Layout.getSize()));
UpdateAlignment(BaseAlign, UnpackedBaseAlign);
return Offset;
}
void ItaniumRecordLayoutBuilder::InitializeLayout(const Decl *D) {
if (const RecordDecl *RD = dyn_cast<RecordDecl>(D)) {
IsUnion = RD->isUnion();
IsMsStruct = RD->isMsStruct(Context);
}
Packed = D->hasAttr<PackedAttr>();
if (unsigned DefaultMaxFieldAlignment = Context.getLangOpts().PackStruct) {
MaxFieldAlignment = CharUnits::fromQuantity(DefaultMaxFieldAlignment);
}
if (D->hasAttr<AlignMac68kAttr>()) {
IsMac68kAlign = true;
MaxFieldAlignment = CharUnits::fromQuantity(2);
Alignment = CharUnits::fromQuantity(2);
} else {
if (const MaxFieldAlignmentAttr *MFAA = D->getAttr<MaxFieldAlignmentAttr>())
MaxFieldAlignment = Context.toCharUnitsFromBits(MFAA->getAlignment());
if (unsigned MaxAlign = D->getMaxAlignment())
UpdateAlignment(Context.toCharUnitsFromBits(MaxAlign));
}
if (const RecordDecl *RD = dyn_cast<RecordDecl>(D))
if (ExternalASTSource *Source = Context.getExternalSource()) {
UseExternalLayout = Source->layoutRecordType(
RD, External.Size, External.Align, External.FieldOffsets,
External.BaseOffsets, External.VirtualBaseOffsets);
if (UseExternalLayout) {
if (External.Align > 0) {
Alignment = Context.toCharUnitsFromBits(External.Align);
} else {
InferAlignment = true;
}
}
}
}
void ItaniumRecordLayoutBuilder::Layout(const RecordDecl *D) {
InitializeLayout(D);
LayoutFields(D);
FinishLayout(D);
}
void ItaniumRecordLayoutBuilder::Layout(const CXXRecordDecl *RD) {
InitializeLayout(RD);
LayoutNonVirtualBases(RD);
LayoutFields(RD);
NonVirtualSize = Context.toCharUnitsFromBits(
llvm::alignTo(getSizeInBits(), Context.getTargetInfo().getCharAlign()));
NonVirtualAlignment = Alignment;
LayoutVirtualBases(RD, RD);
FinishLayout(RD);
#ifndef NDEBUG
for (const CXXBaseSpecifier &Base : RD->bases()) {
if (Base.isVirtual())
continue;
const CXXRecordDecl *BaseDecl = Base.getType()->getAsCXXRecordDecl();
assert(Bases.count(BaseDecl) && "Did not find base offset!");
}
for (const CXXBaseSpecifier &Base : RD->vbases()) {
const CXXRecordDecl *BaseDecl = Base.getType()->getAsCXXRecordDecl();
assert(VBases.count(BaseDecl) && "Did not find base offset!");
}
#endif
}
void ItaniumRecordLayoutBuilder::Layout(const ObjCInterfaceDecl *D) {
if (ObjCInterfaceDecl *SD = D->getSuperClass()) {
const ASTRecordLayout &SL = Context.getASTObjCInterfaceLayout(SD);
UpdateAlignment(SL.getAlignment());
setSize(SL.getDataSize());
setDataSize(getSize());
}
InitializeLayout(D);
for (const ObjCIvarDecl *IVD = D->all_declared_ivar_begin(); IVD;
IVD = IVD->getNextIvar())
LayoutField(IVD, false);
FinishLayout(D);
}
void ItaniumRecordLayoutBuilder::LayoutFields(const RecordDecl *D) {
bool InsertExtraPadding = D->mayInsertExtraPadding(true);
bool HasFlexibleArrayMember = D->hasFlexibleArrayMember();
for (auto I = D->field_begin(), End = D->field_end(); I != End; ++I) {
auto Next(I);
++Next;
LayoutField(*I,
InsertExtraPadding && (Next != End || !HasFlexibleArrayMember));
}
}
static uint64_t
roundUpSizeToCharAlignment(uint64_t Size,
const ASTContext &Context) {
uint64_t CharAlignment = Context.getTargetInfo().getCharAlign();
return llvm::alignTo(Size, CharAlignment);
}
void ItaniumRecordLayoutBuilder::LayoutWideBitField(uint64_t FieldSize,
uint64_t TypeSize,
bool FieldPacked,
const FieldDecl *D) {
assert(Context.getLangOpts().CPlusPlus &&
"Can only have wide bit-fields in C++!");
QualType IntegralPODTypes[] = {
Context.UnsignedCharTy, Context.UnsignedShortTy, Context.UnsignedIntTy,
Context.UnsignedLongTy, Context.UnsignedLongLongTy
};
QualType Type;
for (const QualType &QT : IntegralPODTypes) {
uint64_t Size = Context.getTypeSize(QT);
if (Size > FieldSize)
break;
Type = QT;
}
assert(!Type.isNull() && "Did not find a type!");
CharUnits TypeAlign = Context.getTypeAlignInChars(Type);
UnfilledBitsInLastUnit = 0;
LastBitfieldTypeSize = 0;
uint64_t FieldOffset;
uint64_t UnpaddedFieldOffset = getDataSizeInBits() - UnfilledBitsInLastUnit;
if (IsUnion) {
uint64_t RoundedFieldSize = roundUpSizeToCharAlignment(FieldSize,
Context);
setDataSize(std::max(getDataSizeInBits(), RoundedFieldSize));
FieldOffset = 0;
} else {
FieldOffset = llvm::alignTo(getDataSizeInBits(), Context.toBits(TypeAlign));
uint64_t NewSizeInBits = FieldOffset + FieldSize;
setDataSize(
llvm::alignTo(NewSizeInBits, Context.getTargetInfo().getCharAlign()));
UnfilledBitsInLastUnit = getDataSizeInBits() - NewSizeInBits;
}
FieldOffsets.push_back(FieldOffset);
CheckFieldPadding(FieldOffset, UnpaddedFieldOffset, FieldOffset,
Context.toBits(TypeAlign), FieldPacked, D);
setSize(std::max(getSizeInBits(), getDataSizeInBits()));
UpdateAlignment(TypeAlign);
}
void ItaniumRecordLayoutBuilder::LayoutBitField(const FieldDecl *D) {
bool FieldPacked = Packed || D->hasAttr<PackedAttr>();
uint64_t FieldSize = D->getBitWidthValue(Context);
TypeInfo FieldInfo = Context.getTypeInfo(D->getType());
uint64_t TypeSize = FieldInfo.Width;
unsigned FieldAlign = FieldInfo.Align;
if (IsMsStruct) {
FieldAlign = TypeSize;
if (LastBitfieldTypeSize != TypeSize) {
if (!LastBitfieldTypeSize && !FieldSize)
FieldAlign = 1;
UnfilledBitsInLastUnit = 0;
LastBitfieldTypeSize = 0;
}
}
if (FieldSize > TypeSize) {
LayoutWideBitField(FieldSize, TypeSize, FieldPacked, D);
return;
}
uint64_t FieldOffset =
IsUnion ? 0 : (getDataSizeInBits() - UnfilledBitsInLastUnit);
if (!IsMsStruct && !Context.getTargetInfo().useBitFieldTypeAlignment()) {
if (FieldSize == 0 &&
Context.getTargetInfo().useZeroLengthBitfieldAlignment()) {
unsigned ZeroLengthBitfieldBoundary =
Context.getTargetInfo().getZeroLengthBitfieldBoundary();
FieldAlign = std::max(FieldAlign, ZeroLengthBitfieldBoundary);
} else {
FieldAlign = 1;
}
}
unsigned UnpackedFieldAlign = FieldAlign;
if (!IsMsStruct && FieldPacked && FieldSize != 0)
FieldAlign = 1;
unsigned ExplicitFieldAlign = D->getMaxAlignment();
if (ExplicitFieldAlign) {
FieldAlign = std::max(FieldAlign, ExplicitFieldAlign);
UnpackedFieldAlign = std::max(UnpackedFieldAlign, ExplicitFieldAlign);
}
if (!MaxFieldAlignment.isZero() && FieldSize) {
unsigned MaxFieldAlignmentInBits = Context.toBits(MaxFieldAlignment);
FieldAlign = std::min(FieldAlign, MaxFieldAlignmentInBits);
UnpackedFieldAlign = std::min(UnpackedFieldAlign, MaxFieldAlignmentInBits);
}
if (IsMsStruct && IsUnion) {
FieldAlign = UnpackedFieldAlign = 1;
}
uint64_t UnpaddedFieldOffset = FieldOffset;
uint64_t UnpackedFieldOffset = FieldOffset;
if (IsMsStruct) {
if (FieldSize == 0 || FieldSize > UnfilledBitsInLastUnit) {
FieldOffset = llvm::alignTo(FieldOffset, FieldAlign);
UnpackedFieldOffset =
llvm::alignTo(UnpackedFieldOffset, UnpackedFieldAlign);
UnfilledBitsInLastUnit = 0;
}
} else {
bool AllowPadding = MaxFieldAlignment.isZero();
if (FieldSize == 0 ||
(AllowPadding &&
(FieldOffset & (FieldAlign-1)) + FieldSize > TypeSize)) {
FieldOffset = llvm::alignTo(FieldOffset, FieldAlign);
} else if (ExplicitFieldAlign) {
FieldOffset = llvm::alignTo(FieldOffset, ExplicitFieldAlign);
}
if (FieldSize == 0 ||
(AllowPadding &&
(UnpackedFieldOffset & (UnpackedFieldAlign-1)) + FieldSize > TypeSize))
UnpackedFieldOffset =
llvm::alignTo(UnpackedFieldOffset, UnpackedFieldAlign);
else if (ExplicitFieldAlign)
UnpackedFieldOffset =
llvm::alignTo(UnpackedFieldOffset, ExplicitFieldAlign);
}
if (UseExternalLayout)
FieldOffset = updateExternalFieldOffset(D, FieldOffset);
FieldOffsets.push_back(FieldOffset);
if (!IsMsStruct &&
!Context.getTargetInfo().useZeroLengthBitfieldAlignment() &&
!D->getIdentifier())
FieldAlign = UnpackedFieldAlign = 1;
if (!UseExternalLayout)
CheckFieldPadding(FieldOffset, UnpaddedFieldOffset, UnpackedFieldOffset,
UnpackedFieldAlign, FieldPacked, D);
if (IsUnion) {
uint64_t RoundedFieldSize;
if (IsMsStruct) {
RoundedFieldSize =
(FieldSize ? TypeSize : Context.getTargetInfo().getCharWidth());
} else {
RoundedFieldSize = roundUpSizeToCharAlignment(FieldSize, Context);
}
setDataSize(std::max(getDataSizeInBits(), RoundedFieldSize));
} else if (IsMsStruct && FieldSize) {
if (!UnfilledBitsInLastUnit) {
setDataSize(FieldOffset + TypeSize);
UnfilledBitsInLastUnit = TypeSize;
}
UnfilledBitsInLastUnit -= FieldSize;
LastBitfieldTypeSize = TypeSize;
} else {
uint64_t NewSizeInBits = FieldOffset + FieldSize;
uint64_t CharAlignment = Context.getTargetInfo().getCharAlign();
setDataSize(llvm::alignTo(NewSizeInBits, CharAlignment));
UnfilledBitsInLastUnit = getDataSizeInBits() - NewSizeInBits;
LastBitfieldTypeSize = 0;
}
setSize(std::max(getSizeInBits(), getDataSizeInBits()));
UpdateAlignment(Context.toCharUnitsFromBits(FieldAlign),
Context.toCharUnitsFromBits(UnpackedFieldAlign));
}
void ItaniumRecordLayoutBuilder::LayoutField(const FieldDecl *D,
bool InsertExtraPadding) {
if (D->isBitField()) {
LayoutBitField(D);
return;
}
uint64_t UnpaddedFieldOffset = getDataSizeInBits() - UnfilledBitsInLastUnit;
UnfilledBitsInLastUnit = 0;
LastBitfieldTypeSize = 0;
bool FieldPacked = Packed || D->hasAttr<PackedAttr>();
CharUnits FieldOffset =
IsUnion ? CharUnits::Zero() : getDataSize();
CharUnits FieldSize;
CharUnits FieldAlign;
if (D->getType()->isIncompleteArrayType()) {
FieldSize = CharUnits::Zero();
const ArrayType* ATy = Context.getAsArrayType(D->getType());
FieldAlign = Context.getTypeAlignInChars(ATy->getElementType());
} else if (const ReferenceType *RT = D->getType()->getAs<ReferenceType>()) {
unsigned AS = RT->getPointeeType().getAddressSpace();
FieldSize =
Context.toCharUnitsFromBits(Context.getTargetInfo().getPointerWidth(AS));
FieldAlign =
Context.toCharUnitsFromBits(Context.getTargetInfo().getPointerAlign(AS));
} else {
std::pair<CharUnits, CharUnits> FieldInfo =
Context.getTypeInfoInChars(D->getType());
FieldSize = FieldInfo.first;
FieldAlign = FieldInfo.second;
if (IsMsStruct) {
QualType T = Context.getBaseElementType(D->getType());
if (const BuiltinType *BTy = T->getAs<BuiltinType>()) {
CharUnits TypeSize = Context.getTypeSizeInChars(BTy);
if (TypeSize > FieldAlign)
FieldAlign = TypeSize;
}
}
}
CharUnits UnpackedFieldAlign = FieldAlign;
CharUnits UnpackedFieldOffset = FieldOffset;
if (FieldPacked)
FieldAlign = CharUnits::One();
CharUnits MaxAlignmentInChars =
Context.toCharUnitsFromBits(D->getMaxAlignment());
FieldAlign = std::max(FieldAlign, MaxAlignmentInChars);
UnpackedFieldAlign = std::max(UnpackedFieldAlign, MaxAlignmentInChars);
if (!MaxFieldAlignment.isZero()) {
FieldAlign = std::min(FieldAlign, MaxFieldAlignment);
UnpackedFieldAlign = std::min(UnpackedFieldAlign, MaxFieldAlignment);
}
FieldOffset = FieldOffset.alignTo(FieldAlign);
UnpackedFieldOffset = UnpackedFieldOffset.alignTo(UnpackedFieldAlign);
if (UseExternalLayout) {
FieldOffset = Context.toCharUnitsFromBits(
updateExternalFieldOffset(D, Context.toBits(FieldOffset)));
if (!IsUnion && EmptySubobjects) {
bool Allowed = EmptySubobjects->CanPlaceFieldAtOffset(D, FieldOffset);
(void)Allowed;
assert(Allowed && "Externally-placed field cannot be placed here");
}
} else {
if (!IsUnion && EmptySubobjects) {
while (!EmptySubobjects->CanPlaceFieldAtOffset(D, FieldOffset)) {
FieldOffset += FieldAlign;
}
}
}
FieldOffsets.push_back(Context.toBits(FieldOffset));
if (!UseExternalLayout)
CheckFieldPadding(Context.toBits(FieldOffset), UnpaddedFieldOffset,
Context.toBits(UnpackedFieldOffset),
Context.toBits(UnpackedFieldAlign), FieldPacked, D);
if (InsertExtraPadding) {
CharUnits ASanAlignment = CharUnits::fromQuantity(8);
CharUnits ExtraSizeForAsan = ASanAlignment;
if (FieldSize % ASanAlignment)
ExtraSizeForAsan +=
ASanAlignment - CharUnits::fromQuantity(FieldSize % ASanAlignment);
FieldSize += ExtraSizeForAsan;
}
uint64_t FieldSizeInBits = Context.toBits(FieldSize);
if (IsUnion)
setDataSize(std::max(getDataSizeInBits(), FieldSizeInBits));
else
setDataSize(FieldOffset + FieldSize);
setSize(std::max(getSizeInBits(), getDataSizeInBits()));
UpdateAlignment(FieldAlign, UnpackedFieldAlign);
}
void ItaniumRecordLayoutBuilder::FinishLayout(const NamedDecl *D) {
if (Context.getLangOpts().CPlusPlus && getSizeInBits() == 0) {
if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D)) {
if (RD->isEmpty())
setSize(CharUnits::One());
}
else
setSize(CharUnits::One());
}
uint64_t UnpaddedSize = getSizeInBits() - UnfilledBitsInLastUnit;
uint64_t UnpackedSizeInBits =
llvm::alignTo(getSizeInBits(), Context.toBits(UnpackedAlignment));
CharUnits UnpackedSize = Context.toCharUnitsFromBits(UnpackedSizeInBits);
uint64_t RoundedSize =
llvm::alignTo(getSizeInBits(), Context.toBits(Alignment));
if (UseExternalLayout) {
if (InferAlignment && External.Size < RoundedSize) {
Alignment = CharUnits::One();
InferAlignment = false;
}
setSize(External.Size);
return;
}
setSize(RoundedSize);
unsigned CharBitNum = Context.getTargetInfo().getCharWidth();
if (const RecordDecl *RD = dyn_cast<RecordDecl>(D)) {
if (getSizeInBits() > UnpaddedSize) {
unsigned PadSize = getSizeInBits() - UnpaddedSize;
bool InBits = true;
if (PadSize % CharBitNum == 0) {
PadSize = PadSize / CharBitNum;
InBits = false;
}
Diag(RD->getLocation(), diag::warn_padded_struct_size)
<< Context.getTypeDeclType(RD)
<< PadSize
<< (InBits ? 1 : 0); }
if (Packed && UnpackedAlignment > CharUnits::One() &&
getSize() == UnpackedSize)
Diag(D->getLocation(), diag::warn_unnecessary_packed)
<< Context.getTypeDeclType(RD);
}
}
void ItaniumRecordLayoutBuilder::UpdateAlignment(
CharUnits NewAlignment, CharUnits UnpackedNewAlignment) {
if (IsMac68kAlign || (UseExternalLayout && !InferAlignment))
return;
if (NewAlignment > Alignment) {
assert(llvm::isPowerOf2_64(NewAlignment.getQuantity()) &&
"Alignment not a power of 2");
Alignment = NewAlignment;
}
if (UnpackedNewAlignment > UnpackedAlignment) {
assert(llvm::isPowerOf2_64(UnpackedNewAlignment.getQuantity()) &&
"Alignment not a power of 2");
UnpackedAlignment = UnpackedNewAlignment;
}
}
uint64_t
ItaniumRecordLayoutBuilder::updateExternalFieldOffset(const FieldDecl *Field,
uint64_t ComputedOffset) {
uint64_t ExternalFieldOffset = External.getExternalFieldOffset(Field);
if (InferAlignment && ExternalFieldOffset < ComputedOffset) {
Alignment = CharUnits::One();
InferAlignment = false;
}
return ExternalFieldOffset;
}
static unsigned getPaddingDiagFromTagKind(TagTypeKind Tag) {
switch (Tag) {
case TTK_Struct: return 0;
case TTK_Interface: return 1;
case TTK_Class: return 2;
default: llvm_unreachable("Invalid tag kind for field padding diagnostic!");
}
}
void ItaniumRecordLayoutBuilder::CheckFieldPadding(
uint64_t Offset, uint64_t UnpaddedOffset, uint64_t UnpackedOffset,
unsigned UnpackedAlign, bool isPacked, const FieldDecl *D) {
if (isa<ObjCIvarDecl>(D))
return;
if (D->getLocation().isInvalid())
return;
unsigned CharBitNum = Context.getTargetInfo().getCharWidth();
if (!IsUnion && Offset > UnpaddedOffset) {
unsigned PadSize = Offset - UnpaddedOffset;
bool InBits = true;
if (PadSize % CharBitNum == 0) {
PadSize = PadSize / CharBitNum;
InBits = false;
}
if (D->getIdentifier())
Diag(D->getLocation(), diag::warn_padded_struct_field)
<< getPaddingDiagFromTagKind(D->getParent()->getTagKind())
<< Context.getTypeDeclType(D->getParent())
<< PadSize
<< (InBits ? 1 : 0) << D->getIdentifier();
else
Diag(D->getLocation(), diag::warn_padded_struct_anon_field)
<< getPaddingDiagFromTagKind(D->getParent()->getTagKind())
<< Context.getTypeDeclType(D->getParent())
<< PadSize
<< (InBits ? 1 : 0); }
if (isPacked && UnpackedAlign > CharBitNum && Offset == UnpackedOffset)
Diag(D->getLocation(), diag::warn_unnecessary_packed)
<< D->getIdentifier();
}
static const CXXMethodDecl *computeKeyFunction(ASTContext &Context,
const CXXRecordDecl *RD) {
if (!RD->isPolymorphic())
return nullptr;
if (!RD->isExternallyVisible())
return nullptr;
TemplateSpecializationKind TSK = RD->getTemplateSpecializationKind();
if (TSK == TSK_ImplicitInstantiation ||
TSK == TSK_ExplicitInstantiationDeclaration ||
TSK == TSK_ExplicitInstantiationDefinition)
return nullptr;
bool allowInlineFunctions =
Context.getTargetInfo().getCXXABI().canKeyFunctionBeInline();
for (const CXXMethodDecl *MD : RD->methods()) {
if (!MD->isVirtual())
continue;
if (MD->isPure())
continue;
if (MD->isImplicit())
continue;
if (MD->isInlineSpecified())
continue;
if (MD->hasInlineBody())
continue;
if (!MD->isUserProvided())
continue;
if (!allowInlineFunctions) {
const FunctionDecl *Def;
if (MD->hasBody(Def) && Def->isInlineSpecified())
continue;
}
if (Context.getLangOpts().CUDA) {
if (Context.getLangOpts().CUDAIsDevice) {
if (!MD->hasAttr<CUDADeviceAttr>())
continue;
} else {
if (!MD->hasAttr<CUDAHostAttr>() && MD->hasAttr<CUDADeviceAttr>())
continue;
}
}
if (MD->hasAttr<DLLImportAttr>() && !RD->hasAttr<DLLImportAttr>())
return nullptr;
return MD;
}
return nullptr;
}
DiagnosticBuilder ItaniumRecordLayoutBuilder::Diag(SourceLocation Loc,
unsigned DiagID) {
return Context.getDiagnostics().Report(Loc, DiagID);
}
static bool mustSkipTailPadding(TargetCXXABI ABI, const CXXRecordDecl *RD) {
switch (ABI.getTailPaddingUseRules()) {
case TargetCXXABI::AlwaysUseTailPadding:
return false;
case TargetCXXABI::UseTailPaddingUnlessPOD03:
return RD->isPOD();
case TargetCXXABI::UseTailPaddingUnlessPOD11:
return RD->isTrivial() && RD->isStandardLayout();
}
llvm_unreachable("bad tail-padding use kind");
}
static bool isMsLayout(const ASTContext &Context) {
return Context.getTargetInfo().getCXXABI().isMicrosoft();
}
namespace {
struct MicrosoftRecordLayoutBuilder {
struct ElementInfo {
CharUnits Size;
CharUnits Alignment;
};
typedef llvm::DenseMap<const CXXRecordDecl *, CharUnits> BaseOffsetsMapTy;
MicrosoftRecordLayoutBuilder(const ASTContext &Context) : Context(Context) {}
private:
MicrosoftRecordLayoutBuilder(const MicrosoftRecordLayoutBuilder &) = delete;
void operator=(const MicrosoftRecordLayoutBuilder &) = delete;
public:
void layout(const RecordDecl *RD);
void cxxLayout(const CXXRecordDecl *RD);
void initializeLayout(const RecordDecl *RD);
void initializeCXXLayout(const CXXRecordDecl *RD);
void layoutNonVirtualBases(const CXXRecordDecl *RD);
void layoutNonVirtualBase(const CXXRecordDecl *BaseDecl,
const ASTRecordLayout &BaseLayout,
const ASTRecordLayout *&PreviousBaseLayout);
void injectVFPtr(const CXXRecordDecl *RD);
void injectVBPtr(const CXXRecordDecl *RD);
void layoutFields(const RecordDecl *RD);
void layoutField(const FieldDecl *FD);
void layoutBitField(const FieldDecl *FD);
void layoutZeroWidthBitField(const FieldDecl *FD);
void layoutVirtualBases(const CXXRecordDecl *RD);
void finalizeLayout(const RecordDecl *RD);
ElementInfo getAdjustedElementInfo(const ASTRecordLayout &Layout);
ElementInfo getAdjustedElementInfo(const FieldDecl *FD);
void placeFieldAtOffset(CharUnits FieldOffset) {
FieldOffsets.push_back(Context.toBits(FieldOffset));
}
void placeFieldAtBitOffset(uint64_t FieldOffset) {
FieldOffsets.push_back(FieldOffset);
}
void computeVtorDispSet(
llvm::SmallPtrSetImpl<const CXXRecordDecl *> &HasVtorDispSet,
const CXXRecordDecl *RD) const;
const ASTContext &Context;
CharUnits Size;
CharUnits NonVirtualSize;
CharUnits DataSize;
CharUnits Alignment;
CharUnits MaxFieldAlignment;
CharUnits RequiredAlignment;
CharUnits CurrentBitfieldSize;
CharUnits VBPtrOffset;
CharUnits MinEmptyStructSize;
ElementInfo PointerInfo;
const CXXRecordDecl *PrimaryBase;
const CXXRecordDecl *SharedVBPtrBase;
SmallVector<uint64_t, 16> FieldOffsets;
BaseOffsetsMapTy Bases;
ASTRecordLayout::VBaseOffsetsMapTy VBases;
unsigned RemainingBitsInField;
bool IsUnion : 1;
bool LastFieldIsNonZeroWidthBitfield : 1;
bool HasOwnVFPtr : 1;
bool HasVBPtr : 1;
bool EndsWithZeroSizedObject : 1;
bool LeadsWithZeroSizedBase : 1;
bool UseExternalLayout : 1;
ExternalLayout External;
};
}
MicrosoftRecordLayoutBuilder::ElementInfo
MicrosoftRecordLayoutBuilder::getAdjustedElementInfo(
const ASTRecordLayout &Layout) {
ElementInfo Info;
Info.Alignment = Layout.getAlignment();
if (!MaxFieldAlignment.isZero())
Info.Alignment = std::min(Info.Alignment, MaxFieldAlignment);
EndsWithZeroSizedObject = Layout.hasZeroSizedSubObject();
Alignment = std::max(Alignment, Info.Alignment);
RequiredAlignment = std::max(RequiredAlignment, Layout.getRequiredAlignment());
Info.Alignment = std::max(Info.Alignment, Layout.getRequiredAlignment());
Info.Size = Layout.getNonVirtualSize();
return Info;
}
MicrosoftRecordLayoutBuilder::ElementInfo
MicrosoftRecordLayoutBuilder::getAdjustedElementInfo(
const FieldDecl *FD) {
ElementInfo Info;
std::tie(Info.Size, Info.Alignment) =
Context.getTypeInfoInChars(FD->getType()->getUnqualifiedDesugaredType());
CharUnits FieldRequiredAlignment =
Context.toCharUnitsFromBits(FD->getMaxAlignment());
if (Context.isAlignmentRequired(FD->getType()))
FieldRequiredAlignment = std::max(
Context.getTypeAlignInChars(FD->getType()), FieldRequiredAlignment);
if (FD->isBitField())
Info.Alignment = std::max(Info.Alignment, FieldRequiredAlignment);
else {
if (auto RT =
FD->getType()->getBaseElementTypeUnsafe()->getAs<RecordType>()) {
auto const &Layout = Context.getASTRecordLayout(RT->getDecl());
EndsWithZeroSizedObject = Layout.hasZeroSizedSubObject();
FieldRequiredAlignment = std::max(FieldRequiredAlignment,
Layout.getRequiredAlignment());
}
RequiredAlignment = std::max(RequiredAlignment, FieldRequiredAlignment);
}
if (!MaxFieldAlignment.isZero())
Info.Alignment = std::min(Info.Alignment, MaxFieldAlignment);
if (FD->hasAttr<PackedAttr>())
Info.Alignment = CharUnits::One();
Info.Alignment = std::max(Info.Alignment, FieldRequiredAlignment);
return Info;
}
void MicrosoftRecordLayoutBuilder::layout(const RecordDecl *RD) {
MinEmptyStructSize = CharUnits::fromQuantity(4);
initializeLayout(RD);
layoutFields(RD);
DataSize = Size = Size.alignTo(Alignment);
RequiredAlignment = std::max(
RequiredAlignment, Context.toCharUnitsFromBits(RD->getMaxAlignment()));
finalizeLayout(RD);
}
void MicrosoftRecordLayoutBuilder::cxxLayout(const CXXRecordDecl *RD) {
MinEmptyStructSize = CharUnits::One();
initializeLayout(RD);
initializeCXXLayout(RD);
layoutNonVirtualBases(RD);
layoutFields(RD);
injectVBPtr(RD);
injectVFPtr(RD);
if (HasOwnVFPtr || (HasVBPtr && !SharedVBPtrBase))
Alignment = std::max(Alignment, PointerInfo.Alignment);
auto RoundingAlignment = Alignment;
if (!MaxFieldAlignment.isZero())
RoundingAlignment = std::min(RoundingAlignment, MaxFieldAlignment);
NonVirtualSize = Size = Size.alignTo(RoundingAlignment);
RequiredAlignment = std::max(
RequiredAlignment, Context.toCharUnitsFromBits(RD->getMaxAlignment()));
layoutVirtualBases(RD);
finalizeLayout(RD);
}
void MicrosoftRecordLayoutBuilder::initializeLayout(const RecordDecl *RD) {
IsUnion = RD->isUnion();
Size = CharUnits::Zero();
Alignment = CharUnits::One();
RequiredAlignment = Context.getTargetInfo().getTriple().isArch64Bit()
? CharUnits::One()
: CharUnits::Zero();
MaxFieldAlignment = CharUnits::Zero();
if (unsigned DefaultMaxFieldAlignment = Context.getLangOpts().PackStruct)
MaxFieldAlignment = CharUnits::fromQuantity(DefaultMaxFieldAlignment);
if (const MaxFieldAlignmentAttr *MFAA = RD->getAttr<MaxFieldAlignmentAttr>()){
unsigned PackedAlignment = MFAA->getAlignment();
if (PackedAlignment <= Context.getTargetInfo().getPointerWidth(0))
MaxFieldAlignment = Context.toCharUnitsFromBits(PackedAlignment);
}
if (RD->hasAttr<PackedAttr>())
MaxFieldAlignment = CharUnits::One();
UseExternalLayout = false;
if (ExternalASTSource *Source = Context.getExternalSource())
UseExternalLayout = Source->layoutRecordType(
RD, External.Size, External.Align, External.FieldOffsets,
External.BaseOffsets, External.VirtualBaseOffsets);
}
void
MicrosoftRecordLayoutBuilder::initializeCXXLayout(const CXXRecordDecl *RD) {
EndsWithZeroSizedObject = false;
LeadsWithZeroSizedBase = false;
HasOwnVFPtr = false;
HasVBPtr = false;
PrimaryBase = nullptr;
SharedVBPtrBase = nullptr;
PointerInfo.Size =
Context.toCharUnitsFromBits(Context.getTargetInfo().getPointerWidth(0));
PointerInfo.Alignment =
Context.toCharUnitsFromBits(Context.getTargetInfo().getPointerAlign(0));
if (!MaxFieldAlignment.isZero())
PointerInfo.Alignment = std::min(PointerInfo.Alignment, MaxFieldAlignment);
}
void
MicrosoftRecordLayoutBuilder::layoutNonVirtualBases(const CXXRecordDecl *RD) {
const ASTRecordLayout *PreviousBaseLayout = nullptr;
for (const CXXBaseSpecifier &Base : RD->bases()) {
const CXXRecordDecl *BaseDecl = Base.getType()->getAsCXXRecordDecl();
const ASTRecordLayout &BaseLayout = Context.getASTRecordLayout(BaseDecl);
if (Base.isVirtual()) {
HasVBPtr = true;
continue;
}
if (!SharedVBPtrBase && BaseLayout.hasVBPtr()) {
SharedVBPtrBase = BaseDecl;
HasVBPtr = true;
}
if (!BaseLayout.hasExtendableVFPtr())
continue;
if (!PrimaryBase) {
PrimaryBase = BaseDecl;
LeadsWithZeroSizedBase = BaseLayout.leadsWithZeroSizedBase();
}
layoutNonVirtualBase(BaseDecl, BaseLayout, PreviousBaseLayout);
}
if (!PrimaryBase && RD->isDynamicClass())
for (CXXRecordDecl::method_iterator i = RD->method_begin(),
e = RD->method_end();
!HasOwnVFPtr && i != e; ++i)
HasOwnVFPtr = i->isVirtual() && i->size_overridden_methods() == 0;
bool CheckLeadingLayout = !PrimaryBase;
for (const CXXBaseSpecifier &Base : RD->bases()) {
if (Base.isVirtual())
continue;
const CXXRecordDecl *BaseDecl = Base.getType()->getAsCXXRecordDecl();
const ASTRecordLayout &BaseLayout = Context.getASTRecordLayout(BaseDecl);
if (BaseLayout.hasExtendableVFPtr()) {
VBPtrOffset = Bases[BaseDecl] + BaseLayout.getNonVirtualSize();
continue;
}
if (CheckLeadingLayout) {
CheckLeadingLayout = false;
LeadsWithZeroSizedBase = BaseLayout.leadsWithZeroSizedBase();
}
layoutNonVirtualBase(BaseDecl, BaseLayout, PreviousBaseLayout);
VBPtrOffset = Bases[BaseDecl] + BaseLayout.getNonVirtualSize();
}
if (!HasVBPtr)
VBPtrOffset = CharUnits::fromQuantity(-1);
else if (SharedVBPtrBase) {
const ASTRecordLayout &Layout = Context.getASTRecordLayout(SharedVBPtrBase);
VBPtrOffset = Bases[SharedVBPtrBase] + Layout.getVBPtrOffset();
}
}
void MicrosoftRecordLayoutBuilder::layoutNonVirtualBase(
const CXXRecordDecl *BaseDecl,
const ASTRecordLayout &BaseLayout,
const ASTRecordLayout *&PreviousBaseLayout) {
if (PreviousBaseLayout && PreviousBaseLayout->hasZeroSizedSubObject() &&
BaseLayout.leadsWithZeroSizedBase())
Size++;
ElementInfo Info = getAdjustedElementInfo(BaseLayout);
CharUnits BaseOffset;
bool FoundBase = false;
if (UseExternalLayout) {
FoundBase = External.getExternalNVBaseOffset(BaseDecl, BaseOffset);
if (FoundBase)
assert(BaseOffset >= Size && "base offset already allocated");
}
if (!FoundBase)
BaseOffset = Size.alignTo(Info.Alignment);
Bases.insert(std::make_pair(BaseDecl, BaseOffset));
Size = BaseOffset + BaseLayout.getNonVirtualSize();
PreviousBaseLayout = &BaseLayout;
}
void MicrosoftRecordLayoutBuilder::layoutFields(const RecordDecl *RD) {
LastFieldIsNonZeroWidthBitfield = false;
for (const FieldDecl *Field : RD->fields())
layoutField(Field);
}
void MicrosoftRecordLayoutBuilder::layoutField(const FieldDecl *FD) {
if (FD->isBitField()) {
layoutBitField(FD);
return;
}
LastFieldIsNonZeroWidthBitfield = false;
ElementInfo Info = getAdjustedElementInfo(FD);
Alignment = std::max(Alignment, Info.Alignment);
if (IsUnion) {
placeFieldAtOffset(CharUnits::Zero());
Size = std::max(Size, Info.Size);
} else {
CharUnits FieldOffset;
if (UseExternalLayout) {
FieldOffset =
Context.toCharUnitsFromBits(External.getExternalFieldOffset(FD));
assert(FieldOffset >= Size && "field offset already allocated");
} else {
FieldOffset = Size.alignTo(Info.Alignment);
}
placeFieldAtOffset(FieldOffset);
Size = FieldOffset + Info.Size;
}
}
void MicrosoftRecordLayoutBuilder::layoutBitField(const FieldDecl *FD) {
unsigned Width = FD->getBitWidthValue(Context);
if (Width == 0) {
layoutZeroWidthBitField(FD);
return;
}
ElementInfo Info = getAdjustedElementInfo(FD);
if (Width > Context.toBits(Info.Size))
Width = Context.toBits(Info.Size);
if (!IsUnion && LastFieldIsNonZeroWidthBitfield &&
CurrentBitfieldSize == Info.Size && Width <= RemainingBitsInField) {
placeFieldAtBitOffset(Context.toBits(Size) - RemainingBitsInField);
RemainingBitsInField -= Width;
return;
}
LastFieldIsNonZeroWidthBitfield = true;
CurrentBitfieldSize = Info.Size;
if (IsUnion) {
placeFieldAtOffset(CharUnits::Zero());
Size = std::max(Size, Info.Size);
} else {
CharUnits FieldOffset = Size.alignTo(Info.Alignment);
placeFieldAtOffset(FieldOffset);
Size = FieldOffset + Info.Size;
Alignment = std::max(Alignment, Info.Alignment);
RemainingBitsInField = Context.toBits(Info.Size) - Width;
}
}
void
MicrosoftRecordLayoutBuilder::layoutZeroWidthBitField(const FieldDecl *FD) {
if (!LastFieldIsNonZeroWidthBitfield) {
placeFieldAtOffset(IsUnion ? CharUnits::Zero() : Size);
return;
}
LastFieldIsNonZeroWidthBitfield = false;
ElementInfo Info = getAdjustedElementInfo(FD);
if (IsUnion) {
placeFieldAtOffset(CharUnits::Zero());
Size = std::max(Size, Info.Size);
} else {
CharUnits FieldOffset = Size.alignTo(Info.Alignment);
placeFieldAtOffset(FieldOffset);
Size = FieldOffset;
Alignment = std::max(Alignment, Info.Alignment);
}
}
void MicrosoftRecordLayoutBuilder::injectVBPtr(const CXXRecordDecl *RD) {
if (!HasVBPtr || SharedVBPtrBase)
return;
CharUnits InjectionSite = VBPtrOffset;
VBPtrOffset = VBPtrOffset.alignTo(PointerInfo.Alignment);
if (UseExternalLayout)
return;
CharUnits FieldStart = VBPtrOffset + PointerInfo.Size;
CharUnits Offset = (FieldStart - InjectionSite)
.alignTo(std::max(RequiredAlignment, Alignment));
Size += Offset;
for (uint64_t &FieldOffset : FieldOffsets)
FieldOffset += Context.toBits(Offset);
for (BaseOffsetsMapTy::value_type &Base : Bases)
if (Base.second >= InjectionSite)
Base.second += Offset;
}
void MicrosoftRecordLayoutBuilder::injectVFPtr(const CXXRecordDecl *RD) {
if (!HasOwnVFPtr)
return;
CharUnits Offset =
PointerInfo.Size.alignTo(std::max(RequiredAlignment, Alignment));
if (HasVBPtr)
VBPtrOffset += Offset;
if (UseExternalLayout)
return;
Size += Offset;
for (uint64_t &FieldOffset : FieldOffsets)
FieldOffset += Context.toBits(Offset);
for (BaseOffsetsMapTy::value_type &Base : Bases)
Base.second += Offset;
}
void MicrosoftRecordLayoutBuilder::layoutVirtualBases(const CXXRecordDecl *RD) {
if (!HasVBPtr)
return;
CharUnits VtorDispSize = CharUnits::fromQuantity(4);
CharUnits VtorDispAlignment = VtorDispSize;
if (!MaxFieldAlignment.isZero())
VtorDispAlignment = std::min(VtorDispAlignment, MaxFieldAlignment);
for (const CXXBaseSpecifier &VBase : RD->vbases()) {
const CXXRecordDecl *BaseDecl = VBase.getType()->getAsCXXRecordDecl();
const ASTRecordLayout &BaseLayout = Context.getASTRecordLayout(BaseDecl);
RequiredAlignment =
std::max(RequiredAlignment, BaseLayout.getRequiredAlignment());
}
VtorDispAlignment = std::max(VtorDispAlignment, RequiredAlignment);
llvm::SmallPtrSet<const CXXRecordDecl *, 2> HasVtorDispSet;
computeVtorDispSet(HasVtorDispSet, RD);
const ASTRecordLayout *PreviousBaseLayout = nullptr;
for (const CXXBaseSpecifier &VBase : RD->vbases()) {
const CXXRecordDecl *BaseDecl = VBase.getType()->getAsCXXRecordDecl();
const ASTRecordLayout &BaseLayout = Context.getASTRecordLayout(BaseDecl);
bool HasVtordisp = HasVtorDispSet.count(BaseDecl) > 0;
if ((PreviousBaseLayout && PreviousBaseLayout->hasZeroSizedSubObject() &&
BaseLayout.leadsWithZeroSizedBase()) || HasVtordisp) {
Size = Size.alignTo(VtorDispAlignment) + VtorDispSize;
Alignment = std::max(VtorDispAlignment, Alignment);
}
ElementInfo Info = getAdjustedElementInfo(BaseLayout);
CharUnits BaseOffset;
bool FoundBase = false;
if (UseExternalLayout) {
FoundBase = External.getExternalVBaseOffset(BaseDecl, BaseOffset);
if (FoundBase)
assert(BaseOffset >= Size && "base offset already allocated");
}
if (!FoundBase)
BaseOffset = Size.alignTo(Info.Alignment);
VBases.insert(std::make_pair(BaseDecl,
ASTRecordLayout::VBaseInfo(BaseOffset, HasVtordisp)));
Size = BaseOffset + BaseLayout.getNonVirtualSize();
PreviousBaseLayout = &BaseLayout;
}
}
void MicrosoftRecordLayoutBuilder::finalizeLayout(const RecordDecl *RD) {
DataSize = Size;
if (!RequiredAlignment.isZero()) {
Alignment = std::max(Alignment, RequiredAlignment);
auto RoundingAlignment = Alignment;
if (!MaxFieldAlignment.isZero())
RoundingAlignment = std::min(RoundingAlignment, MaxFieldAlignment);
RoundingAlignment = std::max(RoundingAlignment, RequiredAlignment);
Size = Size.alignTo(RoundingAlignment);
}
if (Size.isZero()) {
EndsWithZeroSizedObject = true;
LeadsWithZeroSizedBase = true;
if (RequiredAlignment >= MinEmptyStructSize)
Size = Alignment;
else
Size = MinEmptyStructSize;
}
if (UseExternalLayout) {
Size = Context.toCharUnitsFromBits(External.Size);
if (External.Align)
Alignment = Context.toCharUnitsFromBits(External.Align);
}
}
static bool
RequiresVtordisp(const llvm::SmallPtrSetImpl<const CXXRecordDecl *> &
BasesWithOverriddenMethods,
const CXXRecordDecl *RD) {
if (BasesWithOverriddenMethods.count(RD))
return true;
for (const CXXBaseSpecifier &Base : RD->bases())
if (!Base.isVirtual() &&
RequiresVtordisp(BasesWithOverriddenMethods,
Base.getType()->getAsCXXRecordDecl()))
return true;
return false;
}
void MicrosoftRecordLayoutBuilder::computeVtorDispSet(
llvm::SmallPtrSetImpl<const CXXRecordDecl *> &HasVtordispSet,
const CXXRecordDecl *RD) const {
if (RD->getMSVtorDispMode() == MSVtorDispAttr::ForVFTable) {
for (const CXXBaseSpecifier &Base : RD->vbases()) {
const CXXRecordDecl *BaseDecl = Base.getType()->getAsCXXRecordDecl();
const ASTRecordLayout &Layout = Context.getASTRecordLayout(BaseDecl);
if (Layout.hasExtendableVFPtr())
HasVtordispSet.insert(BaseDecl);
}
return;
}
for (const CXXBaseSpecifier &Base : RD->bases()) {
const CXXRecordDecl *BaseDecl = Base.getType()->getAsCXXRecordDecl();
const ASTRecordLayout &Layout = Context.getASTRecordLayout(BaseDecl);
for (const auto &bi : Layout.getVBaseOffsetsMap())
if (bi.second.hasVtorDisp())
HasVtordispSet.insert(bi.first);
}
if ((!RD->hasUserDeclaredConstructor() && !RD->hasUserDeclaredDestructor()) ||
RD->getMSVtorDispMode() == MSVtorDispAttr::Never)
return;
assert(RD->getMSVtorDispMode() == MSVtorDispAttr::ForVBaseOverride);
llvm::SmallPtrSet<const CXXMethodDecl *, 8> Work;
llvm::SmallPtrSet<const CXXRecordDecl *, 2> BasesWithOverriddenMethods;
for (const CXXMethodDecl *MD : RD->methods())
if (MD->isVirtual() && !isa<CXXDestructorDecl>(MD) && !MD->isPure())
Work.insert(MD);
while (!Work.empty()) {
const CXXMethodDecl *MD = *Work.begin();
CXXMethodDecl::method_iterator i = MD->begin_overridden_methods(),
e = MD->end_overridden_methods();
if (i == e)
BasesWithOverriddenMethods.insert(MD->getParent());
else
Work.insert(i, e);
Work.erase(MD);
}
for (const CXXBaseSpecifier &Base : RD->vbases()) {
const CXXRecordDecl *BaseDecl = Base.getType()->getAsCXXRecordDecl();
if (!HasVtordispSet.count(BaseDecl) &&
RequiresVtordisp(BasesWithOverriddenMethods, BaseDecl))
HasVtordispSet.insert(BaseDecl);
}
}
const ASTRecordLayout &
ASTContext::getASTRecordLayout(const RecordDecl *D) const {
if (D->hasExternalLexicalStorage() && !D->getDefinition())
getExternalSource()->CompleteType(const_cast<RecordDecl*>(D));
D = D->getDefinition();
assert(D && "Cannot get layout of forward declarations!");
assert(!D->isInvalidDecl() && "Cannot get layout of invalid decl!");
assert(D->isCompleteDefinition() && "Cannot layout type before complete!");
const ASTRecordLayout *Entry = ASTRecordLayouts[D];
if (Entry) return *Entry;
const ASTRecordLayout *NewEntry = nullptr;
if (isMsLayout(*this)) {
MicrosoftRecordLayoutBuilder Builder(*this);
if (const auto *RD = dyn_cast<CXXRecordDecl>(D)) {
Builder.cxxLayout(RD);
NewEntry = new (*this) ASTRecordLayout(
*this, Builder.Size, Builder.Alignment, Builder.RequiredAlignment,
Builder.HasOwnVFPtr, Builder.HasOwnVFPtr || Builder.PrimaryBase,
Builder.VBPtrOffset, Builder.NonVirtualSize,
Builder.FieldOffsets.data(), Builder.FieldOffsets.size(),
Builder.NonVirtualSize, Builder.Alignment, CharUnits::Zero(),
Builder.PrimaryBase, false, Builder.SharedVBPtrBase,
Builder.EndsWithZeroSizedObject, Builder.LeadsWithZeroSizedBase,
Builder.Bases, Builder.VBases);
} else {
Builder.layout(D);
NewEntry = new (*this) ASTRecordLayout(
*this, Builder.Size, Builder.Alignment, Builder.RequiredAlignment,
Builder.Size, Builder.FieldOffsets.data(),
Builder.FieldOffsets.size());
}
} else {
if (const auto *RD = dyn_cast<CXXRecordDecl>(D)) {
EmptySubobjectMap EmptySubobjects(*this, RD);
ItaniumRecordLayoutBuilder Builder(*this, &EmptySubobjects);
Builder.Layout(RD);
bool skipTailPadding =
mustSkipTailPadding(getTargetInfo().getCXXABI(), RD);
CharUnits DataSize =
skipTailPadding ? Builder.getSize() : Builder.getDataSize();
CharUnits NonVirtualSize =
skipTailPadding ? DataSize : Builder.NonVirtualSize;
NewEntry = new (*this) ASTRecordLayout(
*this, Builder.getSize(), Builder.Alignment,
Builder.Alignment, Builder.HasOwnVFPtr, RD->isDynamicClass(),
CharUnits::fromQuantity(-1), DataSize, Builder.FieldOffsets.data(),
Builder.FieldOffsets.size(), NonVirtualSize,
Builder.NonVirtualAlignment,
EmptySubobjects.SizeOfLargestEmptySubobject, Builder.PrimaryBase,
Builder.PrimaryBaseIsVirtual, nullptr, false, false, Builder.Bases,
Builder.VBases);
} else {
ItaniumRecordLayoutBuilder Builder(*this, nullptr);
Builder.Layout(D);
NewEntry = new (*this) ASTRecordLayout(
*this, Builder.getSize(), Builder.Alignment,
Builder.Alignment, Builder.getSize(), Builder.FieldOffsets.data(),
Builder.FieldOffsets.size());
}
}
ASTRecordLayouts[D] = NewEntry;
if (getLangOpts().DumpRecordLayouts) {
llvm::outs() << "\n*** Dumping AST Record Layout\n";
DumpRecordLayout(D, llvm::outs(), getLangOpts().DumpRecordLayoutsSimple);
}
return *NewEntry;
}
const CXXMethodDecl *ASTContext::getCurrentKeyFunction(const CXXRecordDecl *RD) {
if (!getTargetInfo().getCXXABI().hasKeyFunctions())
return nullptr;
assert(RD->getDefinition() && "Cannot get key function for forward decl!");
RD = cast<CXXRecordDecl>(RD->getDefinition());
LazyDeclPtr Entry = KeyFunctions[RD];
const Decl *Result =
Entry ? Entry.get(getExternalSource()) : computeKeyFunction(*this, RD);
if (Entry.isOffset() || Entry.isValid() != bool(Result))
KeyFunctions[RD] = const_cast<Decl*>(Result);
return cast_or_null<CXXMethodDecl>(Result);
}
void ASTContext::setNonKeyFunction(const CXXMethodDecl *Method) {
assert(Method == Method->getFirstDecl() &&
"not working with method declaration from class definition");
const auto &Map = KeyFunctions;
auto I = Map.find(Method->getParent());
if (I == Map.end()) return;
LazyDeclPtr Ptr = I->second;
if (Ptr.get(getExternalSource()) == Method) {
KeyFunctions.erase(Method->getParent());
}
}
static uint64_t getFieldOffset(const ASTContext &C, const FieldDecl *FD) {
const ASTRecordLayout &Layout = C.getASTRecordLayout(FD->getParent());
return Layout.getFieldOffset(FD->getFieldIndex());
}
uint64_t ASTContext::getFieldOffset(const ValueDecl *VD) const {
uint64_t OffsetInBits;
if (const FieldDecl *FD = dyn_cast<FieldDecl>(VD)) {
OffsetInBits = ::getFieldOffset(*this, FD);
} else {
const IndirectFieldDecl *IFD = cast<IndirectFieldDecl>(VD);
OffsetInBits = 0;
for (const NamedDecl *ND : IFD->chain())
OffsetInBits += ::getFieldOffset(*this, cast<FieldDecl>(ND));
}
return OffsetInBits;
}
const ASTRecordLayout &
ASTContext::getObjCLayout(const ObjCInterfaceDecl *D,
const ObjCImplementationDecl *Impl) const {
if (D->hasExternalLexicalStorage() && !D->getDefinition())
getExternalSource()->CompleteType(const_cast<ObjCInterfaceDecl*>(D));
D = D->getDefinition();
assert(D && D->isThisDeclarationADefinition() && "Invalid interface decl!");
const ObjCContainerDecl *Key =
Impl ? (const ObjCContainerDecl*) Impl : (const ObjCContainerDecl*) D;
if (const ASTRecordLayout *Entry = ObjCLayouts[Key])
return *Entry;
if (Impl) {
unsigned SynthCount = CountNonClassIvars(D);
if (SynthCount == 0)
return getObjCLayout(D, nullptr);
}
ItaniumRecordLayoutBuilder Builder(*this, nullptr);
Builder.Layout(D);
const ASTRecordLayout *NewEntry =
new (*this) ASTRecordLayout(*this, Builder.getSize(),
Builder.Alignment,
Builder.Alignment,
Builder.getDataSize(),
Builder.FieldOffsets.data(),
Builder.FieldOffsets.size());
ObjCLayouts[Key] = NewEntry;
return *NewEntry;
}
static void PrintOffset(raw_ostream &OS,
CharUnits Offset, unsigned IndentLevel) {
OS << llvm::format("%10" PRId64 " | ", (int64_t)Offset.getQuantity());
OS.indent(IndentLevel * 2);
}
static void PrintBitFieldOffset(raw_ostream &OS, CharUnits Offset,
unsigned Begin, unsigned Width,
unsigned IndentLevel) {
llvm::SmallString<10> Buffer;
{
llvm::raw_svector_ostream BufferOS(Buffer);
BufferOS << Offset.getQuantity() << ':';
if (Width == 0) {
BufferOS << '-';
} else {
BufferOS << Begin << '-' << (Begin + Width - 1);
}
}
OS << llvm::right_justify(Buffer, 10) << " | ";
OS.indent(IndentLevel * 2);
}
static void PrintIndentNoOffset(raw_ostream &OS, unsigned IndentLevel) {
OS << " | ";
OS.indent(IndentLevel * 2);
}
static void DumpRecordLayout(raw_ostream &OS, const RecordDecl *RD,
const ASTContext &C,
CharUnits Offset,
unsigned IndentLevel,
const char* Description,
bool PrintSizeInfo,
bool IncludeVirtualBases) {
const ASTRecordLayout &Layout = C.getASTRecordLayout(RD);
auto CXXRD = dyn_cast<CXXRecordDecl>(RD);
PrintOffset(OS, Offset, IndentLevel);
OS << C.getTypeDeclType(const_cast<RecordDecl*>(RD)).getAsString();
if (Description)
OS << ' ' << Description;
if (CXXRD && CXXRD->isEmpty())
OS << " (empty)";
OS << '\n';
IndentLevel++;
if (CXXRD) {
const CXXRecordDecl *PrimaryBase = Layout.getPrimaryBase();
bool HasOwnVFPtr = Layout.hasOwnVFPtr();
bool HasOwnVBPtr = Layout.hasOwnVBPtr();
if (CXXRD->isDynamicClass() && !PrimaryBase && !isMsLayout(C)) {
PrintOffset(OS, Offset, IndentLevel);
OS << '(' << *RD << " vtable pointer)\n";
} else if (HasOwnVFPtr) {
PrintOffset(OS, Offset, IndentLevel);
OS << '(' << *RD << " vftable pointer)\n";
}
SmallVector<const CXXRecordDecl *, 4> Bases;
for (const CXXBaseSpecifier &Base : CXXRD->bases()) {
assert(!Base.getType()->isDependentType() &&
"Cannot layout class with dependent bases.");
if (!Base.isVirtual())
Bases.push_back(Base.getType()->getAsCXXRecordDecl());
}
std::stable_sort(Bases.begin(), Bases.end(),
[&](const CXXRecordDecl *L, const CXXRecordDecl *R) {
return Layout.getBaseClassOffset(L) < Layout.getBaseClassOffset(R);
});
for (const CXXRecordDecl *Base : Bases) {
CharUnits BaseOffset = Offset + Layout.getBaseClassOffset(Base);
DumpRecordLayout(OS, Base, C, BaseOffset, IndentLevel,
Base == PrimaryBase ? "(primary base)" : "(base)",
false,
false);
}
if (HasOwnVBPtr) {
PrintOffset(OS, Offset + Layout.getVBPtrOffset(), IndentLevel);
OS << '(' << *RD << " vbtable pointer)\n";
}
}
uint64_t FieldNo = 0;
for (RecordDecl::field_iterator I = RD->field_begin(),
E = RD->field_end(); I != E; ++I, ++FieldNo) {
const FieldDecl &Field = **I;
uint64_t LocalFieldOffsetInBits = Layout.getFieldOffset(FieldNo);
CharUnits FieldOffset =
Offset + C.toCharUnitsFromBits(LocalFieldOffsetInBits);
if (auto RT = Field.getType()->getAs<RecordType>()) {
DumpRecordLayout(OS, RT->getDecl(), C, FieldOffset, IndentLevel,
Field.getName().data(),
false,
true);
continue;
}
if (Field.isBitField()) {
uint64_t LocalFieldByteOffsetInBits = C.toBits(FieldOffset - Offset);
unsigned Begin = LocalFieldOffsetInBits - LocalFieldByteOffsetInBits;
unsigned Width = Field.getBitWidthValue(C);
PrintBitFieldOffset(OS, FieldOffset, Begin, Width, IndentLevel);
} else {
PrintOffset(OS, FieldOffset, IndentLevel);
}
OS << Field.getType().getAsString() << ' ' << Field << '\n';
}
if (CXXRD && IncludeVirtualBases) {
const ASTRecordLayout::VBaseOffsetsMapTy &VtorDisps =
Layout.getVBaseOffsetsMap();
for (const CXXBaseSpecifier &Base : CXXRD->vbases()) {
assert(Base.isVirtual() && "Found non-virtual class!");
const CXXRecordDecl *VBase = Base.getType()->getAsCXXRecordDecl();
CharUnits VBaseOffset = Offset + Layout.getVBaseClassOffset(VBase);
if (VtorDisps.find(VBase)->second.hasVtorDisp()) {
PrintOffset(OS, VBaseOffset - CharUnits::fromQuantity(4), IndentLevel);
OS << "(vtordisp for vbase " << *VBase << ")\n";
}
DumpRecordLayout(OS, VBase, C, VBaseOffset, IndentLevel,
VBase == Layout.getPrimaryBase() ?
"(primary virtual base)" : "(virtual base)",
false,
false);
}
}
if (!PrintSizeInfo) return;
PrintIndentNoOffset(OS, IndentLevel - 1);
OS << "[sizeof=" << Layout.getSize().getQuantity();
if (CXXRD && !isMsLayout(C))
OS << ", dsize=" << Layout.getDataSize().getQuantity();
OS << ", align=" << Layout.getAlignment().getQuantity();
if (CXXRD) {
OS << ",\n";
PrintIndentNoOffset(OS, IndentLevel - 1);
OS << " nvsize=" << Layout.getNonVirtualSize().getQuantity();
OS << ", nvalign=" << Layout.getNonVirtualAlignment().getQuantity();
}
OS << "]\n";
}
void ASTContext::DumpRecordLayout(const RecordDecl *RD,
raw_ostream &OS,
bool Simple) const {
if (!Simple) {
::DumpRecordLayout(OS, RD, *this, CharUnits(), 0, nullptr,
true,
true);
return;
}
const ASTRecordLayout &Info = getASTRecordLayout(RD);
OS << "Type: " << getTypeDeclType(RD).getAsString() << "\n";
OS << "\nLayout: ";
OS << "<ASTRecordLayout\n";
OS << " Size:" << toBits(Info.getSize()) << "\n";
if (!isMsLayout(*this))
OS << " DataSize:" << toBits(Info.getDataSize()) << "\n";
OS << " Alignment:" << toBits(Info.getAlignment()) << "\n";
OS << " FieldOffsets: [";
for (unsigned i = 0, e = Info.getFieldCount(); i != e; ++i) {
if (i) OS << ", ";
OS << Info.getFieldOffset(i);
}
OS << "]>\n";
}