https://github.com/firmiana402 created https://github.com/llvm/llvm-project/pull/204353
This PR fixes two related DWARF constant-handling bugs that were blocking each other. First, LLDB's DWARF expression evaluator in [`DWARFExpression.cpp`](https://github.com/llvm/llvm-project/blob/main/lldb/source/Expression/DWARFExpression.cpp) handled `DW_OP_constu` and `DW_OP_consts` without going through `to_generic`. Under DWARF, these operators push a generic value: an address-sized integral value with unspecified signedness. That means the result should be truncated to the target address size (via `to_generic`). Second, LLVM already had a producer-side issue tracked as [#47431](https://github.com/llvm/llvm-project/issues/47431): on 32-bit targets, LLVM could emit `DW_OP_consts` / `DW_OP_constu` for source integer constants wider than the target generic type. If LLDB were fixed alone, those producer-emitted constants would become truncated as DWARF requires, exposing incorrect debug info for wide source values. This patch fixes both sides together. ## What Changed On the LLDB consumer side: - `DW_OP_constu` now uses `to_generic`. - `DW_OP_consts` now uses `to_generic`. - The corresponding LLDB DWARF expression tests were updated to expect address-sized generic values. On the LLVM producer side: - Wide integer debug-location constants that cannot be represented by the target generic type are emitted as `DW_OP_implicit_value` instead of `DW_OP_const*`. - This preserves the source value bytes instead of relying on an address-sized DWARF generic constant. - The producer-side change is limited to complete constant values, where there are no remaining `DIExpression` operations. ## Validation Locally verified with: ```text build/tools/lldb/unittests/Expression/ExpressionTests --gtest_filter='DWARFExpression.*' 74 tests passed build/bin/llvm-lit -sv llvm/test/DebugInfo/X86/constant-loclist.ll 1 test passed ninja -C build check-lldb -j12 No unexpected failures ninja -C build check-all -j12 Completed with one unrelated local failure in Clang Tools :: clang-doc/DR-141990.cpp, caused by host warning-option output. No DebugInfo, DWARF, LLDB expression, or AsmPrinter-related failures were observed. ``` >From d3248dd31deea96f173dbbd0ba2ad514ea2a15bf Mon Sep 17 00:00:00 2001 From: firmiana402 <[email protected]> Date: Wed, 17 Jun 2026 19:17:07 +0800 Subject: [PATCH] [DebugInfo][LLDB] Fix generic DW_OP_const handling Make LLDB evaluate DW_OP_constu and DW_OP_consts as generic address-sized values, and teach LLVM to use DW_OP_implicit_value for wide integer constants that cannot be represented by a target generic value. --- lldb/source/Expression/DWARFExpression.cpp | 6 ++-- .../Expression/DWARFExpressionTest.cpp | 6 ++-- llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp | 35 +++++++++++++++++-- .../CodeGen/AsmPrinter/DwarfExpression.cpp | 21 +++++++++++ llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h | 3 ++ llvm/test/DebugInfo/X86/constant-loclist.ll | 16 +++++++++ 6 files changed, 77 insertions(+), 10 deletions(-) diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp index dd436e0c8afd9..7966673bb65ed 100644 --- a/lldb/source/Expression/DWARFExpression.cpp +++ b/lldb/source/Expression/DWARFExpression.cpp @@ -1411,13 +1411,11 @@ llvm::Expected<Value> DWARFExpression::Evaluate( case DW_OP_const8s: stack.push_back(to_generic(static_cast<int64_t>(op->getRawOperand(0)))); break; - // These should also use to_generic, but we can't do that due to a - // producer-side bug in llvm. See llvm.org/pr48087. case DW_OP_constu: - stack.push_back(Scalar(op->getRawOperand(0))); + stack.push_back(to_generic(op->getRawOperand(0))); break; case DW_OP_consts: - stack.push_back(Scalar(static_cast<int64_t>(op->getRawOperand(0)))); + stack.push_back(to_generic(static_cast<int64_t>(op->getRawOperand(0)))); break; case DW_OP_dup: diff --git a/lldb/unittests/Expression/DWARFExpressionTest.cpp b/lldb/unittests/Expression/DWARFExpressionTest.cpp index 6d4a624505390..7feacb2b9da24 100644 --- a/lldb/unittests/Expression/DWARFExpressionTest.cpp +++ b/lldb/unittests/Expression/DWARFExpressionTest.cpp @@ -388,13 +388,13 @@ TEST(DWARFExpression, DW_OP_const) { Evaluate({DW_OP_const8s, 0x00, 0x11, 0x22, 0x33, 0x44, 0x42, 0x47, 0x88}), ExpectScalar(0x33221100)); - // Don't truncate to address size for compatibility with clang (pr48087). + // LEB constants also push generic values, so truncate to address size. EXPECT_THAT_EXPECTED( Evaluate({DW_OP_constu, 0x81, 0x82, 0x84, 0x88, 0x90, 0xa0, 0x40}), - ExpectScalar(0x01010101010101)); + ExpectScalar(32, 0x01010101, false)); EXPECT_THAT_EXPECTED( Evaluate({DW_OP_consts, 0x81, 0x82, 0x84, 0x88, 0x90, 0xa0, 0x40}), - ExpectScalar(0xffff010101010101)); + ExpectScalar(32, 0x01010101, true)); } TEST(DWARFExpression, DW_OP_skip) { diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp index 3479485cf866c..02e1d8667cc62 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -50,6 +50,7 @@ #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/MD5.h" +#include "llvm/Support/MathExtras.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Target/TargetLoweringObjectFile.h" #include "llvm/Target/TargetMachine.h" @@ -3251,10 +3252,38 @@ void DwarfDebug::emitDebugLocValue(const AsmPrinter &AP, const DIBasicType *BT, &AP](const DbgValueLocEntry &Entry, DIExpressionCursor &Cursor) -> bool { if (Entry.isInt()) { - if (BT && (BT->getEncoding() == dwarf::DW_ATE_boolean)) + if (BT && (BT->getEncoding() == dwarf::DW_ATE_boolean)) { DwarfExpr.addBooleanConstant(Entry.getInt()); - else if (BT && (BT->getEncoding() == dwarf::DW_ATE_signed || - BT->getEncoding() == dwarf::DW_ATE_signed_char)) + return true; + } + + bool IsSigned = BT && (BT->getEncoding() == dwarf::DW_ATE_signed || + BT->getEncoding() == dwarf::DW_ATE_signed_char); + unsigned GenericBitSize = AP.MAI.getCodePointerSize() * 8; + if (BT && AP.getDwarfVersion() >= 4 && + !AP.getDwarfDebug()->tuneForSCE() && !Cursor) { + // DW_OP_const* pushes a generic, address-sized value. For a wider + // source integer value that cannot fit in the generic type, use + // DW_OP_implicit_value to preserve the source bytes instead. Keep this + // limited to complete constant values: SCE tuning already avoids + // DW_OP_implicit_value for compatibility, and expressions with + // remaining operations may need a scalar stack value rather than an + // implicit value block. + uint64_t TypeBitSize = BT->getSizeInBits(); + bool IsByteSized = TypeBitSize % 8 == 0; + bool IsOutOfRange = + IsSigned ? !isIntN(GenericBitSize, Entry.getInt()) + : !isUIntN(GenericBitSize, + static_cast<uint64_t>(Entry.getInt())); + if (TypeBitSize > GenericBitSize && IsByteSized && IsOutOfRange) { + DwarfExpr.addImplicitValue( + APInt(static_cast<unsigned>(TypeBitSize), + static_cast<uint64_t>(Entry.getInt()), IsSigned)); + return true; + } + } + + if (IsSigned) DwarfExpr.addSignedConstant(Entry.getInt()); else DwarfExpr.addUnsignedConstant(Entry.getInt()); diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp index d9b2deb6ccf3d..4057605d973c3 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp @@ -236,6 +236,27 @@ void DwarfExpression::addUnsignedConstant(const APInt &Value) { } } +void DwarfExpression::addImplicitValue(const APInt &Value) { + assert(isImplicitLocation() || isUnknownLocation()); + assert(DwarfVersion >= 4); + + APInt API = Value; + unsigned NumBytes = API.getBitWidth() / 8; + assert(API.getBitWidth() == NumBytes * 8 && + "implicit value must be byte-sized"); + + emitOp(dwarf::DW_OP_implicit_value); + emitUnsigned(NumBytes); + + // The loop below is emitting the value starting at the least significant + // byte, so byte-swap first for big-endian targets. + if (CU.getAsmPrinter()->getDataLayout().isBigEndian()) + API = API.byteSwap(); + + for (unsigned I = 0; I < NumBytes; ++I) + emitData1(API.extractBits(8, I * 8).getZExtValue()); +} + void DwarfExpression::addConstantFP(const APFloat &APF, const AsmPrinter &AP) { assert(isImplicitLocation() || isUnknownLocation()); APInt API = APF.bitcastToAPInt(); diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h index c4929aed1c197..14357168c52c9 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h @@ -240,6 +240,9 @@ class DwarfExpression { /// Emit an unsigned constant. void addUnsignedConstant(const APInt &Value); + /// Emit an implicit value. + void addImplicitValue(const APInt &Value); + /// Emit an floating point constant. void addConstantFP(const APFloat &Value, const AsmPrinter &AP); diff --git a/llvm/test/DebugInfo/X86/constant-loclist.ll b/llvm/test/DebugInfo/X86/constant-loclist.ll index 630c652ba0ccf..7d33bfd4d3341 100644 --- a/llvm/test/DebugInfo/X86/constant-loclist.ll +++ b/llvm/test/DebugInfo/X86/constant-loclist.ll @@ -1,4 +1,5 @@ ; RUN: llc -filetype=obj %s -o - -experimental-debug-variable-locations=true | llvm-dwarfdump -v -debug-info - | FileCheck %s +; RUN: llc -filetype=obj %s -o - -experimental-debug-variable-locations=true -mtriple=i386-unknown-linux-gnu -dwarf-version=4 | llvm-dwarfdump -v -debug-info - | FileCheck %s --check-prefix=I386 ; A hand-written testcase to check 64-bit constant handling in location lists. @@ -18,6 +19,21 @@ ; CHECK-NEXT: {{.*}}: DW_OP_constu 0x4000000000000000) ; CHECK-NEXT: DW_AT_name {{.*}}"d" +; On 32-bit targets, source integer constants that do not fit in the +; address-sized DWARF generic type must use DW_OP_implicit_value to avoid +; truncating the source value. +; I386: .debug_info contents: +; I386: DW_TAG_variable +; I386-NEXT: DW_AT_location +; I386-NEXT: {{.*}}: DW_OP_lit0 +; I386-NEXT: {{.*}}: DW_OP_implicit_value 0x8 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x40) +; I386-NEXT: DW_AT_name {{.*}}"u" +; I386: DW_TAG_variable +; I386-NEXT: DW_AT_location +; I386-NEXT: {{.*}}: DW_OP_consts +0 +; I386-NEXT: {{.*}}: DW_OP_implicit_value 0x8 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x40) +; I386-NEXT: DW_AT_name {{.*}}"i" + source_filename = "test.c" target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-apple-macosx" _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
