]> granicus.if.org Git - clang/commitdiff
Fix the computation of alignment for fields of packed+aligned structs.
authorJohn McCall <rjmccall@apple.com>
Thu, 20 Jan 2011 07:57:12 +0000 (07:57 +0000)
committerJohn McCall <rjmccall@apple.com>
Thu, 20 Jan 2011 07:57:12 +0000 (07:57 +0000)
Part of the fix for PR8413.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@123904 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/Decl.h
lib/AST/ASTContext.cpp
lib/AST/Decl.cpp
lib/AST/ExprConstant.cpp
test/CodeGen/packed-structure.c

index fe4b71ccc1dc3fcd18e6d3abd7685ea31fedd7fa..25424ce85327d706f291b28a3e14c67d7ff0e5e3 100644 (file)
@@ -1728,12 +1728,15 @@ public:
 class FieldDecl : public DeclaratorDecl {
   // FIXME: This can be packed into the bitfields in Decl.
   bool Mutable : 1;
+  mutable unsigned CachedFieldIndex : 31;
+
   Expr *BitWidth;
 protected:
   FieldDecl(Kind DK, DeclContext *DC, SourceLocation L,
             IdentifierInfo *Id, QualType T, TypeSourceInfo *TInfo,
             Expr *BW, bool Mutable)
-    : DeclaratorDecl(DK, DC, L, Id, T, TInfo), Mutable(Mutable), BitWidth(BW) {
+    : DeclaratorDecl(DK, DC, L, Id, T, TInfo),
+      Mutable(Mutable), CachedFieldIndex(0), BitWidth(BW) {
   }
 
 public:
@@ -1741,6 +1744,10 @@ public:
                            SourceLocation L, IdentifierInfo *Id, QualType T,
                            TypeSourceInfo *TInfo, Expr *BW, bool Mutable);
 
+  /// getFieldIndex - Returns the index of this field within its record,
+  /// as appropriate for passing to ASTRecordLayout::getFieldOffset.
+  unsigned getFieldIndex() const;
+
   /// isMutable - Determines whether this field is mutable (C++ only).
   bool isMutable() const { return Mutable; }
 
index d1ac00871a7b0d73921b5ae598098f60d3da7e73..043d56a98d0f76a303d6a89f583aab09ab85b74c 100644 (file)
@@ -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);
     }
   }
 
index 9e2e8763f853206af075f857ff9bb57c216b3053..b1da849ae1533d0230b60c010051b5db88144d2e 100644 (file)
@@ -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
 //===----------------------------------------------------------------------===//
index f262a4acff08455c6d9b5c0f29fafd2839d179f6..afce24e6254d58448d83d4e357f338e60cec2964 100644 (file)
@@ -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();
index 2934d01d64961c3d13bca75dbfe2a060de4ccc5a..731a50bb077290422ba33ae28e287405205ebf46 100644 (file)
@@ -87,3 +87,16 @@ int s2_load_y(struct s2 *a) { return a->y; }
 // CHECK-FUNCTIONS: define void @s2_copy
 // CHECK-FUNCTIONS: call void @llvm.memcpy.p0i8.p0i8.i64(i8* {{.*}}, i8* {{.*}}, i64 8, i32 2, i1 false)
 void s2_copy(struct s2 *a, struct s2 *b) { *b = *a; }
+
+struct __attribute__((packed, aligned)) s3 {
+  short aShort;
+  int anInt;
+};
+// CHECK-GLOBAL: @s3_1 = global i32 2
+int s3_1 = __alignof(((struct s3*) 0)->anInt);
+// CHECK-FUNCTIONS: define i32 @test3(
+int test3(struct s3 *ptr) {
+  // CHECK-FUNCTIONS:      [[PTR:%.*]] = getelementptr inbounds {{%.*}}* {{%.*}}, i32 0, i32 1
+  // CHECK-FUNCTIONS-NEXT: load i32* [[PTR]], align 2
+  return ptr->anInt;
+}