Skip to content

[lldb] Prefer DW_AT_bit_size over DW_AT_byte_size in GetDIEBitSizeAndSign #137123

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

dtcxzyw
Copy link
Member

@dtcxzyw dtcxzyw commented Apr 24, 2025

DW_AT_bit_size will be ignored by lldb if DW_AT_byte_size is available and non-zero. Unfortunately, gdb crashes when DW_AT_byte_size does not exist or is set to zero. So I decided to change lldb to match gdb's behavior :(

Stacked on #137118 and #137013.
Closes #71065

@llvmbot
Copy link
Member

llvmbot commented Apr 24, 2025

@llvm/pr-subscribers-debuginfo

@llvm/pr-subscribers-lldb

Author: Yingwei Zheng (dtcxzyw)

Changes

DW_AT_bit_size will be ignored by lldb if DW_AT_byte_size is available and non-zero. Unfortunately, gdb crashes when DW_AT_byte_size does not exist or is set to zero. So I decided to change lldb to match gdb's behavior :(

Stacked on #137118 and #137013.
Closes #71065


Full diff: https://github.com/llvm/llvm-project/pull/137123.diff

6 Files Affected:

  • (modified) lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp (+2-2)
  • (added) lldb/test/Shell/SymbolFile/DWARF/print-boolean.c (+19)
  • (modified) llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp (+4)
  • (modified) llvm/lib/Transforms/InstCombine/InstructionCombining.cpp (+18)
  • (added) llvm/test/DebugInfo/X86/convert-bool.ll (+43)
  • (added) llvm/test/Transforms/InstCombine/debuginfo-invert.ll (+70)
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp
index 7d0afc04ac3b6..c6aab01fb4a59 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp
@@ -707,9 +707,9 @@ DWARFUnit::GetDIEBitSizeAndSign(uint64_t relative_die_offset) const {
     return llvm::createStringError("cannot resolve DW_OP_convert type DIE");
   uint64_t encoding =
       die.GetAttributeValueAsUnsigned(DW_AT_encoding, DW_ATE_hi_user);
-  uint64_t bit_size = die.GetAttributeValueAsUnsigned(DW_AT_byte_size, 0) * 8;
+  uint64_t bit_size = die.GetAttributeValueAsUnsigned(DW_AT_bit_size, 0);
   if (!bit_size)
-    bit_size = die.GetAttributeValueAsUnsigned(DW_AT_bit_size, 0);
+    bit_size = die.GetAttributeValueAsUnsigned(DW_AT_byte_size, 0) * 8;
   if (!bit_size)
     return llvm::createStringError("unsupported type size");
   bool sign;
diff --git a/lldb/test/Shell/SymbolFile/DWARF/print-boolean.c b/lldb/test/Shell/SymbolFile/DWARF/print-boolean.c
new file mode 100644
index 0000000000000..27778d89140ac
--- /dev/null
+++ b/lldb/test/Shell/SymbolFile/DWARF/print-boolean.c
@@ -0,0 +1,19 @@
+// RUN: %clang_host -O3 -ggdb -o %t %s
+// RUN: %lldb %t \
+// RUN:   -o "b 17" \
+// RUN:   -o r \
+// RUN:   -o "p t" \
+// RUN:   -o exit | FileCheck %s
+
+// CHECK: (lldb) p t
+// CHECK-NEXT: (int) 1
+
+int a, b, c;
+int d(int e) { return e; }
+int main() {
+  int t;
+  c = d(1);
+  t = 1 && c;
+  b = t & a;
+  return 0;
+}
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
index 3939dae81841f..e73028d2e28d9 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
@@ -1749,6 +1749,10 @@ void DwarfCompileUnit::createBaseTypeDIEs() {
     // Round up to smallest number of bytes that contains this number of bits.
     addUInt(Die, dwarf::DW_AT_byte_size, std::nullopt,
             divideCeil(Btr.BitSize, 8));
+    // If the size is not a multiple of 8 (e.g., boolean), add the bit size
+    // field.
+    if (Btr.BitSize % 8 != 0)
+      addUInt(Die, dwarf::DW_AT_bit_size, std::nullopt, Btr.BitSize);
 
     Btr.Die = &Die;
   }
diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index f807f5f4519fc..c47d32dc01cd3 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -1396,6 +1396,24 @@ void InstCombinerImpl::freelyInvertAllUsersOf(Value *I, Value *IgnoredUser) {
                        "canFreelyInvertAllUsersOf() ?");
     }
   }
+
+  // Update pre-existing debug value uses.
+  SmallVector<DbgValueInst *, 4> DbgValues;
+  SmallVector<DbgVariableRecord *, 4> DbgVariableRecords;
+  llvm::findDbgValues(DbgValues, I, &DbgVariableRecords);
+  auto ApplyNot = [](DIExpression *Src) {
+    SmallVector<uint64_t> Elements;
+    Elements.reserve(Src->getElements().size() + 1);
+    Elements.push_back(dwarf::DW_OP_not);
+    Elements.append(Src->getElements().begin(), Src->getElements().end());
+    return DIExpression::get(Src->getContext(), Elements);
+  };
+
+  for (auto *DVI : DbgValues)
+    DVI->setExpression(ApplyNot(DVI->getExpression()));
+
+  for (DbgVariableRecord *DVR : DbgVariableRecords)
+    DVR->setExpression(ApplyNot(DVR->getExpression()));
 }
 
 /// Given a 'sub' instruction, return the RHS of the instruction if the LHS is a
diff --git a/llvm/test/DebugInfo/X86/convert-bool.ll b/llvm/test/DebugInfo/X86/convert-bool.ll
new file mode 100644
index 0000000000000..3b131579dfe74
--- /dev/null
+++ b/llvm/test/DebugInfo/X86/convert-bool.ll
@@ -0,0 +1,43 @@
+; RUN: llc -mtriple=x86_64 -dwarf-version=5 -filetype=obj -O0 < %s | llvm-dwarfdump - | FileCheck %s
+
+; CHECK: DW_TAG_base_type
+; CHECK-NEXT: DW_AT_name      ("DW_ATE_unsigned_1")
+; CHECK-NEXT: DW_AT_encoding  (DW_ATE_unsigned)
+; CHECK-NEXT: DW_AT_byte_size (0x01)
+; CHECK-NEXT: DW_AT_bit_size  (0x01)
+
+define void @main() !dbg !18 {
+entry:
+    #dbg_value(i1 false, !22, !DIExpression(DW_OP_not, DW_OP_LLVM_convert, 1, DW_ATE_unsigned, DW_OP_LLVM_convert, 32, DW_ATE_unsigned, DW_OP_stack_value), !23)
+  ret void, !dbg !24
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!10, !11, !12, !13, !14, !15, !16}
+!llvm.ident = !{!17}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 21.0.0git", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, globals: !2, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "test.c", directory: "/", checksumkind: CSK_MD5, checksum: "100bdbce655d729c24c7c0e8523a58ae")
+!2 = !{!3, !6, !8}
+!3 = !DIGlobalVariableExpression(var: !4, expr: !DIExpression())
+!4 = distinct !DIGlobalVariable(name: "a", scope: !0, file: !1, line: 1, type: !5, isLocal: false, isDefinition: true)
+!5 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!6 = !DIGlobalVariableExpression(var: !7, expr: !DIExpression())
+!7 = distinct !DIGlobalVariable(name: "b", scope: !0, file: !1, line: 1, type: !5, isLocal: false, isDefinition: true)
+!8 = !DIGlobalVariableExpression(var: !9, expr: !DIExpression())
+!9 = distinct !DIGlobalVariable(name: "c", scope: !0, file: !1, line: 1, type: !5, isLocal: false, isDefinition: true)
+!10 = !{i32 7, !"Dwarf Version", i32 5}
+!11 = !{i32 2, !"Debug Info Version", i32 3}
+!12 = !{i32 1, !"wchar_size", i32 4}
+!13 = !{i32 8, !"PIC Level", i32 2}
+!14 = !{i32 7, !"PIE Level", i32 2}
+!15 = !{i32 7, !"uwtable", i32 2}
+!16 = !{i32 7, !"debug-info-assignment-tracking", i1 true}
+!17 = !{!"clang version 21.0.0git"}
+!18 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 3, type: !19, scopeLine: 3, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !21)
+!19 = !DISubroutineType(types: !20)
+!20 = !{null}
+!21 = !{!22}
+!22 = !DILocalVariable(name: "l_4516", scope: !18, file: !1, line: 4, type: !5)
+!23 = !DILocation(line: 0, scope: !18)
+!24 = !DILocation(line: 9, column: 1, scope: !18)
diff --git a/llvm/test/Transforms/InstCombine/debuginfo-invert.ll b/llvm/test/Transforms/InstCombine/debuginfo-invert.ll
new file mode 100644
index 0000000000000..8c673e319f7f7
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/debuginfo-invert.ll
@@ -0,0 +1,70 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -passes=instcombine -S %s -o - | FileCheck %s
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+define i32 @test(i32 noundef %x, i32 noundef %y) !dbg !10 {
+; CHECK-LABEL: define i32 @test(
+; CHECK-SAME: i32 noundef [[X:%.*]], i32 noundef [[Y:%.*]]) !dbg [[DBG10:![0-9]+]] {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:      #dbg_value(i32 [[X]], [[META15:![0-9]+]], !DIExpression(), [[META18:![0-9]+]])
+; CHECK-NEXT:      #dbg_value(i32 [[Y]], [[META16:![0-9]+]], !DIExpression(), [[META18]])
+; CHECK-NEXT:    [[CMP_NOT:%.*]] = icmp eq i32 [[X]], 0, !dbg [[DBG19:![0-9]+]]
+; CHECK-NEXT:      #dbg_value(i1 [[CMP_NOT]], [[META17:![0-9]+]], !DIExpression(DW_OP_not, DW_OP_LLVM_convert, 1, DW_ATE_unsigned, DW_OP_LLVM_convert, 32, DW_ATE_unsigned, DW_OP_stack_value), [[META18]])
+; CHECK-NEXT:    [[TMP0:%.*]] = and i32 [[Y]], 1, !dbg [[DBG20:![0-9]+]]
+; CHECK-NEXT:    [[AND:%.*]] = select i1 [[CMP_NOT]], i32 0, i32 [[TMP0]], !dbg [[DBG20]]
+; CHECK-NEXT:    ret i32 [[AND]], !dbg [[DBG21:![0-9]+]]
+;
+entry:
+  #dbg_value(i32 %x, !15, !DIExpression(), !18)
+  #dbg_value(i32 %y, !16, !DIExpression(), !18)
+  %cmp = icmp ne i32 %x, 0, !dbg !19
+  %conv = zext i1 %cmp to i32, !dbg !19
+  #dbg_value(i32 %conv, !17, !DIExpression(), !18)
+  %and = and i32 %conv, %y, !dbg !20
+  ret i32 %and, !dbg !21
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3, !4, !5, !6, !7, !8}
+!llvm.ident = !{!9}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 21.0.0git", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "test.c", directory: "/", checksumkind: CSK_MD5, checksum: "b2d9ffc7905684d8b7c3b52a3136e57c")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !{i32 1, !"wchar_size", i32 4}
+!5 = !{i32 8, !"PIC Level", i32 2}
+!6 = !{i32 7, !"PIE Level", i32 2}
+!7 = !{i32 7, !"uwtable", i32 2}
+!8 = !{i32 7, !"debug-info-assignment-tracking", i1 true}
+!9 = !{!"clang version 21.0.0git"}
+!10 = distinct !DISubprogram(name: "test", scope: !1, file: !1, line: 1, type: !11, scopeLine: 1, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !14)
+!11 = !DISubroutineType(types: !12)
+!12 = !{!13, !13, !13}
+!13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!14 = !{!15, !16, !17}
+!15 = !DILocalVariable(name: "x", arg: 1, scope: !10, file: !1, line: 1, type: !13)
+!16 = !DILocalVariable(name: "y", arg: 2, scope: !10, file: !1, line: 1, type: !13)
+!17 = !DILocalVariable(name: "z", scope: !10, file: !1, line: 2, type: !13)
+!18 = !DILocation(line: 0, scope: !10)
+!19 = !DILocation(line: 2, column: 13, scope: !10)
+!20 = !DILocation(line: 3, column: 12, scope: !10)
+!21 = !DILocation(line: 3, column: 3, scope: !10)
+;.
+; CHECK: [[META0:![0-9]+]] = distinct !DICompileUnit(language: DW_LANG_C11, file: [[META1:![0-9]+]], producer: "{{.*}}clang version {{.*}}", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+; CHECK: [[META1]] = !DIFile(filename: "test.c", directory: {{.*}})
+; CHECK: [[DBG10]] = distinct !DISubprogram(name: "test", scope: [[META1]], file: [[META1]], line: 1, type: [[META11:![0-9]+]], scopeLine: 1, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: [[META0]], retainedNodes: [[META14:![0-9]+]])
+; CHECK: [[META11]] = !DISubroutineType(types: [[META12:![0-9]+]])
+; CHECK: [[META12]] = !{[[META13:![0-9]+]], [[META13]], [[META13]]}
+; CHECK: [[META13]] = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+; CHECK: [[META14]] = !{[[META15]], [[META16]], [[META17]]}
+; CHECK: [[META15]] = !DILocalVariable(name: "x", arg: 1, scope: [[DBG10]], file: [[META1]], line: 1, type: [[META13]])
+; CHECK: [[META16]] = !DILocalVariable(name: "y", arg: 2, scope: [[DBG10]], file: [[META1]], line: 1, type: [[META13]])
+; CHECK: [[META17]] = !DILocalVariable(name: "z", scope: [[DBG10]], file: [[META1]], line: 2, type: [[META13]])
+; CHECK: [[META18]] = !DILocation(line: 0, scope: [[DBG10]])
+; CHECK: [[DBG19]] = !DILocation(line: 2, column: 13, scope: [[DBG10]])
+; CHECK: [[DBG20]] = !DILocation(line: 3, column: 12, scope: [[DBG10]])
+; CHECK: [[DBG21]] = !DILocation(line: 3, column: 3, scope: [[DBG10]])
+;.

@llvmbot
Copy link
Member

llvmbot commented Apr 24, 2025

@llvm/pr-subscribers-llvm-transforms

Author: Yingwei Zheng (dtcxzyw)

Changes

DW_AT_bit_size will be ignored by lldb if DW_AT_byte_size is available and non-zero. Unfortunately, gdb crashes when DW_AT_byte_size does not exist or is set to zero. So I decided to change lldb to match gdb's behavior :(

Stacked on #137118 and #137013.
Closes #71065


Full diff: https://github.com/llvm/llvm-project/pull/137123.diff

6 Files Affected:

  • (modified) lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp (+2-2)
  • (added) lldb/test/Shell/SymbolFile/DWARF/print-boolean.c (+19)
  • (modified) llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp (+4)
  • (modified) llvm/lib/Transforms/InstCombine/InstructionCombining.cpp (+18)
  • (added) llvm/test/DebugInfo/X86/convert-bool.ll (+43)
  • (added) llvm/test/Transforms/InstCombine/debuginfo-invert.ll (+70)
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp
index 7d0afc04ac3b6..c6aab01fb4a59 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp
@@ -707,9 +707,9 @@ DWARFUnit::GetDIEBitSizeAndSign(uint64_t relative_die_offset) const {
     return llvm::createStringError("cannot resolve DW_OP_convert type DIE");
   uint64_t encoding =
       die.GetAttributeValueAsUnsigned(DW_AT_encoding, DW_ATE_hi_user);
-  uint64_t bit_size = die.GetAttributeValueAsUnsigned(DW_AT_byte_size, 0) * 8;
+  uint64_t bit_size = die.GetAttributeValueAsUnsigned(DW_AT_bit_size, 0);
   if (!bit_size)
-    bit_size = die.GetAttributeValueAsUnsigned(DW_AT_bit_size, 0);
+    bit_size = die.GetAttributeValueAsUnsigned(DW_AT_byte_size, 0) * 8;
   if (!bit_size)
     return llvm::createStringError("unsupported type size");
   bool sign;
diff --git a/lldb/test/Shell/SymbolFile/DWARF/print-boolean.c b/lldb/test/Shell/SymbolFile/DWARF/print-boolean.c
new file mode 100644
index 0000000000000..27778d89140ac
--- /dev/null
+++ b/lldb/test/Shell/SymbolFile/DWARF/print-boolean.c
@@ -0,0 +1,19 @@
+// RUN: %clang_host -O3 -ggdb -o %t %s
+// RUN: %lldb %t \
+// RUN:   -o "b 17" \
+// RUN:   -o r \
+// RUN:   -o "p t" \
+// RUN:   -o exit | FileCheck %s
+
+// CHECK: (lldb) p t
+// CHECK-NEXT: (int) 1
+
+int a, b, c;
+int d(int e) { return e; }
+int main() {
+  int t;
+  c = d(1);
+  t = 1 && c;
+  b = t & a;
+  return 0;
+}
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
index 3939dae81841f..e73028d2e28d9 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
@@ -1749,6 +1749,10 @@ void DwarfCompileUnit::createBaseTypeDIEs() {
     // Round up to smallest number of bytes that contains this number of bits.
     addUInt(Die, dwarf::DW_AT_byte_size, std::nullopt,
             divideCeil(Btr.BitSize, 8));
+    // If the size is not a multiple of 8 (e.g., boolean), add the bit size
+    // field.
+    if (Btr.BitSize % 8 != 0)
+      addUInt(Die, dwarf::DW_AT_bit_size, std::nullopt, Btr.BitSize);
 
     Btr.Die = &Die;
   }
diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index f807f5f4519fc..c47d32dc01cd3 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -1396,6 +1396,24 @@ void InstCombinerImpl::freelyInvertAllUsersOf(Value *I, Value *IgnoredUser) {
                        "canFreelyInvertAllUsersOf() ?");
     }
   }
+
+  // Update pre-existing debug value uses.
+  SmallVector<DbgValueInst *, 4> DbgValues;
+  SmallVector<DbgVariableRecord *, 4> DbgVariableRecords;
+  llvm::findDbgValues(DbgValues, I, &DbgVariableRecords);
+  auto ApplyNot = [](DIExpression *Src) {
+    SmallVector<uint64_t> Elements;
+    Elements.reserve(Src->getElements().size() + 1);
+    Elements.push_back(dwarf::DW_OP_not);
+    Elements.append(Src->getElements().begin(), Src->getElements().end());
+    return DIExpression::get(Src->getContext(), Elements);
+  };
+
+  for (auto *DVI : DbgValues)
+    DVI->setExpression(ApplyNot(DVI->getExpression()));
+
+  for (DbgVariableRecord *DVR : DbgVariableRecords)
+    DVR->setExpression(ApplyNot(DVR->getExpression()));
 }
 
 /// Given a 'sub' instruction, return the RHS of the instruction if the LHS is a
diff --git a/llvm/test/DebugInfo/X86/convert-bool.ll b/llvm/test/DebugInfo/X86/convert-bool.ll
new file mode 100644
index 0000000000000..3b131579dfe74
--- /dev/null
+++ b/llvm/test/DebugInfo/X86/convert-bool.ll
@@ -0,0 +1,43 @@
+; RUN: llc -mtriple=x86_64 -dwarf-version=5 -filetype=obj -O0 < %s | llvm-dwarfdump - | FileCheck %s
+
+; CHECK: DW_TAG_base_type
+; CHECK-NEXT: DW_AT_name      ("DW_ATE_unsigned_1")
+; CHECK-NEXT: DW_AT_encoding  (DW_ATE_unsigned)
+; CHECK-NEXT: DW_AT_byte_size (0x01)
+; CHECK-NEXT: DW_AT_bit_size  (0x01)
+
+define void @main() !dbg !18 {
+entry:
+    #dbg_value(i1 false, !22, !DIExpression(DW_OP_not, DW_OP_LLVM_convert, 1, DW_ATE_unsigned, DW_OP_LLVM_convert, 32, DW_ATE_unsigned, DW_OP_stack_value), !23)
+  ret void, !dbg !24
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!10, !11, !12, !13, !14, !15, !16}
+!llvm.ident = !{!17}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 21.0.0git", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, globals: !2, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "test.c", directory: "/", checksumkind: CSK_MD5, checksum: "100bdbce655d729c24c7c0e8523a58ae")
+!2 = !{!3, !6, !8}
+!3 = !DIGlobalVariableExpression(var: !4, expr: !DIExpression())
+!4 = distinct !DIGlobalVariable(name: "a", scope: !0, file: !1, line: 1, type: !5, isLocal: false, isDefinition: true)
+!5 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!6 = !DIGlobalVariableExpression(var: !7, expr: !DIExpression())
+!7 = distinct !DIGlobalVariable(name: "b", scope: !0, file: !1, line: 1, type: !5, isLocal: false, isDefinition: true)
+!8 = !DIGlobalVariableExpression(var: !9, expr: !DIExpression())
+!9 = distinct !DIGlobalVariable(name: "c", scope: !0, file: !1, line: 1, type: !5, isLocal: false, isDefinition: true)
+!10 = !{i32 7, !"Dwarf Version", i32 5}
+!11 = !{i32 2, !"Debug Info Version", i32 3}
+!12 = !{i32 1, !"wchar_size", i32 4}
+!13 = !{i32 8, !"PIC Level", i32 2}
+!14 = !{i32 7, !"PIE Level", i32 2}
+!15 = !{i32 7, !"uwtable", i32 2}
+!16 = !{i32 7, !"debug-info-assignment-tracking", i1 true}
+!17 = !{!"clang version 21.0.0git"}
+!18 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 3, type: !19, scopeLine: 3, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !21)
+!19 = !DISubroutineType(types: !20)
+!20 = !{null}
+!21 = !{!22}
+!22 = !DILocalVariable(name: "l_4516", scope: !18, file: !1, line: 4, type: !5)
+!23 = !DILocation(line: 0, scope: !18)
+!24 = !DILocation(line: 9, column: 1, scope: !18)
diff --git a/llvm/test/Transforms/InstCombine/debuginfo-invert.ll b/llvm/test/Transforms/InstCombine/debuginfo-invert.ll
new file mode 100644
index 0000000000000..8c673e319f7f7
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/debuginfo-invert.ll
@@ -0,0 +1,70 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -passes=instcombine -S %s -o - | FileCheck %s
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+define i32 @test(i32 noundef %x, i32 noundef %y) !dbg !10 {
+; CHECK-LABEL: define i32 @test(
+; CHECK-SAME: i32 noundef [[X:%.*]], i32 noundef [[Y:%.*]]) !dbg [[DBG10:![0-9]+]] {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:      #dbg_value(i32 [[X]], [[META15:![0-9]+]], !DIExpression(), [[META18:![0-9]+]])
+; CHECK-NEXT:      #dbg_value(i32 [[Y]], [[META16:![0-9]+]], !DIExpression(), [[META18]])
+; CHECK-NEXT:    [[CMP_NOT:%.*]] = icmp eq i32 [[X]], 0, !dbg [[DBG19:![0-9]+]]
+; CHECK-NEXT:      #dbg_value(i1 [[CMP_NOT]], [[META17:![0-9]+]], !DIExpression(DW_OP_not, DW_OP_LLVM_convert, 1, DW_ATE_unsigned, DW_OP_LLVM_convert, 32, DW_ATE_unsigned, DW_OP_stack_value), [[META18]])
+; CHECK-NEXT:    [[TMP0:%.*]] = and i32 [[Y]], 1, !dbg [[DBG20:![0-9]+]]
+; CHECK-NEXT:    [[AND:%.*]] = select i1 [[CMP_NOT]], i32 0, i32 [[TMP0]], !dbg [[DBG20]]
+; CHECK-NEXT:    ret i32 [[AND]], !dbg [[DBG21:![0-9]+]]
+;
+entry:
+  #dbg_value(i32 %x, !15, !DIExpression(), !18)
+  #dbg_value(i32 %y, !16, !DIExpression(), !18)
+  %cmp = icmp ne i32 %x, 0, !dbg !19
+  %conv = zext i1 %cmp to i32, !dbg !19
+  #dbg_value(i32 %conv, !17, !DIExpression(), !18)
+  %and = and i32 %conv, %y, !dbg !20
+  ret i32 %and, !dbg !21
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3, !4, !5, !6, !7, !8}
+!llvm.ident = !{!9}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 21.0.0git", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "test.c", directory: "/", checksumkind: CSK_MD5, checksum: "b2d9ffc7905684d8b7c3b52a3136e57c")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !{i32 1, !"wchar_size", i32 4}
+!5 = !{i32 8, !"PIC Level", i32 2}
+!6 = !{i32 7, !"PIE Level", i32 2}
+!7 = !{i32 7, !"uwtable", i32 2}
+!8 = !{i32 7, !"debug-info-assignment-tracking", i1 true}
+!9 = !{!"clang version 21.0.0git"}
+!10 = distinct !DISubprogram(name: "test", scope: !1, file: !1, line: 1, type: !11, scopeLine: 1, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !14)
+!11 = !DISubroutineType(types: !12)
+!12 = !{!13, !13, !13}
+!13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!14 = !{!15, !16, !17}
+!15 = !DILocalVariable(name: "x", arg: 1, scope: !10, file: !1, line: 1, type: !13)
+!16 = !DILocalVariable(name: "y", arg: 2, scope: !10, file: !1, line: 1, type: !13)
+!17 = !DILocalVariable(name: "z", scope: !10, file: !1, line: 2, type: !13)
+!18 = !DILocation(line: 0, scope: !10)
+!19 = !DILocation(line: 2, column: 13, scope: !10)
+!20 = !DILocation(line: 3, column: 12, scope: !10)
+!21 = !DILocation(line: 3, column: 3, scope: !10)
+;.
+; CHECK: [[META0:![0-9]+]] = distinct !DICompileUnit(language: DW_LANG_C11, file: [[META1:![0-9]+]], producer: "{{.*}}clang version {{.*}}", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+; CHECK: [[META1]] = !DIFile(filename: "test.c", directory: {{.*}})
+; CHECK: [[DBG10]] = distinct !DISubprogram(name: "test", scope: [[META1]], file: [[META1]], line: 1, type: [[META11:![0-9]+]], scopeLine: 1, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: [[META0]], retainedNodes: [[META14:![0-9]+]])
+; CHECK: [[META11]] = !DISubroutineType(types: [[META12:![0-9]+]])
+; CHECK: [[META12]] = !{[[META13:![0-9]+]], [[META13]], [[META13]]}
+; CHECK: [[META13]] = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+; CHECK: [[META14]] = !{[[META15]], [[META16]], [[META17]]}
+; CHECK: [[META15]] = !DILocalVariable(name: "x", arg: 1, scope: [[DBG10]], file: [[META1]], line: 1, type: [[META13]])
+; CHECK: [[META16]] = !DILocalVariable(name: "y", arg: 2, scope: [[DBG10]], file: [[META1]], line: 1, type: [[META13]])
+; CHECK: [[META17]] = !DILocalVariable(name: "z", scope: [[DBG10]], file: [[META1]], line: 2, type: [[META13]])
+; CHECK: [[META18]] = !DILocation(line: 0, scope: [[DBG10]])
+; CHECK: [[DBG19]] = !DILocation(line: 2, column: 13, scope: [[DBG10]])
+; CHECK: [[DBG20]] = !DILocation(line: 3, column: 12, scope: [[DBG10]])
+; CHECK: [[DBG21]] = !DILocation(line: 3, column: 3, scope: [[DBG10]])
+;.

@tromey
Copy link
Contributor

tromey commented Apr 24, 2025

DW_AT_bit_size will be ignored by lldb if DW_AT_byte_size is available and non-zero. Unfortunately, gdb crashes when DW_AT_byte_size does not exist or is set to zero. So I decided to change lldb to match gdb's behavior :(

Can you file a gdb bug report with an executable showing this behavior? Or even just email it to me?

@dtcxzyw
Copy link
Member Author

dtcxzyw commented Apr 24, 2025

DW_AT_bit_size will be ignored by lldb if DW_AT_byte_size is available and non-zero. Unfortunately, gdb crashes when DW_AT_byte_size does not exist or is set to zero. So I decided to change lldb to match gdb's behavior :(

Can you file a gdb bug report with an executable showing this behavior? Or even just email it to me?

Reproducer:
test.c:

int a, b, c;
int d(int e) { return e; }
int main() {
  int l_4516;
  c = d(1);
  l_4516 = 1 && c;
  b = l_4516 & a;
  return 0;
}

test.s: https://gist.github.com/dtcxzyw/a3fa3d33dbbea9717dfe7a1579df0d01

> gcc test.s # or use `clang test.s`
> gdb ./a.out
(gdb) b 7
(gdb) r
(gdb) p l_4516
/build/gdb-1WjiBe/gdb-15.0.50.20240403/gdb/gmp-utils.c:84: internal-error: export_bits: Assertion `buf.size () > 0' failed.
A problem internal to GDB has been detected,
further debugging may prove unreliable.
----- Backtrace -----
0x5d2df1cddbc6 ???
0x5d2df20f9dd4 ???
0x5d2df20fa0d0 ???
0x5d2df228df74 ???
0x5d2df1e41744 ???
0x5d2df21140ad ???
0x5d2df2105e96 ???
0x5d2df1d91242 ???
0x5d2df1d91ae7 ???
0x5d2df1d921c3 ???
0x5d2df1dacbcd ???
0x5d2df1dacfc6 ???
0x5d2df1dad599 ???
0x5d2df1e1d270 ???
0x5d2df2100eb8 ???
0x5d2df1dfaf46 ???
0x5d2df1dfa398 ???
0x5d2df1f3bcc3 ???
0x5d2df1f3c32f ???
0x5d2df1d16464 ???
0x5d2df2080727 ???
0x5d2df1e01967 ???
0x5d2df1e03143 ???
0x5d2df1e02332 ???
0x7c7e1b1ca9e4 ???
0x5d2df1e02495 ???
0x5d2df1e02663 ???
0x5d2df20c3a5c ???
0x5d2df228e975 ???
0x5d2df228f4f7 ???
0x5d2df1eddea9 ???
0x5d2df1ee0f04 ???
0x5d2df1c27dab ???
0x7c7e19e2a1c9 __libc_start_call_main
        ../sysdeps/nptl/libc_start_call_main.h:58
0x7c7e19e2a28a __libc_start_main_impl
        ../csu/libc-start.c:360
0x5d2df1c361f4 ???
0xffffffffffffffff ???
---------------------
/build/gdb-1WjiBe/gdb-15.0.50.20240403/gdb/gmp-utils.c:84: internal-error: export_bits: Assertion `buf.size () > 0' failed.
A problem internal to GDB has been detected,
further debugging may prove unreliable.

gcc version: gcc (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0
gdb version: GNU gdb (Ubuntu 15.0.50.20240403-0ubuntu1) 15.0.50.20240403-git

tromey added a commit to tromey/gdb that referenced this pull request Apr 25, 2025
DWARF says that a base type can have DW_AT_bit_size, without
DW_AT_byte_size.  However, gdb does not correctly handle this; in
fact, it crashes, as pointed out in this LLVM merge request:

    llvm/llvm-project#137123

This patch reworks the base type size logic a bit to handle this
situation.
@tromey
Copy link
Contributor

tromey commented Apr 25, 2025

Thank you for the test case. I sent a gdb fix: https://sourceware.org/pipermail/gdb-patches/2025-April/217513.html

tromey added a commit to tromey/gdb that referenced this pull request Apr 25, 2025
DWARF says that a base type can have DW_AT_bit_size, without
DW_AT_byte_size.  However, gdb does not correctly handle this; in
fact, it crashes, as pointed out in this LLVM merge request:

    llvm/llvm-project#137123

This patch reworks the base type size logic a bit to handle this
situation.
sebhub pushed a commit to RTEMS/sourceware-mirror-binutils-gdb that referenced this pull request Apr 29, 2025
DWARF says that a base type can have DW_AT_bit_size, without
DW_AT_byte_size.  However, gdb does not correctly handle this; in
fact, it crashes, as pointed out in this LLVM merge request:

    llvm/llvm-project#137123

This patch reworks the base type size logic a bit to handle this
situation.

Tested-by: Kevin Buettner <kevinb@redhat.com>
Approved-by: Kevin Buettner <kevinb@redhat.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Incorrect debug info generated at clang-trunk -O3 (clang-17 -O3 is correct)
3 participants