]> granicus.if.org Git - clang/commitdiff
Correct debug info bit offset calculation for big-endian targets
authorReid Kleckner <rnk@google.com>
Mon, 12 Jun 2017 19:57:56 +0000 (19:57 +0000)
committerReid Kleckner <rnk@google.com>
Mon, 12 Jun 2017 19:57:56 +0000 (19:57 +0000)
Summary:
The change "[CodeView] Implement support for bit fields in
Clang" (r274201, https://reviews.llvm.org/rL274201) broke the
calculation of bit offsets for the debug info describing bitfields on
big-endian targets.

Prior to commit r274201 the debug info for bitfields got their offsets
from the ASTRecordLayout in CGDebugInfo::CollectRecordFields(), the
current field offset was then passed on to
CGDebugInfo::CollectRecordNormalField() and used directly in the
DIDerivedType.

Since commit r274201, the bit offset ending up in the DIDerivedType no
longer comes directly from the ASTRecordLayout. Instead
CGDebugInfo::CollectRecordNormalField() calls the new method
CGDebugInfo::createBitFieldType(), which in turn calls
CodeGenTypes::getCGRecordLayout().getBitFieldInfo() to fetch a
CGBitFieldInfo describing the field. The 'Offset' member of
CGBitFieldInfo is then used to calculate the bit offset of the
DIDerivedType. Unfortunately the previous and current method of
calculating the bit offset are only equivalent for little endian
targets, as CGRecordLowering::setBitFieldInfo() reverses the bit
offsets for big endian targets as the last thing it does.

A simple reproducer for this error is the following module:

struct fields {
  unsigned a : 4;
  unsigned b : 4;
} flags = {0x0f, 0x1};

Compiled for Mips, with commit r274200 both the DIDerivedType bit
offsets on the IR-level and the DWARF information on the ELF-level
will have the expected values: the offsets of 'a' and 'b' are 0 and 4
respectively. With r274201 the offsets are switched to 4 and 0. By
noting that the static initialization of 'flags' in both cases is the
same, we can eliminate a change in record layout as the cause of the
change in the debug info. Also compiling this example with gcc,
produces the same record layout and debug info as commit r274200.

In order to restore the previous function we extend
CGDebugInfo::createBitFieldType() to compensate for the reversal done
in CGRecordLowering::setBitFieldInfo().

Patch by Frej Drejhammar!

Reviewers: cfe-commits, majnemer, rnk, aaboud, echristo, aprantl

Reviewed By: rnk, aprantl

Subscribers: aprantl, arichardson, frej

Differential Revision: https://reviews.llvm.org/D32745

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

lib/CodeGen/CGDebugInfo.cpp
test/CodeGen/mips-debug-info-bitfield.c [new file with mode: 0644]

index ebb264eb61c9f2f06f79ae3bb0536cc8f6dc757d..e43da6ff4c97642368e851d9e4b4052f3bfb5ba7 100644 (file)
@@ -1041,7 +1041,13 @@ llvm::DIType *CGDebugInfo::createBitFieldType(const FieldDecl *BitFieldDecl,
   assert(SizeInBits > 0 && "found named 0-width bitfield");
   uint64_t StorageOffsetInBits =
       CGM.getContext().toBits(BitFieldInfo.StorageOffset);
-  uint64_t OffsetInBits = StorageOffsetInBits + BitFieldInfo.Offset;
+  uint64_t Offset = BitFieldInfo.Offset;
+  // The bit offsets for big endian machines are reversed for big
+  // endian target, compensate for that as the DIDerivedType requires
+  // un-reversed offsets.
+  if (CGM.getDataLayout().isBigEndian())
+    Offset = BitFieldInfo.StorageSize - BitFieldInfo.Size - Offset;
+  uint64_t OffsetInBits = StorageOffsetInBits + Offset;
   llvm::DINode::DIFlags Flags = getAccessFlag(BitFieldDecl->getAccess(), RD);
   return DBuilder.createBitFieldMemberType(
       RecordTy, Name, File, Line, SizeInBits, OffsetInBits, StorageOffsetInBits,
diff --git a/test/CodeGen/mips-debug-info-bitfield.c b/test/CodeGen/mips-debug-info-bitfield.c
new file mode 100644 (file)
index 0000000..a0e2ed9
--- /dev/null
@@ -0,0 +1,17 @@
+// RUN: %clang_cc1 -x c -debug-info-kind=limited -triple mips-none-linux-gnu -emit-llvm -o - %s | FileCheck %s
+
+struct fields
+{
+  unsigned a : 4;
+  unsigned b : 4;
+} flags;
+
+// CHECK: !DIDerivedType(tag: DW_TAG_member,
+// CHECK-SAME: {{.*}}name: "a"
+// CHECK-NOT: {{.*}}offset:
+// CHECK-SAME: {{.*}}flags: DIFlagBitField
+
+// CHECK: !DIDerivedType(tag: DW_TAG_member,
+// CHECK-SAME: {{.*}}name: "b"
+// CHECK-SAME: {{.*}}offset: 4
+// CHECK-SAME: {{.*}}flags: DIFlagBitField