diff options
author | John McCall <rjmccall@apple.com> | 2011-01-20 07:57:12 +0000 |
---|---|---|
committer | John McCall <rjmccall@apple.com> | 2011-01-20 07:57:12 +0000 |
commit | ba4f5d5754c8291690d01ca9581926673d69b24c (patch) | |
tree | ba5c8463382801f9f2f55641e03278da8c565d69 /lib/AST | |
parent | 9eefa229dfb71400a6bbee326420a7f0e2e91f1f (diff) |
Fix the computation of alignment for fields of packed+aligned structs.
Part of the fix for PR8413.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@123904 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/AST')
-rw-r--r-- | lib/AST/ASTContext.cpp | 34 | ||||
-rw-r--r-- | lib/AST/Decl.cpp | 19 | ||||
-rw-r--r-- | lib/AST/ExprConstant.cpp | 9 |
3 files changed, 48 insertions, 14 deletions
diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index d1ac00871a..043d56a98d 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -590,8 +590,11 @@ CharUnits ASTContext::getDeclAlign(const Decl *D, bool RefAsPointee) const { } } + // If we're using the align attribute only, just ignore everything + // else about the declaration and its type. if (UseAlignAttrOnly) { - // ignore type of value + // do nothing + } else if (const ValueDecl *VD = dyn_cast<ValueDecl>(D)) { QualType T = VD->getType(); if (const ReferenceType* RT = T->getAs<ReferenceType>()) { @@ -617,11 +620,30 @@ CharUnits ASTContext::getDeclAlign(const Decl *D, bool RefAsPointee) const { } Align = std::max(Align, getPreferredTypeAlign(T.getTypePtr())); } - if (const FieldDecl *FD = dyn_cast<FieldDecl>(VD)) { - // In the case of a field in a packed struct, we want the minimum - // of the alignment of the field and the alignment of the struct. - Align = std::min(Align, - getPreferredTypeAlign(FD->getParent()->getTypeForDecl())); + + // Fields can be subject to extra alignment constraints, like if + // the field is packed, the struct is packed, or the struct has a + // a max-field-alignment constraint (#pragma pack). So calculate + // the actual alignment of the field within the struct, and then + // (as we're expected to) constrain that by the alignment of the type. + if (const FieldDecl *field = dyn_cast<FieldDecl>(VD)) { + // So calculate the alignment of the field. + const ASTRecordLayout &layout = getASTRecordLayout(field->getParent()); + + // Start with the record's overall alignment. + unsigned fieldAlign = layout.getAlignment(); + + // Use the GCD of that and the offset within the record. + uint64_t offset = layout.getFieldOffset(field->getFieldIndex()); + if (offset > 0) { + // Alignment is always a power of 2, so the GCD will be a power of 2, + // which means we get to do this crazy thing instead of Euclid's. + uint64_t lowBitOfOffset = offset & (~offset + 1); + if (lowBitOfOffset < fieldAlign) + fieldAlign = static_cast<unsigned>(lowBitOfOffset); + } + + Align = std::min(Align, fieldAlign); } } diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 9e2e8763f8..b1da849ae1 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -1881,6 +1881,25 @@ bool FieldDecl::isAnonymousStructOrUnion() const { return false; } +unsigned FieldDecl::getFieldIndex() const { + if (CachedFieldIndex) return CachedFieldIndex - 1; + + unsigned index = 0; + RecordDecl::field_iterator + i = getParent()->field_begin(), e = getParent()->field_end(); + while (true) { + assert(i != e && "failed to find field in parent!"); + if (*i == this) + break; + + ++i; + ++index; + } + + CachedFieldIndex = index + 1; + return index; +} + //===----------------------------------------------------------------------===// // TagDecl Implementation //===----------------------------------------------------------------------===// diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index f262a4acff..afce24e625 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -1565,14 +1565,7 @@ bool IntExprEvaluator::VisitOffsetOfExpr(const OffsetOfExpr *E) { return false; RecordDecl *RD = RT->getDecl(); const ASTRecordLayout &RL = Info.Ctx.getASTRecordLayout(RD); - unsigned i = 0; - // FIXME: It would be nice if we didn't have to loop here! - for (RecordDecl::field_iterator Field = RD->field_begin(), - FieldEnd = RD->field_end(); - Field != FieldEnd; (void)++Field, ++i) { - if (*Field == MemberDecl) - break; - } + unsigned i = MemberDecl->getFieldIndex(); assert(i < RL.getFieldCount() && "offsetof field in wrong type"); Result += Info.Ctx.toCharUnitsFromBits(RL.getFieldOffset(i)); CurrentType = MemberDecl->getType().getNonReferenceType(); |