mirror of
https://github.com/intel/llvm.git
synced 2026-02-01 08:56:15 +08:00
MS ABI: Consider alignment attributes on typedefs for layout
The MS ABI has a notion of 'required alignment' for fields; this alignment supercedes pragma pack directives. MSVC takes into account alignment attributes on typedefs when determining whether or not a field has a certain required alignment. Do the same in clang by tracking whether or not we saw such an attribute when calculating the type's bitwidth and alignment. This fixes PR20418. Reviewers: rnk Differential Revision: http://reviews.llvm.org/D4714 llvm-svn: 214274
This commit is contained in:
@@ -74,6 +74,15 @@ namespace clang {
|
||||
class FullComment;
|
||||
}
|
||||
|
||||
struct TypeInfo {
|
||||
uint64_t Width;
|
||||
unsigned Align;
|
||||
bool AlignIsRequired : 1;
|
||||
TypeInfo() : Width(0), Align(0), AlignIsRequired(false) {}
|
||||
TypeInfo(uint64_t Width, unsigned Align, bool AlignIsRequired)
|
||||
: Width(Width), Align(Align), AlignIsRequired(AlignIsRequired) {}
|
||||
};
|
||||
|
||||
/// \brief Holds long-lived AST nodes (such as types and decls) that can be
|
||||
/// referred to throughout the semantic analysis of a file.
|
||||
class ASTContext : public RefCountedBase<ASTContext> {
|
||||
@@ -144,8 +153,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
|
||||
ObjCLayouts;
|
||||
|
||||
/// \brief A cache from types to size and alignment information.
|
||||
typedef llvm::DenseMap<const Type*,
|
||||
std::pair<uint64_t, unsigned> > TypeInfoMap;
|
||||
typedef llvm::DenseMap<const Type *, struct TypeInfo> TypeInfoMap;
|
||||
mutable TypeInfoMap MemoizedTypeInfo;
|
||||
|
||||
/// \brief A cache mapping from CXXRecordDecls to key functions.
|
||||
@@ -1581,7 +1589,7 @@ public:
|
||||
|
||||
private:
|
||||
CanQualType getFromTargetType(unsigned Type) const;
|
||||
std::pair<uint64_t, unsigned> getTypeInfoImpl(const Type *T) const;
|
||||
TypeInfo getTypeInfoImpl(const Type *T) const;
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Type Predicates.
|
||||
@@ -1614,18 +1622,12 @@ public:
|
||||
const llvm::fltSemantics &getFloatTypeSemantics(QualType T) const;
|
||||
|
||||
/// \brief Get the size and alignment of the specified complete type in bits.
|
||||
std::pair<uint64_t, unsigned> getTypeInfo(const Type *T) const;
|
||||
std::pair<uint64_t, unsigned> getTypeInfo(QualType T) const {
|
||||
return getTypeInfo(T.getTypePtr());
|
||||
}
|
||||
TypeInfo getTypeInfo(const Type *T) const;
|
||||
TypeInfo getTypeInfo(QualType T) const { return getTypeInfo(T.getTypePtr()); }
|
||||
|
||||
/// \brief Return the size of the specified (complete) type \p T, in bits.
|
||||
uint64_t getTypeSize(QualType T) const {
|
||||
return getTypeInfo(T).first;
|
||||
}
|
||||
uint64_t getTypeSize(const Type *T) const {
|
||||
return getTypeInfo(T).first;
|
||||
}
|
||||
uint64_t getTypeSize(QualType T) const { return getTypeInfo(T).Width; }
|
||||
uint64_t getTypeSize(const Type *T) const { return getTypeInfo(T).Width; }
|
||||
|
||||
/// \brief Return the size of the character type, in bits.
|
||||
uint64_t getCharWidth() const {
|
||||
@@ -1645,12 +1647,8 @@ public:
|
||||
|
||||
/// \brief Return the ABI-specified alignment of a (complete) type \p T, in
|
||||
/// bits.
|
||||
unsigned getTypeAlign(QualType T) const {
|
||||
return getTypeInfo(T).second;
|
||||
}
|
||||
unsigned getTypeAlign(const Type *T) const {
|
||||
return getTypeInfo(T).second;
|
||||
}
|
||||
unsigned getTypeAlign(QualType T) const { return getTypeInfo(T).Align; }
|
||||
unsigned getTypeAlign(const Type *T) const { return getTypeInfo(T).Align; }
|
||||
|
||||
/// \brief Return the ABI-specified alignment of a (complete) type \p T, in
|
||||
/// characters.
|
||||
@@ -1664,6 +1662,11 @@ public:
|
||||
std::pair<CharUnits, CharUnits> getTypeInfoInChars(const Type *T) const;
|
||||
std::pair<CharUnits, CharUnits> getTypeInfoInChars(QualType T) const;
|
||||
|
||||
/// \brief Determine if the alignment the type has was required using an
|
||||
/// alignment attribute.
|
||||
bool isAlignmentRequired(const Type *T) const;
|
||||
bool isAlignmentRequired(QualType T) const;
|
||||
|
||||
/// \brief Return the "preferred" alignment of the specified type \p T for
|
||||
/// the current target, in bits.
|
||||
///
|
||||
|
||||
@@ -1413,9 +1413,9 @@ std::pair<CharUnits, CharUnits>
|
||||
ASTContext::getTypeInfoInChars(const Type *T) const {
|
||||
if (const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(T))
|
||||
return getConstantArrayInfoInChars(*this, CAT);
|
||||
std::pair<uint64_t, unsigned> Info = getTypeInfo(T);
|
||||
return std::make_pair(toCharUnitsFromBits(Info.first),
|
||||
toCharUnitsFromBits(Info.second));
|
||||
TypeInfo Info = getTypeInfo(T);
|
||||
return std::make_pair(toCharUnitsFromBits(Info.Width),
|
||||
toCharUnitsFromBits(Info.Align));
|
||||
}
|
||||
|
||||
std::pair<CharUnits, CharUnits>
|
||||
@@ -1423,14 +1423,20 @@ ASTContext::getTypeInfoInChars(QualType T) const {
|
||||
return getTypeInfoInChars(T.getTypePtr());
|
||||
}
|
||||
|
||||
std::pair<uint64_t, unsigned> ASTContext::getTypeInfo(const Type *T) const {
|
||||
TypeInfoMap::iterator it = MemoizedTypeInfo.find(T);
|
||||
if (it != MemoizedTypeInfo.end())
|
||||
return it->second;
|
||||
bool ASTContext::isAlignmentRequired(const Type *T) const {
|
||||
return getTypeInfo(T).AlignIsRequired;
|
||||
}
|
||||
|
||||
std::pair<uint64_t, unsigned> Info = getTypeInfoImpl(T);
|
||||
MemoizedTypeInfo.insert(std::make_pair(T, Info));
|
||||
return Info;
|
||||
bool ASTContext::isAlignmentRequired(QualType T) const {
|
||||
return isAlignmentRequired(T.getTypePtr());
|
||||
}
|
||||
|
||||
TypeInfo ASTContext::getTypeInfo(const Type *T) const {
|
||||
TypeInfo &TI = MemoizedTypeInfo[T];
|
||||
if (!TI.Align)
|
||||
TI = getTypeInfoImpl(T);
|
||||
|
||||
return TI;
|
||||
}
|
||||
|
||||
/// getTypeInfoImpl - Return the size of the specified type, in bits. This
|
||||
@@ -1439,10 +1445,10 @@ std::pair<uint64_t, unsigned> ASTContext::getTypeInfo(const Type *T) const {
|
||||
/// FIXME: Pointers into different addr spaces could have different sizes and
|
||||
/// alignment requirements: getPointerInfo should take an AddrSpace, this
|
||||
/// should take a QualType, &c.
|
||||
std::pair<uint64_t, unsigned>
|
||||
ASTContext::getTypeInfoImpl(const Type *T) const {
|
||||
uint64_t Width=0;
|
||||
unsigned Align=8;
|
||||
TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const {
|
||||
uint64_t Width = 0;
|
||||
unsigned Align = 8;
|
||||
bool AlignIsRequired = false;
|
||||
switch (T->getTypeClass()) {
|
||||
#define TYPE(Class, Base)
|
||||
#define ABSTRACT_TYPE(Class, Base)
|
||||
@@ -1471,12 +1477,12 @@ ASTContext::getTypeInfoImpl(const Type *T) const {
|
||||
case Type::ConstantArray: {
|
||||
const ConstantArrayType *CAT = cast<ConstantArrayType>(T);
|
||||
|
||||
std::pair<uint64_t, unsigned> EltInfo = getTypeInfo(CAT->getElementType());
|
||||
TypeInfo EltInfo = getTypeInfo(CAT->getElementType());
|
||||
uint64_t Size = CAT->getSize().getZExtValue();
|
||||
assert((Size == 0 || EltInfo.first <= (uint64_t)(-1)/Size) &&
|
||||
assert((Size == 0 || EltInfo.Width <= (uint64_t)(-1) / Size) &&
|
||||
"Overflow in array type bit size evaluation");
|
||||
Width = EltInfo.first*Size;
|
||||
Align = EltInfo.second;
|
||||
Width = EltInfo.Width * Size;
|
||||
Align = EltInfo.Align;
|
||||
if (!getTargetInfo().getCXXABI().isMicrosoft() ||
|
||||
getTargetInfo().getPointerWidth(0) == 64)
|
||||
Width = llvm::RoundUpToAlignment(Width, Align);
|
||||
@@ -1485,8 +1491,8 @@ ASTContext::getTypeInfoImpl(const Type *T) const {
|
||||
case Type::ExtVector:
|
||||
case Type::Vector: {
|
||||
const VectorType *VT = cast<VectorType>(T);
|
||||
std::pair<uint64_t, unsigned> EltInfo = getTypeInfo(VT->getElementType());
|
||||
Width = EltInfo.first*VT->getNumElements();
|
||||
TypeInfo EltInfo = getTypeInfo(VT->getElementType());
|
||||
Width = EltInfo.Width * VT->getNumElements();
|
||||
Align = Width;
|
||||
// If the alignment is not a power of 2, round up to the next power of 2.
|
||||
// This happens for non-power-of-2 length vectors.
|
||||
@@ -1638,10 +1644,9 @@ ASTContext::getTypeInfoImpl(const Type *T) const {
|
||||
case Type::Complex: {
|
||||
// Complex types have the same alignment as their elements, but twice the
|
||||
// size.
|
||||
std::pair<uint64_t, unsigned> EltInfo =
|
||||
getTypeInfo(cast<ComplexType>(T)->getElementType());
|
||||
Width = EltInfo.first*2;
|
||||
Align = EltInfo.second;
|
||||
TypeInfo EltInfo = getTypeInfo(cast<ComplexType>(T)->getElementType());
|
||||
Width = EltInfo.Width * 2;
|
||||
Align = EltInfo.Align;
|
||||
break;
|
||||
}
|
||||
case Type::ObjCObject:
|
||||
@@ -1692,16 +1697,16 @@ ASTContext::getTypeInfoImpl(const Type *T) const {
|
||||
|
||||
case Type::Typedef: {
|
||||
const TypedefNameDecl *Typedef = cast<TypedefType>(T)->getDecl();
|
||||
std::pair<uint64_t, unsigned> Info
|
||||
= getTypeInfo(Typedef->getUnderlyingType().getTypePtr());
|
||||
TypeInfo Info = getTypeInfo(Typedef->getUnderlyingType().getTypePtr());
|
||||
// If the typedef has an aligned attribute on it, it overrides any computed
|
||||
// alignment we have. This violates the GCC documentation (which says that
|
||||
// attribute(aligned) can only round up) but matches its implementation.
|
||||
if (unsigned AttrAlign = Typedef->getMaxAlignment())
|
||||
if (unsigned AttrAlign = Typedef->getMaxAlignment()) {
|
||||
Align = AttrAlign;
|
||||
else
|
||||
Align = Info.second;
|
||||
Width = Info.first;
|
||||
AlignIsRequired = true;
|
||||
} else
|
||||
Align = Info.Align;
|
||||
Width = Info.Width;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1714,10 +1719,9 @@ ASTContext::getTypeInfoImpl(const Type *T) const {
|
||||
|
||||
case Type::Atomic: {
|
||||
// Start with the base type information.
|
||||
std::pair<uint64_t, unsigned> Info
|
||||
= getTypeInfo(cast<AtomicType>(T)->getValueType());
|
||||
Width = Info.first;
|
||||
Align = Info.second;
|
||||
TypeInfo Info = getTypeInfo(cast<AtomicType>(T)->getValueType());
|
||||
Width = Info.Width;
|
||||
Align = Info.Align;
|
||||
|
||||
// If the size of the type doesn't exceed the platform's max
|
||||
// atomic promotion width, make the size and alignment more
|
||||
@@ -1735,7 +1739,7 @@ ASTContext::getTypeInfoImpl(const Type *T) const {
|
||||
}
|
||||
|
||||
assert(llvm::isPowerOf2_32(Align) && "Alignment must be power of 2");
|
||||
return std::make_pair(Width, Align);
|
||||
return TypeInfo(Width, Align, AlignIsRequired);
|
||||
}
|
||||
|
||||
/// toCharUnitsFromBits - Convert a size in bits to a size in characters.
|
||||
@@ -1771,13 +1775,12 @@ CharUnits ASTContext::getTypeAlignInChars(const Type *T) const {
|
||||
/// alignment in cases where it is beneficial for performance to overalign
|
||||
/// a data type.
|
||||
unsigned ASTContext::getPreferredTypeAlign(const Type *T) const {
|
||||
unsigned ABIAlign = getTypeAlign(T);
|
||||
TypeInfo TI = getTypeInfo(T);
|
||||
unsigned ABIAlign = TI.Align;
|
||||
|
||||
if (Target->getTriple().getArch() == llvm::Triple::xcore)
|
||||
return ABIAlign; // Never overalign on XCore.
|
||||
|
||||
const TypedefType *TT = T->getAs<TypedefType>();
|
||||
|
||||
// Double and long long should be naturally aligned if possible.
|
||||
T = T->getBaseElementTypeUnsafe();
|
||||
if (const ComplexType *CT = T->getAs<ComplexType>())
|
||||
@@ -1787,7 +1790,7 @@ unsigned ASTContext::getPreferredTypeAlign(const Type *T) const {
|
||||
T->isSpecificBuiltinType(BuiltinType::ULongLong))
|
||||
// Don't increase the alignment if an alignment attribute was specified on a
|
||||
// typedef declaration.
|
||||
if (!TT || !TT->getDecl()->getMaxAlignment())
|
||||
if (!TI.AlignIsRequired)
|
||||
return std::max(ABIAlign, (unsigned)getTypeSize(T));
|
||||
|
||||
return ABIAlign;
|
||||
|
||||
@@ -1413,9 +1413,9 @@ void RecordLayoutBuilder::LayoutWideBitField(uint64_t FieldSize,
|
||||
void RecordLayoutBuilder::LayoutBitField(const FieldDecl *D) {
|
||||
bool FieldPacked = Packed || D->hasAttr<PackedAttr>();
|
||||
uint64_t FieldSize = D->getBitWidthValue(Context);
|
||||
std::pair<uint64_t, unsigned> FieldInfo = Context.getTypeInfo(D->getType());
|
||||
uint64_t TypeSize = FieldInfo.first;
|
||||
unsigned FieldAlign = FieldInfo.second;
|
||||
TypeInfo FieldInfo = Context.getTypeInfo(D->getType());
|
||||
uint64_t TypeSize = FieldInfo.Width;
|
||||
unsigned FieldAlign = FieldInfo.Align;
|
||||
|
||||
// UnfilledBitsInLastUnit is the difference between the end of the
|
||||
// last allocated bitfield (i.e. the first bit offset available for
|
||||
@@ -2260,12 +2260,18 @@ MicrosoftRecordLayoutBuilder::getAdjustedElementInfo(
|
||||
MicrosoftRecordLayoutBuilder::ElementInfo
|
||||
MicrosoftRecordLayoutBuilder::getAdjustedElementInfo(
|
||||
const FieldDecl *FD) {
|
||||
// Get the alignment of the field type's natural alignment, ignore any
|
||||
// alignment attributes.
|
||||
ElementInfo Info;
|
||||
std::tie(Info.Size, Info.Alignment) =
|
||||
Context.getTypeInfoInChars(FD->getType());
|
||||
// Respect align attributes.
|
||||
CharUnits FieldRequiredAlignment =
|
||||
Context.getTypeInfoInChars(FD->getType()->getUnqualifiedDesugaredType());
|
||||
// Respect align attributes on the field.
|
||||
CharUnits FieldRequiredAlignment =
|
||||
Context.toCharUnitsFromBits(FD->getMaxAlignment());
|
||||
// Respect align attributes on the type.
|
||||
if (Context.isAlignmentRequired(FD->getType()))
|
||||
FieldRequiredAlignment = std::max(
|
||||
Context.getTypeAlignInChars(FD->getType()), FieldRequiredAlignment);
|
||||
// Respect attributes applied to subobjects of the field.
|
||||
if (FD->isBitField())
|
||||
// For some reason __declspec align impacts alignment rather than required
|
||||
|
||||
@@ -46,17 +46,21 @@ namespace {
|
||||
|
||||
ASTContext &C = CGF.getContext();
|
||||
|
||||
uint64_t valueAlignInBits;
|
||||
std::tie(ValueSizeInBits, valueAlignInBits) = C.getTypeInfo(ValueTy);
|
||||
uint64_t ValueAlignInBits;
|
||||
uint64_t AtomicAlignInBits;
|
||||
TypeInfo ValueTI = C.getTypeInfo(ValueTy);
|
||||
ValueSizeInBits = ValueTI.Width;
|
||||
ValueAlignInBits = ValueTI.Align;
|
||||
|
||||
uint64_t atomicAlignInBits;
|
||||
std::tie(AtomicSizeInBits, atomicAlignInBits) = C.getTypeInfo(AtomicTy);
|
||||
TypeInfo AtomicTI = C.getTypeInfo(AtomicTy);
|
||||
AtomicSizeInBits = AtomicTI.Width;
|
||||
AtomicAlignInBits = AtomicTI.Align;
|
||||
|
||||
assert(ValueSizeInBits <= AtomicSizeInBits);
|
||||
assert(valueAlignInBits <= atomicAlignInBits);
|
||||
assert(ValueAlignInBits <= AtomicAlignInBits);
|
||||
|
||||
AtomicAlign = C.toCharUnitsFromBits(atomicAlignInBits);
|
||||
ValueAlign = C.toCharUnitsFromBits(valueAlignInBits);
|
||||
AtomicAlign = C.toCharUnitsFromBits(AtomicAlignInBits);
|
||||
ValueAlign = C.toCharUnitsFromBits(ValueAlignInBits);
|
||||
if (lvalue.getAlignment().isZero())
|
||||
lvalue.setAlignment(AtomicAlign);
|
||||
|
||||
|
||||
@@ -803,13 +803,15 @@ llvm::DIType CGDebugInfo::createFieldType(StringRef name,
|
||||
llvm::DIFile file = getOrCreateFile(loc);
|
||||
unsigned line = getLineNumber(loc);
|
||||
|
||||
uint64_t sizeInBits = 0;
|
||||
unsigned alignInBits = 0;
|
||||
uint64_t SizeInBits = 0;
|
||||
unsigned AlignInBits = 0;
|
||||
if (!type->isIncompleteArrayType()) {
|
||||
std::tie(sizeInBits, alignInBits) = CGM.getContext().getTypeInfo(type);
|
||||
TypeInfo TI = CGM.getContext().getTypeInfo(type);
|
||||
SizeInBits = TI.Width;
|
||||
AlignInBits = TI.Align;
|
||||
|
||||
if (sizeInBitsOverride)
|
||||
sizeInBits = sizeInBitsOverride;
|
||||
SizeInBits = sizeInBitsOverride;
|
||||
}
|
||||
|
||||
unsigned flags = 0;
|
||||
@@ -818,8 +820,8 @@ llvm::DIType CGDebugInfo::createFieldType(StringRef name,
|
||||
else if (AS == clang::AS_protected)
|
||||
flags |= llvm::DIDescriptor::FlagProtected;
|
||||
|
||||
return DBuilder.createMemberType(scope, name, file, line, sizeInBits,
|
||||
alignInBits, offsetInBits, flags, debugType);
|
||||
return DBuilder.createMemberType(scope, name, file, line, SizeInBits,
|
||||
AlignInBits, offsetInBits, flags, debugType);
|
||||
}
|
||||
|
||||
/// CollectRecordLambdaFields - Helper for CollectRecordFields.
|
||||
@@ -3030,15 +3032,15 @@ void CGDebugInfo::EmitDeclareOfBlockLiteralArgVariable(const CGBlockInfo &block,
|
||||
|
||||
llvm::DIType fieldType;
|
||||
if (capture->isByRef()) {
|
||||
std::pair<uint64_t,unsigned> ptrInfo = C.getTypeInfo(C.VoidPtrTy);
|
||||
TypeInfo PtrInfo = C.getTypeInfo(C.VoidPtrTy);
|
||||
|
||||
// FIXME: this creates a second copy of this type!
|
||||
uint64_t xoffset;
|
||||
fieldType = EmitTypeForVarWithBlocksAttr(variable, &xoffset);
|
||||
fieldType = DBuilder.createPointerType(fieldType, ptrInfo.first);
|
||||
fieldType = DBuilder.createMemberType(tunit, name, tunit, line,
|
||||
ptrInfo.first, ptrInfo.second,
|
||||
offsetInBits, 0, fieldType);
|
||||
fieldType = DBuilder.createPointerType(fieldType, PtrInfo.Width);
|
||||
fieldType =
|
||||
DBuilder.createMemberType(tunit, name, tunit, line, PtrInfo.Width,
|
||||
PtrInfo.Align, offsetInBits, 0, fieldType);
|
||||
} else {
|
||||
fieldType = createFieldType(name, variable->getType(), 0,
|
||||
loc, AS_public, offsetInBits, tunit, tunit);
|
||||
|
||||
@@ -2107,7 +2107,12 @@ static bool matchTypes(ASTContext &Context, Sema::MethodMatchStrategy strategy,
|
||||
// validate the basic, low-level compatibility of the two types.
|
||||
|
||||
// As a minimum, require the sizes and alignments to match.
|
||||
if (Context.getTypeInfo(left) != Context.getTypeInfo(right))
|
||||
TypeInfo LeftTI = Context.getTypeInfo(left);
|
||||
TypeInfo RightTI = Context.getTypeInfo(right);
|
||||
if (LeftTI.Width != RightTI.Width)
|
||||
return false;
|
||||
|
||||
if (LeftTI.Align != RightTI.Align)
|
||||
return false;
|
||||
|
||||
// Consider all the kinds of non-dependent canonical types:
|
||||
@@ -2159,7 +2164,13 @@ static bool tryMatchRecordTypes(ASTContext &Context,
|
||||
return false;
|
||||
|
||||
// Require size and alignment to match.
|
||||
if (Context.getTypeInfo(lt) != Context.getTypeInfo(rt)) return false;
|
||||
TypeInfo LeftTI = Context.getTypeInfo(lt);
|
||||
TypeInfo RightTI = Context.getTypeInfo(rt);
|
||||
if (LeftTI.Width != RightTI.Width)
|
||||
return false;
|
||||
|
||||
if (LeftTI.Align != RightTI.Align)
|
||||
return false;
|
||||
|
||||
// Require fields to match.
|
||||
RecordDecl::field_iterator li = left->field_begin(), le = left->field_end();
|
||||
|
||||
@@ -652,7 +652,94 @@ struct OD : OC {};
|
||||
// CHECK-X64-NEXT: | [sizeof=12, align=1
|
||||
// CHECK-X64-NEXT: | nvsize=8, nvalign=1]
|
||||
|
||||
struct __declspec(align(4)) PA {
|
||||
int c;
|
||||
};
|
||||
|
||||
typedef __declspec(align(8)) PA PB;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct PC {
|
||||
char a;
|
||||
PB x;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
// CHECK: *** Dumping AST Record Layout
|
||||
// CHECK: 0 | struct PC
|
||||
// CHECK-NEXT: 0 | char a
|
||||
// CHECK-NEXT: 8 | struct PA x
|
||||
// CHECK-NEXT: 8 | int c
|
||||
// CHECK-NEXT: | [sizeof=4, align=4
|
||||
// CHECK-NEXT: | nvsize=4, nvalign=4]
|
||||
// CHECK-NEXT: | [sizeof=16, align=8
|
||||
// CHECK-NEXT: | nvsize=12, nvalign=8]
|
||||
// CHECK-X64: *** Dumping AST Record Layout
|
||||
// CHECK-X64: 0 | struct PC
|
||||
// CHECK-X64-NEXT: 0 | char a
|
||||
// CHECK-X64-NEXT: 8 | struct PA x
|
||||
// CHECK-X64-NEXT: 8 | int c
|
||||
// CHECK-X64-NEXT: | [sizeof=4, align=4
|
||||
// CHECK-X64-NEXT: | nvsize=4, nvalign=4]
|
||||
// CHECK-X64-NEXT: | [sizeof=16, align=8
|
||||
// CHECK-X64-NEXT: | nvsize=12, nvalign=8]
|
||||
|
||||
typedef int __declspec(align(2)) QA;
|
||||
#pragma pack(push, 1)
|
||||
struct QB {
|
||||
char a;
|
||||
QA b;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
// CHECK: *** Dumping AST Record Layout
|
||||
// CHECK-NEXT: 0 | struct QB
|
||||
// CHECK-NEXT: 0 | char a
|
||||
// CHECK-NEXT: 2 | QA b
|
||||
// CHECK-NEXT: | [sizeof=6, align=2
|
||||
// CHECK-NEXT: | nvsize=6, nvalign=2]
|
||||
// CHECK-X64: *** Dumping AST Record Layout
|
||||
// CHECK-X64-NEXT: 0 | struct QB
|
||||
// CHECK-X64-NEXT: 0 | char a
|
||||
// CHECK-X64-NEXT: 2 | QA b
|
||||
// CHECK-X64-NEXT: | [sizeof=6, align=2
|
||||
// CHECK-X64-NEXT: | nvsize=6, nvalign=2]
|
||||
|
||||
struct QC {
|
||||
char a;
|
||||
QA b;
|
||||
};
|
||||
|
||||
// CHECK: *** Dumping AST Record Layout
|
||||
// CHECK-NEXT: 0 | struct QC
|
||||
// CHECK-NEXT: 0 | char a
|
||||
// CHECK-NEXT: 4 | QA b
|
||||
// CHECK-NEXT: | [sizeof=8, align=4
|
||||
// CHECK-NEXT: | nvsize=8, nvalign=4]
|
||||
// CHECK-X64: *** Dumping AST Record Layout
|
||||
// CHECK-X64-NEXT: 0 | struct QC
|
||||
// CHECK-X64-NEXT: 0 | char a
|
||||
// CHECK-X64-NEXT: 4 | QA b
|
||||
// CHECK-X64-NEXT: | [sizeof=8, align=4
|
||||
// CHECK-X64-NEXT: | nvsize=8, nvalign=4]
|
||||
|
||||
struct QD {
|
||||
char a;
|
||||
QA b : 3;
|
||||
};
|
||||
|
||||
// CHECK: *** Dumping AST Record Layout
|
||||
// CHECK-NEXT: 0 | struct QD
|
||||
// CHECK-NEXT: 0 | char a
|
||||
// CHECK-NEXT: 4 | QA b
|
||||
// CHECK-NEXT: | [sizeof=8, align=4
|
||||
// CHECK-NEXT: | nvsize=8, nvalign=4]
|
||||
// CHECK-X64: *** Dumping AST Record Layout
|
||||
// CHECK-X64-NEXT: 0 | struct QD
|
||||
// CHECK-X64-NEXT: 0 | char a
|
||||
// CHECK-X64-NEXT: 4 | QA b
|
||||
// CHECK-X64-NEXT: | [sizeof=8, align=4
|
||||
// CHECK-X64-NEXT: | nvsize=8, nvalign=4]
|
||||
|
||||
int a[
|
||||
sizeof(X)+
|
||||
@@ -680,4 +767,8 @@ sizeof(RC)+
|
||||
sizeof(RE)+
|
||||
sizeof(ND)+
|
||||
sizeof(OD)+
|
||||
sizeof(PC)+
|
||||
sizeof(QB)+
|
||||
sizeof(QC)+
|
||||
sizeof(QD)+
|
||||
0];
|
||||
|
||||
Reference in New Issue
Block a user