]> granicus.if.org Git - clang/commitdiff
CodeGen: FieldMemcpyizer didn't handle copies starting inside bitfields
authorDavid Majnemer <david.majnemer@gmail.com>
Fri, 10 Oct 2014 18:57:10 +0000 (18:57 +0000)
committerDavid Majnemer <david.majnemer@gmail.com>
Fri, 10 Oct 2014 18:57:10 +0000 (18:57 +0000)
It's possible to construct cases where the first field we are trying to
copy is in the middle of an IR field.  In some complicated cases, we
would fail to use an appropriate offset inside the object.  Earlier
builds of clang seemed to miscompile the code by copying an insufficient
number of bytes.  Up until now, we would assert: the copying offset was
insufficiently aligned.

This fixes PR21232.

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

lib/CodeGen/CGClass.cpp
test/CodeGenCXX/pod-member-memcpys.cpp

index 941e998bfd753e6c4959290b7308f584197b2e91..49760853dc8d6147121cf27b206b21e35c4e990b 100644 (file)
@@ -796,13 +796,13 @@ namespace {
         addNextField(F);
     }
 
-    CharUnits getMemcpySize() const {
+    CharUnits getMemcpySize(uint64_t FirstByteOffset) const {
       unsigned LastFieldSize =
         LastField->isBitField() ?
           LastField->getBitWidthValue(CGF.getContext()) :
           CGF.getContext().getTypeSize(LastField->getType()); 
       uint64_t MemcpySizeBits =
-        LastFieldOffset + LastFieldSize - FirstFieldOffset +
+        LastFieldOffset + LastFieldSize - FirstByteOffset +
         CGF.getContext().getCharWidth() - 1;
       CharUnits MemcpySize =
         CGF.getContext().toCharUnitsFromBits(MemcpySizeBits);
@@ -818,19 +818,31 @@ namespace {
 
       CharUnits Alignment;
 
+      uint64_t FirstByteOffset;
       if (FirstField->isBitField()) {
         const CGRecordLayout &RL =
           CGF.getTypes().getCGRecordLayout(FirstField->getParent());
         const CGBitFieldInfo &BFInfo = RL.getBitFieldInfo(FirstField);
         Alignment = CharUnits::fromQuantity(BFInfo.StorageAlignment);
+        // FirstFieldOffset is not appropriate for bitfields,
+        // it won't tell us what the storage offset should be and thus might not
+        // be properly aligned.
+        //
+        // Instead calculate the storage offset using the offset of the field in
+        // the struct type.
+        const llvm::DataLayout &DL = CGF.CGM.getDataLayout();
+        FirstByteOffset =
+            DL.getStructLayout(RL.getLLVMType())
+                ->getElementOffsetInBits(RL.getLLVMFieldNo(FirstField));
       } else {
         Alignment = CGF.getContext().getDeclAlign(FirstField);
+        FirstByteOffset = FirstFieldOffset;
       }
 
-      assert((CGF.getContext().toCharUnitsFromBits(FirstFieldOffset) %
+      assert((CGF.getContext().toCharUnitsFromBits(FirstByteOffset) %
               Alignment) == 0 && "Bad field alignment.");
 
-      CharUnits MemcpySize = getMemcpySize();
+      CharUnits MemcpySize = getMemcpySize(FirstByteOffset);
       QualType RecordTy = CGF.getContext().getTypeDeclType(ClassDecl);
       llvm::Value *ThisPtr = CGF.LoadCXXThis();
       LValue DestLV = CGF.MakeNaturalAlignAddrLValue(ThisPtr, RecordTy);
index 31d774c5345e19479e8f7ed857712c8464ed0009..5bd8adfc1d183cbdfab7daaa1b1580d05b068c7e 100644 (file)
@@ -67,6 +67,13 @@ struct BitfieldMember2 {
   NonPOD np;
 };
 
+struct BitfieldMember3 {
+  virtual void f();
+  int   : 8;
+  int x : 1;
+  int y;
+};
+
 struct InnerClassMember {
   struct {
     int a, b, c, d;
@@ -174,6 +181,7 @@ CALL_AO(PackedMembers)
 
 CALL_CC(PackedMembers)
 CALL_CC(BitfieldMember2)
+CALL_CC(BitfieldMember3)
 CALL_CC(ReferenceMember)
 CALL_CC(InnerClassMember)
 CALL_CC(BitfieldMember)
@@ -243,6 +251,11 @@ CALL_CC(Basic)
 // CHECK: call void @llvm.memcpy.p0i8.p0i8.i64({{.*}}i64 16, i32 8{{.*}})
 // CHECK: ret void
 
+// BitfieldMember3 copy-constructor:
+// CHECK-LABEL: define linkonce_odr void @_ZN15BitfieldMember3C2ERKS_(%struct.BitfieldMember3* %this, %struct.BitfieldMember3* dereferenceable({{[0-9]+}}))
+// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* %3, i8* %4, i64 8, i32 8, i1 false)
+// CHECK: ret void
+
 // BitfieldMember2 copy-constructor:
 // CHECK-2-LABEL: define linkonce_odr void @_ZN15BitfieldMember2C2ERKS_(%struct.BitfieldMember2* %this, %struct.BitfieldMember2* dereferenceable({{[0-9]+}}))
 // CHECK-2: call void @llvm.memcpy.p0i8.p0i8.i64({{.*}}i64 16, i32 4, i1 false)