]> granicus.if.org Git - clang/commitdiff
Take #pragma pack into account when laying out structs. Fixes rdar://problem/7095436.
authorAnders Carlsson <andersca@mac.com>
Sat, 8 Aug 2009 19:38:24 +0000 (19:38 +0000)
committerAnders Carlsson <andersca@mac.com>
Sat, 8 Aug 2009 19:38:24 +0000 (19:38 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@78490 91177308-0d34-0410-b5e6-96231b3b80d8

lib/AST/RecordLayoutBuilder.cpp
lib/AST/RecordLayoutBuilder.h
lib/CodeGen/CGRecordLayoutBuilder.cpp
lib/CodeGen/CGRecordLayoutBuilder.h

index 8a18197a5698e538b501aa5919ea1466ce58f7e1..68350daa6c4af85cc0bdb55c21e7d8a749f70f23 100644 (file)
@@ -22,8 +22,8 @@
 using namespace clang;
 
 ASTRecordLayoutBuilder::ASTRecordLayoutBuilder(ASTContext &Ctx) 
-  : Ctx(Ctx), Size(0), Alignment(8), StructPacking(0), NextOffset(0),
-  IsUnion(false), NonVirtualSize(0), NonVirtualAlignment(8) {}
+  : Ctx(Ctx), Size(0), Alignment(8), Packed(false), MaxFieldAlignment(0), 
+  NextOffset(0), IsUnion(false), NonVirtualSize(0), NonVirtualAlignment(8) {}
 
 /// LayoutVtable - Lay out the vtable and set PrimaryBase.
 void ASTRecordLayoutBuilder::LayoutVtable(const CXXRecordDecl *RD) {
@@ -198,10 +198,11 @@ void ASTRecordLayoutBuilder::LayoutBaseNonVirtually(const CXXRecordDecl *RD) {
 void ASTRecordLayoutBuilder::Layout(const RecordDecl *D) {
   IsUnion = D->isUnion();
 
-  if (D->hasAttr<PackedAttr>())
-    StructPacking = 1;
+  Packed = D->hasAttr<PackedAttr>();
+
+  // The #pragma pack attribute specifies the maximum field alignment.
   if (const PragmaPackAttr *PPA = D->getAttr<PragmaPackAttr>())
-    StructPacking = PPA->getAlignment();
+    MaxFieldAlignment = PPA->getAlignment();
   
   if (const AlignedAttr *AA = D->getAttr<AlignedAttr>())
     UpdateAlignment(AA->getAlignment());
@@ -242,8 +243,11 @@ void ASTRecordLayoutBuilder::Layout(const ObjCInterfaceDecl *D,
     NextOffset = Size;
   }
   
-  if (D->hasAttr<PackedAttr>())
-    StructPacking = 1;
+  Packed = D->hasAttr<PackedAttr>();
+  
+  // The #pragma pack attribute specifies the maximum field alignment.
+  if (const PragmaPackAttr *PPA = D->getAttr<PragmaPackAttr>())
+    MaxFieldAlignment = PPA->getAlignment();
   
   if (const AlignedAttr *AA = D->getAttr<AlignedAttr>())
     UpdateAlignment(AA->getAlignment());
@@ -268,15 +272,12 @@ void ASTRecordLayoutBuilder::LayoutFields(const RecordDecl *D) {
 }
 
 void ASTRecordLayoutBuilder::LayoutField(const FieldDecl *D) {
-  unsigned FieldPacking = StructPacking;
+  bool FieldPacked = Packed;
   uint64_t FieldOffset = IsUnion ? 0 : Size;
   uint64_t FieldSize;
   unsigned FieldAlign;
   
-  // FIXME: Should this override struct packing? Probably we want to
-  // take the minimum?
-  if (D->hasAttr<PackedAttr>())
-    FieldPacking = 1;
+  FieldPacked |= D->hasAttr<PackedAttr>();  
   
   if (const Expr *BitWidthExpr = D->getBitWidth()) {
     // TODO: Need to check this algorithm on other targets!
@@ -285,18 +286,17 @@ void ASTRecordLayoutBuilder::LayoutField(const FieldDecl *D) {
     
     std::pair<uint64_t, unsigned> FieldInfo = Ctx.getTypeInfo(D->getType());
     uint64_t TypeSize = FieldInfo.first;
-    
-    // Determine the alignment of this bitfield. The packing
-    // attributes define a maximum and the alignment attribute defines
-    // a minimum.
-    // FIXME: What is the right behavior when the specified alignment
-    // is smaller than the specified packing?
+
     FieldAlign = FieldInfo.second;
-    if (FieldPacking)
-      FieldAlign = std::min(FieldAlign, FieldPacking);
+    
+    if (FieldPacked)
+      FieldAlign = 1;
     if (const AlignedAttr *AA = D->getAttr<AlignedAttr>())
       FieldAlign = std::max(FieldAlign, AA->getAlignment());
-    
+    // The maximum field alignment overrides the aligned attribute.
+    if (MaxFieldAlignment)
+      FieldAlign = std::min(FieldAlign, MaxFieldAlignment);
+
     // Check if we need to add padding to give the field the correct
     // alignment.
     if (FieldSize == 0 || (FieldOffset & (FieldAlign-1)) + FieldSize > TypeSize)
@@ -324,17 +324,13 @@ void ASTRecordLayoutBuilder::LayoutField(const FieldDecl *D) {
       FieldAlign = FieldInfo.second;
     }
     
-    // Determine the alignment of this bitfield. The packing
-    // attributes define a maximum and the alignment attribute defines
-    // a minimum. Additionally, the packing alignment must be at least
-    // a byte for non-bitfields.
-    //
-    // FIXME: What is the right behavior when the specified alignment
-    // is smaller than the specified packing?
-    if (FieldPacking)
-      FieldAlign = std::min(FieldAlign, std::max(8U, FieldPacking));
+    if (FieldPacked)
+      FieldAlign = 8;
     if (const AlignedAttr *AA = D->getAttr<AlignedAttr>())
       FieldAlign = std::max(FieldAlign, AA->getAlignment());
+    // The maximum field alignment overrides the aligned attribute.
+    if (MaxFieldAlignment)
+      FieldAlign = std::min(FieldAlign, MaxFieldAlignment);
     
     // Round up the current record size to the field's alignment boundary.
     FieldOffset = (FieldOffset + (FieldAlign-1)) & ~(FieldAlign-1);
index 75c6b51921145f4b0713ff28bba003aa7c2f037f..488f6662c00f50f40acc1a8aeba037390d42a09a 100644 (file)
@@ -30,7 +30,8 @@ class ASTRecordLayoutBuilder {
   unsigned Alignment;
   llvm::SmallVector<uint64_t, 16> FieldOffsets;
   
-  unsigned StructPacking;
+  bool Packed;
+  unsigned MaxFieldAlignment;
   uint64_t NextOffset;
   bool IsUnion;
   
index d3699bb992d7e80d2d63fd3b700549a2b129792e..4576b808f02e000eed789d798e8a8bcd21ce8c0e 100644 (file)
@@ -27,13 +27,15 @@ using namespace clang;
 using namespace CodeGen;
 
 void CGRecordLayoutBuilder::Layout(const RecordDecl *D) {
+  Alignment = Types.getContext().getASTRecordLayout(D).getAlignment() / 8;
+
   if (D->isUnion()) {
     LayoutUnion(D);
     return;
   }
 
   Packed = D->hasAttr<PackedAttr>();
-
+  
   if (LayoutFields(D))
     return;
   
@@ -115,6 +117,21 @@ bool CGRecordLayoutBuilder::LayoutField(const FieldDecl *D,
   const llvm::Type *Ty = Types.ConvertTypeForMemRecursive(D->getType());
   unsigned TypeAlignment = getTypeAlignment(Ty);
 
+  // If the type alignment is larger then the struct alignment, we must use
+  // a packed struct.
+  if (TypeAlignment > Alignment) {
+    assert(!Packed && "Alignment is wrong even with packed struct!");
+    return false;
+  }
+  
+  if (const RecordType *RT = D->getType()->getAs<RecordType>()) {
+    const RecordDecl *RD = cast<RecordDecl>(RT->getDecl());
+    if (const PragmaPackAttr *PPA = RD->getAttr<PragmaPackAttr>()) {
+      if (PPA->getAlignment() != TypeAlignment * 8 && !Packed)
+        return false;
+    }
+  }
+
   // Round up the field offset to the alignment of the field type.
   uint64_t AlignedNextFieldOffsetInBytes = 
     llvm::RoundUpToAlignment(NextFieldOffsetInBytes, TypeAlignment);
@@ -193,6 +210,7 @@ void CGRecordLayoutBuilder::LayoutUnion(const RecordDecl *D) {
 
 bool CGRecordLayoutBuilder::LayoutFields(const RecordDecl *D) {
   assert(!D->isUnion() && "Can't call LayoutFields on a union!");
+  assert(Alignment && "Did not set alignment!");
   
   const ASTRecordLayout &Layout = Types.getContext().getASTRecordLayout(D);
   
index bca0b5c14290eb4c6af8c860f35d4402c2090ef5..ff551a4f135c2af732a70ab534679f0db133de4c 100644 (file)
@@ -36,6 +36,9 @@ class CGRecordLayoutBuilder {
   /// Packed - Whether the resulting LLVM struct will be packed or not.
   bool Packed;
 
+  /// Alignment - Contains the alignment of the RecordDecl.
+  unsigned Alignment;
+
   /// AlignmentAsLLVMStruct - Will contain the maximum alignment of all the
   /// LLVM types.
   unsigned AlignmentAsLLVMStruct;
@@ -69,7 +72,7 @@ class CGRecordLayoutBuilder {
   llvm::SmallVector<LLVMBitFieldInfo, 16> LLVMBitFields;
   
   CGRecordLayoutBuilder(CodeGenTypes &Types) 
-    : Types(Types), Packed(false), AlignmentAsLLVMStruct(1)
+    : Types(Types), Packed(false), Alignment(0), AlignmentAsLLVMStruct(1)
     , BitsAvailableInLastField(0), NextFieldOffsetInBytes(0) { }
 
   /// Layout - Will layout a RecordDecl.