#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTDiagnostic.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/AST/VTableBuilder.h"
#include "clang/AST/RecordLayout.h"
#include "clang/Basic/TargetInfo.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,
bool PlacingOverlappingField);
void UpdateEmptyFieldSubobjects(const FieldDecl *FD, CharUnits Offset,
bool PlacingOverlappingField);
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 (!llvm::is_contained(Classes, RD))
return true;
return false;
}
void EmptySubobjectMap::AddSubobjectAtOffset(const CXXRecordDecl *RD,
CharUnits Offset) {
if (!RD->isEmpty())
return;
ClassVectorTy &Classes = EmptyClassOffsets[Offset];
if (llvm::is_contained(Classes, RD))
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, PlacingEmptyBase);
}
}
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, FD->hasAttr<NoUniqueAddressAttr>());
return true;
}
void EmptySubobjectMap::UpdateEmptyFieldSubobjects(
const CXXRecordDecl *RD, const CXXRecordDecl *Class, CharUnits Offset,
bool PlacingOverlappingField) {
if (!PlacingOverlappingField && 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,
PlacingOverlappingField);
}
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,
PlacingOverlappingField);
}
}
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, PlacingOverlappingField);
}
}
void EmptySubobjectMap::UpdateEmptyFieldSubobjects(
const FieldDecl *FD, CharUnits Offset, bool PlacingOverlappingField) {
QualType T = FD->getType();
if (const CXXRecordDecl *RD = T->getAsCXXRecordDecl()) {
UpdateEmptyFieldSubobjects(RD, RD, Offset, PlacingOverlappingField);
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 (!PlacingOverlappingField &&
ElementOffset >= SizeOfLargestEmptySubobject)
return;
UpdateEmptyFieldSubobjects(RD, RD, ElementOffset,
PlacingOverlappingField);
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 PreferredAlignment;
CharUnits UnpackedAlignment;
CharUnits UnadjustedAlignment;
SmallVector<uint64_t, 16> FieldOffsets;
unsigned UseExternalLayout : 1;
unsigned InferAlignment : 1;
unsigned Packed : 1;
unsigned IsUnion : 1;
unsigned IsMac68kAlign : 1;
unsigned IsNaturalAlign : 1;
unsigned IsMsStruct : 1;
unsigned char UnfilledBitsInLastUnit;
unsigned char LastBitfieldStorageUnitSize;
CharUnits MaxFieldAlignment;
uint64_t DataSize;
CharUnits NonVirtualSize;
CharUnits NonVirtualAlignment;
CharUnits PreferredNVAlignment;
CharUnits PaddedFieldSize;
const CXXRecordDecl *PrimaryBase;
bool PrimaryBaseIsVirtual;
bool HasOwnVFPtr;
bool HasPackedField;
bool HandledFirstNonOverlappingEmptyField;
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()), PreferredAlignment(CharUnits::One()),
UnpackedAlignment(CharUnits::One()),
UnadjustedAlignment(CharUnits::One()), UseExternalLayout(false),
InferAlignment(false), Packed(false), IsUnion(false),
IsMac68kAlign(false),
IsNaturalAlign(!Context.getTargetInfo().getTriple().isOSAIX()),
IsMsStruct(false), UnfilledBitsInLastUnit(0),
LastBitfieldStorageUnitSize(0), MaxFieldAlignment(CharUnits::Zero()),
DataSize(0), NonVirtualSize(CharUnits::Zero()),
NonVirtualAlignment(CharUnits::One()),
PreferredNVAlignment(CharUnits::One()),
PaddedFieldSize(CharUnits::Zero()), PrimaryBase(nullptr),
PrimaryBaseIsVirtual(false), HasOwnVFPtr(false), HasPackedField(false),
HandledFirstNonOverlappingEmptyField(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 StorageUnitSize,
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,
CharUnits PreferredAlignment);
void UpdateAlignment(CharUnits NewAlignment, CharUnits UnpackedNewAlignment) {
UpdateAlignment(NewAlignment, UnpackedNewAlignment, NewAlignment);
}
void UpdateAlignment(CharUnits NewAlignment) {
UpdateAlignment(NewAlignment, 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));
UpdateAlignment(BaseAlign, UnpackedBaseAlign, BaseAlign);
}
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;
assert(!IsUnion && "Unions cannot be dynamic classes.");
HandledFirstNonOverlappingEmptyField = 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) {
assert(!IsUnion && "Unions cannot have base classes.");
const ASTRecordLayout &Layout = Context.getASTRecordLayout(Base->Class);
CharUnits Offset;
bool HasExternalLayout = false;
if (UseExternalLayout) {
if (Base->IsVirtual)
HasExternalLayout = External.getExternalVBaseOffset(Base->Class, Offset);
else
HasExternalLayout = External.getExternalNVBaseOffset(Base->Class, Offset);
}
auto getBaseOrPreferredBaseAlignFromUnpacked = [&](CharUnits UnpackedAlign) {
return (Packed && ((Context.getLangOpts().getClangABICompat() <=
LangOptions::ClangABI::Ver6) ||
Context.getTargetInfo().getTriple().isPS() ||
Context.getTargetInfo().getTriple().isOSAIX()))
? CharUnits::One()
: UnpackedAlign;
};
CharUnits UnpackedBaseAlign = Layout.getNonVirtualAlignment();
CharUnits UnpackedPreferredBaseAlign = Layout.getPreferredNVAlignment();
CharUnits BaseAlign =
getBaseOrPreferredBaseAlignFromUnpacked(UnpackedBaseAlign);
CharUnits PreferredBaseAlign =
getBaseOrPreferredBaseAlignFromUnpacked(UnpackedPreferredBaseAlign);
const bool DefaultsToAIXPowerAlignment =
Context.getTargetInfo().defaultsToAIXPowerAlignment();
if (DefaultsToAIXPowerAlignment) {
if (!Base->Class->isEmpty() && !HandledFirstNonOverlappingEmptyField) {
HandledFirstNonOverlappingEmptyField = true;
} else if (!IsNaturalAlign) {
UnpackedPreferredBaseAlign = UnpackedBaseAlign;
PreferredBaseAlign = BaseAlign;
}
}
CharUnits UnpackedAlignTo = !DefaultsToAIXPowerAlignment
? UnpackedBaseAlign
: UnpackedPreferredBaseAlign;
if (Base->Class->isEmpty() &&
(!HasExternalLayout || Offset == CharUnits::Zero()) &&
EmptySubobjects->CanPlaceBaseAtOffset(Base, CharUnits::Zero())) {
setSize(std::max(getSize(), Layout.getSize()));
if (!Context.getTargetInfo().getTriple().isPS())
UpdateAlignment(BaseAlign, UnpackedAlignTo, PreferredBaseAlign);
return CharUnits::Zero();
}
if (!MaxFieldAlignment.isZero()) {
BaseAlign = std::min(BaseAlign, MaxFieldAlignment);
PreferredBaseAlign = std::min(PreferredBaseAlign, MaxFieldAlignment);
UnpackedAlignTo = std::min(UnpackedAlignTo, MaxFieldAlignment);
}
CharUnits AlignTo =
!DefaultsToAIXPowerAlignment ? BaseAlign : PreferredBaseAlign;
if (!HasExternalLayout) {
Offset = getDataSize().alignTo(AlignTo);
while (!EmptySubobjects->CanPlaceBaseAtOffset(Base, Offset))
Offset += AlignTo;
} else {
bool Allowed = EmptySubobjects->CanPlaceBaseAtOffset(Base, Offset);
(void)Allowed;
assert(Allowed && "Base subobject externally placed at overlapping offset");
if (InferAlignment && Offset < getDataSize().alignTo(AlignTo)) {
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, UnpackedAlignTo, PreferredBaseAlign);
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>()) {
assert(
!D->hasAttr<AlignNaturalAttr>() &&
"Having both mac68k and natural alignment on a decl is not allowed.");
IsMac68kAlign = true;
MaxFieldAlignment = CharUnits::fromQuantity(2);
Alignment = CharUnits::fromQuantity(2);
PreferredAlignment = CharUnits::fromQuantity(2);
} else {
if (D->hasAttr<AlignNaturalAttr>())
IsNaturalAlign = true;
if (const MaxFieldAlignmentAttr *MFAA = D->getAttr<MaxFieldAlignmentAttr>())
MaxFieldAlignment = Context.toCharUnitsFromBits(MFAA->getAlignment());
if (unsigned MaxAlign = D->getMaxAlignment())
UpdateAlignment(Context.toCharUnitsFromBits(MaxAlign));
}
HandledFirstNonOverlappingEmptyField =
!Context.getTargetInfo().defaultsToAIXPowerAlignment() || IsNaturalAlign;
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);
PreferredAlignment = 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;
PreferredNVAlignment = PreferredAlignment;
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());
setDataSize(SL.getDataSize());
setSize(getDataSize());
}
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 StorageUnitSize,
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;
LastBitfieldStorageUnitSize = 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);
}
static bool isAIXLayout(const ASTContext &Context) {
return Context.getTargetInfo().getTriple().getOS() == llvm::Triple::AIX;
}
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 StorageUnitSize = FieldInfo.Width;
unsigned FieldAlign = FieldInfo.Align;
bool AlignIsRequired = FieldInfo.isAlignRequired();
if (IsMsStruct) {
FieldAlign = StorageUnitSize;
if (LastBitfieldStorageUnitSize != StorageUnitSize ||
UnfilledBitsInLastUnit < FieldSize) {
if (!LastBitfieldStorageUnitSize && !FieldSize)
FieldAlign = 1;
UnfilledBitsInLastUnit = 0;
LastBitfieldStorageUnitSize = 0;
}
}
if (isAIXLayout(Context)) {
if (StorageUnitSize < Context.getTypeSize(Context.UnsignedIntTy)) {
StorageUnitSize = Context.getTypeSize(Context.UnsignedIntTy);
} else if (StorageUnitSize > Context.getTypeSize(Context.UnsignedIntTy) &&
Context.getTargetInfo().getTriple().isArch32Bit() &&
FieldSize <= 32) {
StorageUnitSize = 32;
if (!AlignIsRequired)
FieldAlign = 32;
}
if (FieldAlign < StorageUnitSize) {
FieldAlign = StorageUnitSize;
}
}
if (FieldSize > StorageUnitSize && !isAIXLayout(Context)) {
LayoutWideBitField(FieldSize, StorageUnitSize, FieldPacked, D);
return;
}
uint64_t FieldOffset =
IsUnion ? 0 : (getDataSizeInBits() - UnfilledBitsInLastUnit);
if (!IsMsStruct && !Context.getTargetInfo().useBitFieldTypeAlignment()) {
if (FieldSize == 0 &&
Context.getTargetInfo().useZeroLengthBitfieldAlignment()) {
if (!IsUnion && FieldOffset == 0 &&
!Context.getTargetInfo().useLeadingZeroLengthBitfield())
FieldAlign = 1;
else {
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);
}
unsigned MaxFieldAlignmentInBits = Context.toBits(MaxFieldAlignment);
if (!MaxFieldAlignment.isZero() && FieldSize) {
UnpackedFieldAlign = std::min(UnpackedFieldAlign, MaxFieldAlignmentInBits);
if (FieldPacked)
FieldAlign = UnpackedFieldAlign;
else
FieldAlign = std::min(FieldAlign, 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 > StorageUnitSize)) {
FieldOffset = llvm::alignTo(FieldOffset, FieldAlign);
} else if (ExplicitFieldAlign &&
(MaxFieldAlignmentInBits == 0 ||
ExplicitFieldAlign <= MaxFieldAlignmentInBits) &&
Context.getTargetInfo().useExplicitBitFieldAlignment()) {
FieldOffset = llvm::alignTo(FieldOffset, ExplicitFieldAlign);
}
if (FieldSize == 0 ||
(AllowPadding &&
(UnpackedFieldOffset & (UnpackedFieldAlign - 1)) + FieldSize >
StorageUnitSize))
UnpackedFieldOffset =
llvm::alignTo(UnpackedFieldOffset, UnpackedFieldAlign);
else if (ExplicitFieldAlign &&
(MaxFieldAlignmentInBits == 0 ||
ExplicitFieldAlign <= MaxFieldAlignmentInBits) &&
Context.getTargetInfo().useExplicitBitFieldAlignment())
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 (isAIXLayout(Context) && !FieldSize) {
if (FieldPacked)
FieldAlign = 1;
if (!MaxFieldAlignment.isZero()) {
UnpackedFieldAlign =
std::min(UnpackedFieldAlign, MaxFieldAlignmentInBits);
FieldAlign = std::min(FieldAlign, MaxFieldAlignmentInBits);
}
}
if (!UseExternalLayout)
CheckFieldPadding(FieldOffset, UnpaddedFieldOffset, UnpackedFieldOffset,
UnpackedFieldAlign, FieldPacked, D);
if (IsUnion) {
uint64_t RoundedFieldSize;
if (IsMsStruct) {
RoundedFieldSize = (FieldSize ? StorageUnitSize
: Context.getTargetInfo().getCharWidth());
} else {
RoundedFieldSize = roundUpSizeToCharAlignment(FieldSize, Context);
}
setDataSize(std::max(getDataSizeInBits(), RoundedFieldSize));
} else if (IsMsStruct && FieldSize) {
if (!UnfilledBitsInLastUnit) {
setDataSize(FieldOffset + StorageUnitSize);
UnfilledBitsInLastUnit = StorageUnitSize;
}
UnfilledBitsInLastUnit -= FieldSize;
LastBitfieldStorageUnitSize = StorageUnitSize;
} else {
uint64_t NewSizeInBits = FieldOffset + FieldSize;
uint64_t CharAlignment = Context.getTargetInfo().getCharAlign();
setDataSize(llvm::alignTo(NewSizeInBits, CharAlignment));
UnfilledBitsInLastUnit = getDataSizeInBits() - NewSizeInBits;
LastBitfieldStorageUnitSize = 0;
}
setSize(std::max(getSizeInBits(), getDataSizeInBits()));
UnadjustedAlignment =
std::max(UnadjustedAlignment, Context.toCharUnitsFromBits(FieldAlign));
UpdateAlignment(Context.toCharUnitsFromBits(FieldAlign),
Context.toCharUnitsFromBits(UnpackedFieldAlign));
}
void ItaniumRecordLayoutBuilder::LayoutField(const FieldDecl *D,
bool InsertExtraPadding) {
auto *FieldClass = D->getType()->getAsCXXRecordDecl();
bool PotentiallyOverlapping = D->hasAttr<NoUniqueAddressAttr>() && FieldClass;
bool IsOverlappingEmptyField =
PotentiallyOverlapping && FieldClass->isEmpty();
CharUnits FieldOffset =
(IsUnion || IsOverlappingEmptyField) ? CharUnits::Zero() : getDataSize();
const bool DefaultsToAIXPowerAlignment =
Context.getTargetInfo().defaultsToAIXPowerAlignment();
bool FoundFirstNonOverlappingEmptyFieldForAIX = false;
if (DefaultsToAIXPowerAlignment && !HandledFirstNonOverlappingEmptyField) {
assert(FieldOffset == CharUnits::Zero() &&
"The first non-overlapping empty field should have been handled.");
if (!IsOverlappingEmptyField) {
FoundFirstNonOverlappingEmptyFieldForAIX = true;
HandledFirstNonOverlappingEmptyField = !IsUnion;
}
}
if (D->isBitField()) {
LayoutBitField(D);
return;
}
uint64_t UnpaddedFieldOffset = getDataSizeInBits() - UnfilledBitsInLastUnit;
UnfilledBitsInLastUnit = 0;
LastBitfieldStorageUnitSize = 0;
bool FieldPacked = Packed || D->hasAttr<PackedAttr>();
AlignRequirementKind AlignRequirement = AlignRequirementKind::None;
CharUnits FieldSize;
CharUnits FieldAlign;
CharUnits EffectiveFieldSize;
auto setDeclInfo = [&](bool IsIncompleteArrayType) {
auto TI = Context.getTypeInfoInChars(D->getType());
FieldAlign = TI.Align;
EffectiveFieldSize = FieldSize =
IsIncompleteArrayType ? CharUnits::Zero() : TI.Width;
AlignRequirement = TI.AlignRequirement;
};
if (D->getType()->isIncompleteArrayType()) {
setDeclInfo(true );
} else if (const ReferenceType *RT = D->getType()->getAs<ReferenceType>()) {
unsigned AS = Context.getTargetAddressSpace(RT->getPointeeType());
EffectiveFieldSize = FieldSize = Context.toCharUnitsFromBits(
Context.getTargetInfo().getPointerWidth(AS));
FieldAlign = Context.toCharUnitsFromBits(
Context.getTargetInfo().getPointerAlign(AS));
} else {
setDeclInfo(false );
if (PotentiallyOverlapping) {
const ASTRecordLayout &Layout = Context.getASTRecordLayout(FieldClass);
EffectiveFieldSize =
std::max(Layout.getNonVirtualSize(), Layout.getDataSize());
}
if (IsMsStruct) {
QualType T = Context.getBaseElementType(D->getType());
if (const BuiltinType *BTy = T->getAs<BuiltinType>()) {
CharUnits TypeSize = Context.getTypeSizeInChars(BTy);
if (!llvm::isPowerOf2_64(TypeSize.getQuantity())) {
assert(
!Context.getTargetInfo().getTriple().isWindowsMSVCEnvironment() &&
"Non PowerOf2 size in MSVC mode");
if (!Context.getTargetInfo().getTriple().isWindowsGNUEnvironment())
Diag(D->getLocation(), diag::warn_npot_ms_struct);
}
if (TypeSize > FieldAlign &&
llvm::isPowerOf2_64(TypeSize.getQuantity()))
FieldAlign = TypeSize;
}
}
}
auto alignedAttrCanDecreaseAIXAlignment = [AlignRequirement, FieldPacked] {
return AlignRequirement == AlignRequirementKind::RequiredByTypedef ||
(AlignRequirement == AlignRequirementKind::RequiredByRecord &&
FieldPacked);
};
CharUnits PreferredAlign = FieldAlign;
if (DefaultsToAIXPowerAlignment && !alignedAttrCanDecreaseAIXAlignment() &&
(FoundFirstNonOverlappingEmptyFieldForAIX || IsNaturalAlign)) {
auto performBuiltinTypeAlignmentUpgrade = [&](const BuiltinType *BTy) {
if (BTy->getKind() == BuiltinType::Double ||
BTy->getKind() == BuiltinType::LongDouble) {
assert(PreferredAlign == CharUnits::fromQuantity(4) &&
"No need to upgrade the alignment value.");
PreferredAlign = CharUnits::fromQuantity(8);
}
};
const Type *BaseTy = D->getType()->getBaseElementTypeUnsafe();
if (const ComplexType *CTy = BaseTy->getAs<ComplexType>()) {
performBuiltinTypeAlignmentUpgrade(
CTy->getElementType()->castAs<BuiltinType>());
} else if (const BuiltinType *BTy = BaseTy->getAs<BuiltinType>()) {
performBuiltinTypeAlignmentUpgrade(BTy);
} else if (const RecordType *RT = BaseTy->getAs<RecordType>()) {
const RecordDecl *RD = RT->getDecl();
assert(RD && "Expected non-null RecordDecl.");
const ASTRecordLayout &FieldRecord = Context.getASTRecordLayout(RD);
PreferredAlign = FieldRecord.getPreferredAlignment();
}
}
CharUnits UnpackedFieldAlign =
!DefaultsToAIXPowerAlignment ? FieldAlign : PreferredAlign;
CharUnits UnpackedFieldOffset = FieldOffset;
CharUnits OriginalFieldAlign = UnpackedFieldAlign;
if (FieldPacked) {
FieldAlign = CharUnits::One();
PreferredAlign = CharUnits::One();
}
CharUnits MaxAlignmentInChars =
Context.toCharUnitsFromBits(D->getMaxAlignment());
FieldAlign = std::max(FieldAlign, MaxAlignmentInChars);
PreferredAlign = std::max(PreferredAlign, MaxAlignmentInChars);
UnpackedFieldAlign = std::max(UnpackedFieldAlign, MaxAlignmentInChars);
if (!MaxFieldAlignment.isZero()) {
FieldAlign = std::min(FieldAlign, MaxFieldAlignment);
PreferredAlign = std::min(PreferredAlign, MaxFieldAlignment);
UnpackedFieldAlign = std::min(UnpackedFieldAlign, MaxFieldAlignment);
}
CharUnits AlignTo =
!DefaultsToAIXPowerAlignment ? FieldAlign : PreferredAlign;
FieldOffset = FieldOffset.alignTo(AlignTo);
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)) {
if (FieldOffset == CharUnits::Zero() &&
getDataSize() != CharUnits::Zero())
FieldOffset = getDataSize().alignTo(AlignTo);
else
FieldOffset += AlignTo;
}
}
}
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);
EffectiveFieldSize = FieldSize = FieldSize + ExtraSizeForAsan;
}
if (!IsOverlappingEmptyField) {
uint64_t EffectiveFieldSizeInBits = Context.toBits(EffectiveFieldSize);
if (IsUnion)
setDataSize(std::max(getDataSizeInBits(), EffectiveFieldSizeInBits));
else
setDataSize(FieldOffset + EffectiveFieldSize);
PaddedFieldSize = std::max(PaddedFieldSize, FieldOffset + FieldSize);
setSize(std::max(getSizeInBits(), getDataSizeInBits()));
} else {
setSize(std::max(getSizeInBits(),
(uint64_t)Context.toBits(FieldOffset + FieldSize)));
}
UnadjustedAlignment = std::max(UnadjustedAlignment, FieldAlign);
UpdateAlignment(FieldAlign, UnpackedFieldAlign, PreferredAlign);
if (const RecordDecl *RD = D->getParent()) {
if (RD->hasAttr<PackedAttr>() || !MaxFieldAlignment.isZero())
if (FieldAlign < OriginalFieldAlign)
if (D->getType()->isRecordType()) {
if (FieldOffset % OriginalFieldAlign != 0)
Diag(D->getLocation(), diag::warn_unaligned_access)
<< Context.getTypeDeclType(RD) << D->getName() << D->getType();
}
}
}
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());
}
setSize(std::max(getSizeInBits(), (uint64_t)Context.toBits(PaddedFieldSize)));
uint64_t UnpaddedSize = getSizeInBits() - UnfilledBitsInLastUnit;
uint64_t UnpackedSizeInBits =
llvm::alignTo(getSizeInBits(), Context.toBits(UnpackedAlignment));
uint64_t RoundedSize = llvm::alignTo(
getSizeInBits(),
Context.toBits(!Context.getTargetInfo().defaultsToAIXPowerAlignment()
? Alignment
: PreferredAlignment));
if (UseExternalLayout) {
if (InferAlignment && External.Size < RoundedSize) {
Alignment = CharUnits::One();
PreferredAlignment = 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 <= Alignment &&
UnpackedSizeInBits == getSizeInBits() && !HasPackedField)
Diag(D->getLocation(), diag::warn_unnecessary_packed)
<< Context.getTypeDeclType(RD);
}
}
void ItaniumRecordLayoutBuilder::UpdateAlignment(
CharUnits NewAlignment, CharUnits UnpackedNewAlignment,
CharUnits PreferredNewAlignment) {
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;
}
if (PreferredNewAlignment > PreferredAlignment) {
assert(llvm::isPowerOf2_64(PreferredNewAlignment.getQuantity()) &&
"Alignment not a power of 2");
PreferredAlignment = PreferredNewAlignment;
}
}
uint64_t
ItaniumRecordLayoutBuilder::updateExternalFieldOffset(const FieldDecl *Field,
uint64_t ComputedOffset) {
uint64_t ExternalFieldOffset = External.getExternalFieldOffset(Field);
if (InferAlignment && ExternalFieldOffset < ComputedOffset) {
Alignment = CharUnits::One();
PreferredAlignment = 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 && Offset != UnpackedOffset) {
HasPackedField = true;
}
}
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() || MD->isConstexpr())
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>() &&
!Context.getTargetInfo().hasPS4DLLImportExport())
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->isCXX11StandardLayout();
}
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 *RD,
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.endsWithZeroSizedObject();
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) {
auto TInfo =
Context.getTypeInfoInChars(FD->getType()->getUnqualifiedDesugaredType());
ElementInfo Info{TInfo.Width, TInfo.Align};
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.endsWithZeroSizedObject();
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);
if (!UseExternalLayout)
Size = Size.alignTo(RoundingAlignment);
NonVirtualSize = Size;
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;
bool HasPolymorphicBaseClass = false;
for (const CXXBaseSpecifier &Base : RD->bases()) {
const CXXRecordDecl *BaseDecl = Base.getType()->getAsCXXRecordDecl();
HasPolymorphicBaseClass |= BaseDecl->isPolymorphic();
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(RD, BaseDecl, BaseLayout, PreviousBaseLayout);
}
if (RD->isPolymorphic()) {
if (!HasPolymorphicBaseClass)
HasOwnVFPtr = true;
else if (!PrimaryBase) {
for (CXXMethodDecl *M : RD->methods()) {
if (MicrosoftVTableContext::hasVtableSlot(M) &&
M->size_overridden_methods() == 0) {
HasOwnVFPtr = true;
break;
}
}
}
}
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(RD, 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();
}
}
static bool recordUsesEBO(const RecordDecl *RD) {
if (!isa<CXXRecordDecl>(RD))
return false;
if (RD->hasAttr<EmptyBasesAttr>())
return true;
if (auto *LVA = RD->getAttr<LayoutVersionAttr>())
if (LVA->getVersion() <= LangOptions::MSVC2015)
return false;
return false;
}
void MicrosoftRecordLayoutBuilder::layoutNonVirtualBase(
const CXXRecordDecl *RD,
const CXXRecordDecl *BaseDecl,
const ASTRecordLayout &BaseLayout,
const ASTRecordLayout *&PreviousBaseLayout) {
bool MDCUsesEBO = recordUsesEBO(RD);
if (PreviousBaseLayout && PreviousBaseLayout->endsWithZeroSizedObject() &&
BaseLayout.leadsWithZeroSizedBase() && !MDCUsesEBO)
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");
Size = BaseOffset;
}
}
if (!FoundBase) {
if (MDCUsesEBO && BaseDecl->isEmpty()) {
assert(BaseLayout.getNonVirtualSize() == CharUnits::Zero());
BaseOffset = CharUnits::Zero();
} else {
BaseOffset = Size = Size.alignTo(Info.Alignment);
}
}
Bases.insert(std::make_pair(BaseDecl, BaseOffset));
Size += 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);
CharUnits FieldOffset;
if (UseExternalLayout)
FieldOffset =
Context.toCharUnitsFromBits(External.getExternalFieldOffset(FD));
else if (IsUnion)
FieldOffset = CharUnits::Zero();
else
FieldOffset = Size.alignTo(Info.Alignment);
placeFieldAtOffset(FieldOffset);
Size = std::max(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 (!UseExternalLayout && !IsUnion && LastFieldIsNonZeroWidthBitfield &&
CurrentBitfieldSize == Info.Size && Width <= RemainingBitsInField) {
placeFieldAtBitOffset(Context.toBits(Size) - RemainingBitsInField);
RemainingBitsInField -= Width;
return;
}
LastFieldIsNonZeroWidthBitfield = true;
CurrentBitfieldSize = Info.Size;
if (UseExternalLayout) {
auto FieldBitOffset = External.getExternalFieldOffset(FD);
placeFieldAtBitOffset(FieldBitOffset);
auto NewSize = Context.toCharUnitsFromBits(
llvm::alignDown(FieldBitOffset, Context.toBits(Info.Alignment)) +
Context.toBits(Info.Size));
Size = std::max(Size, NewSize);
Alignment = std::max(Alignment, Info.Alignment);
} else 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);
CharUnits FieldStart = VBPtrOffset + PointerInfo.Size;
if (UseExternalLayout) {
if (Size < FieldStart)
Size = FieldStart;
return;
}
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) {
if (FieldOffsets.empty() && Bases.empty())
Size += Offset;
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.contains(BaseDecl);
if ((PreviousBaseLayout && PreviousBaseLayout->endsWithZeroSizedObject() &&
BaseLayout.leadsWithZeroSizedBase() && !recordUsesEBO(RD)) ||
HasVtordisp) {
Size = Size.alignTo(VtorDispAlignment) + VtorDispSize;
Alignment = std::max(VtorDispAlignment, Alignment);
}
ElementInfo Info = getAdjustedElementInfo(BaseLayout);
CharUnits BaseOffset;
if (UseExternalLayout) {
if (!External.getExternalVBaseOffset(BaseDecl, BaseOffset))
BaseOffset = Size;
} else
BaseOffset = Size.alignTo(Info.Alignment);
assert(BaseOffset >= Size && "base offset already allocated");
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()) {
if (!recordUsesEBO(RD) || !cast<CXXRecordDecl>(RD)->isEmpty()) {
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() == MSVtorDispMode::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() == MSVtorDispMode::Never)
return;
assert(RD->getMSVtorDispMode() == MSVtorDispMode::ForVBaseOverride);
llvm::SmallPtrSet<const CXXMethodDecl *, 8> Work;
llvm::SmallPtrSet<const CXXRecordDecl *, 2> BasesWithOverriddenMethods;
for (const CXXMethodDecl *MD : RD->methods())
if (MicrosoftVTableContext::hasVtableSlot(MD) &&
!isa<CXXDestructorDecl>(MD) && !MD->isPure())
Work.insert(MD);
while (!Work.empty()) {
const CXXMethodDecl *MD = *Work.begin();
auto MethodRange = MD->overridden_methods();
if (MethodRange.begin() == MethodRange.end())
BasesWithOverriddenMethods.insert(MD->getParent());
else
Work.insert(MethodRange.begin(), MethodRange.end());
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.Alignment,
Builder.Alignment, Builder.RequiredAlignment, Builder.HasOwnVFPtr,
Builder.HasOwnVFPtr || Builder.PrimaryBase, Builder.VBPtrOffset,
Builder.DataSize, Builder.FieldOffsets, Builder.NonVirtualSize,
Builder.Alignment, 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.Alignment,
Builder.Alignment, Builder.RequiredAlignment, Builder.Size,
Builder.FieldOffsets);
}
} 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.PreferredAlignment, Builder.UnadjustedAlignment,
Builder.Alignment, Builder.HasOwnVFPtr, RD->isDynamicClass(),
CharUnits::fromQuantity(-1), DataSize, Builder.FieldOffsets,
NonVirtualSize, Builder.NonVirtualAlignment,
Builder.PreferredNVAlignment,
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.PreferredAlignment, Builder.UnadjustedAlignment,
Builder.Alignment, Builder.getSize(), Builder.FieldOffsets);
}
}
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 = 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;
}
uint64_t ASTContext::lookupFieldBitOffset(const ObjCInterfaceDecl *OID,
const ObjCImplementationDecl *ID,
const ObjCIvarDecl *Ivar) const {
Ivar = Ivar->getCanonicalDecl();
const ObjCInterfaceDecl *Container = Ivar->getContainingInterface();
const ASTRecordLayout *RL;
if (ID && declaresSameEntity(ID->getClassInterface(), Container))
RL = &getASTObjCImplementationLayout(ID);
else
RL = &getASTObjCInterfaceLayout(Container);
unsigned Index = 0;
for (const ObjCIvarDecl *IVD = Container->all_declared_ivar_begin();
IVD; IVD = IVD->getNextIvar()) {
if (Ivar == IVD)
break;
++Index;
}
assert(Index < RL->getFieldCount() && "Ivar is not inside record layout!");
return RL->getFieldOffset(Index);
}
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->isInvalidDecl() && 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.PreferredAlignment,
Builder.UnadjustedAlignment,
Builder.Alignment, Builder.getDataSize(), Builder.FieldOffsets);
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));
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());
}
llvm::stable_sort(
Bases, [&](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);
}
const QualType &FieldType = C.getLangOpts().DumpRecordLayoutsCanonical
? Field.getType().getCanonicalType()
: Field.getType();
OS << FieldType << ' ' << 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 (C.getTargetInfo().defaultsToAIXPowerAlignment())
OS << ", preferredalign=" << Layout.getPreferredAlignment().getQuantity();
if (CXXRD) {
OS << ",\n";
PrintIndentNoOffset(OS, IndentLevel - 1);
OS << " nvsize=" << Layout.getNonVirtualSize().getQuantity();
OS << ", nvalign=" << Layout.getNonVirtualAlignment().getQuantity();
if (C.getTargetInfo().defaultsToAIXPowerAlignment())
OS << ", preferrednvalign="
<< Layout.getPreferredNVAlignment().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) << "\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";
if (Target->defaultsToAIXPowerAlignment())
OS << " PreferredAlignment:" << toBits(Info.getPreferredAlignment())
<< "\n";
OS << " FieldOffsets: [";
for (unsigned i = 0, e = Info.getFieldCount(); i != e; ++i) {
if (i)
OS << ", ";
OS << Info.getFieldOffset(i);
}
OS << "]>\n";
}