[Lldb-commits] [lldb] [lldb] Support DW_ATE_float in DW_OP_convert (PR #219131)
via lldb-commits
lldb-commits at lists.llvm.org
Thu Aug 27 00:40:32 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-lldb
Author: MrEven132
<details>
<summary>Changes</summary>
## Problem
DWARF permits the operand of `DW_OP_convert` to reference a floating-point
base type. LLDB instead stops while resolving such a DIE and reports
`unsupported encoding`. This also breaks expressions that only use the float
as an intermediate value, such as converting zero to float32 and then back to
the generic type.
## Change
Carry the base type's DWARF encoding into the expression evaluator so it can
distinguish integer and floating-point destinations. Float32, float64, and x87
float80 are mapped to their corresponding `APFloat` semantics.
The conversion rules are now:
- integer to float and float precision changes use nearest-ties-to-even;
- float to integer uses toward-zero;
- NaN, integer overflow, unsupported float widths, and impractical integer
widths produce evaluation errors.
The existing integer truncation and extension behavior, including conversion
to the generic address-sized unsigned type, is unchanged.
## Tests
Extend `DWARFExpression.DW_OP_convert` with the reported expression and cover
32-, 64-, and 80-bit float targets, precision narrowing, positive and negative
toward-zero conversion, NaN and range errors, excessive integer widths, and
the existing malformed-DIE paths.
Fixes #<!-- -->202605
---
Full diff: https://github.com/llvm/llvm-project/pull/219131.diff
5 Files Affected:
- (modified) lldb/include/lldb/Expression/DWARFExpression.h (+2-2)
- (modified) lldb/source/Expression/DWARFExpression.cpp (+58-9)
- (modified) lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp (+4-7)
- (modified) lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h (+2-2)
- (modified) lldb/unittests/Expression/DWARFExpressionTest.cpp (+117-12)
``````````diff
diff --git a/lldb/include/lldb/Expression/DWARFExpression.h b/lldb/include/lldb/Expression/DWARFExpression.h
index feecd50fc9c22..1f9994815499a 100644
--- a/lldb/include/lldb/Expression/DWARFExpression.h
+++ b/lldb/include/lldb/Expression/DWARFExpression.h
@@ -45,8 +45,8 @@ class DWARFExpression {
virtual uint16_t GetVersion() const = 0;
virtual dw_addr_t GetBaseAddress() const = 0;
virtual uint8_t GetAddressByteSize() const = 0;
- virtual llvm::Expected<std::pair<uint64_t, bool>>
- GetDIEBitSizeAndSign(uint64_t relative_die_offset) const = 0;
+ virtual llvm::Expected<std::pair<uint64_t, llvm::dwarf::TypeKind>>
+ GetDIEBitSizeAndEncoding(uint64_t relative_die_offset) const = 0;
virtual dw_addr_t ReadAddressFromDebugAddrSection(uint32_t index) const = 0;
virtual lldb::offset_t
GetVendorDWARFOpcodeSize(const DataExtractor &data,
diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp
index e62b6945dc3ed..d22634d63e875 100644
--- a/lldb/source/Expression/DWARFExpression.cpp
+++ b/lldb/source/Expression/DWARFExpression.cpp
@@ -16,6 +16,7 @@
#include "lldb/Expression/DWARFExpression.h"
#include <cinttypes>
+#include <limits>
#include <optional>
#include <vector>
@@ -1239,14 +1240,14 @@ static llvm::Error Evaluate_DW_OP_piece(EvalContext &eval_ctx,
static llvm::Error Evaluate_DW_OP_convert(EvalContext &eval_ctx,
uint64_t relative_die_offset) {
uint64_t bit_size;
- bool sign;
+ llvm::dwarf::TypeKind encoding;
if (relative_die_offset == 0) {
// The generic type has the size of an address on the target
// machine and an unspecified signedness. Scalar has no
// "unspecified signedness", so we use unsigned types.
if (!eval_ctx.module_sp)
return llvm::createStringError("no module");
- sign = false;
+ encoding = llvm::dwarf::DW_ATE_unsigned;
bit_size = eval_ctx.module_sp->GetArchitecture().GetAddressByteSize() * 8;
if (!bit_size)
return llvm::createStringError("unspecified architecture");
@@ -1254,14 +1255,62 @@ static llvm::Error Evaluate_DW_OP_convert(EvalContext &eval_ctx,
if (!eval_ctx.dwarf_cu)
return llvm::createStringError(
"DW_OP_convert with a DIE offset requires a DWARF unit");
- auto bit_size_sign_or_err =
- eval_ctx.dwarf_cu->GetDIEBitSizeAndSign(relative_die_offset);
- if (!bit_size_sign_or_err)
- return bit_size_sign_or_err.takeError();
- bit_size = bit_size_sign_or_err->first;
- sign = bit_size_sign_or_err->second;
+ auto bit_size_encoding_or_err =
+ eval_ctx.dwarf_cu->GetDIEBitSizeAndEncoding(relative_die_offset);
+ if (!bit_size_encoding_or_err)
+ return bit_size_encoding_or_err.takeError();
+ bit_size = bit_size_encoding_or_err->first;
+ encoding = bit_size_encoding_or_err->second;
+ }
+
+ Scalar &scalar = eval_ctx.stack.back().GetScalar();
+ if (encoding == llvm::dwarf::DW_ATE_float) {
+ const llvm::fltSemantics *semantics;
+ switch (bit_size) {
+ case 32:
+ semantics = &llvm::APFloat::IEEEsingle();
+ break;
+ case 64:
+ semantics = &llvm::APFloat::IEEEdouble();
+ break;
+ case 80:
+ semantics = &llvm::APFloat::x87DoubleExtended();
+ break;
+ default:
+ return llvm::createStringError("unsupported floating-point type size");
+ }
+
+ if (scalar.GetType() == Scalar::e_float) {
+ llvm::APFloat value = scalar.GetAPFloat();
+ bool loses_info;
+ value.convert(*semantics, llvm::APFloat::rmNearestTiesToEven,
+ &loses_info);
+ scalar = Scalar(std::move(value));
+ } else if (!scalar.FloatPromote(*semantics)) {
+ return llvm::createStringError("cannot convert value to floating point");
+ }
+ return llvm::Error::success();
+ }
+
+ const bool sign = encoding == llvm::dwarf::DW_ATE_signed ||
+ encoding == llvm::dwarf::DW_ATE_signed_char;
+ if (scalar.GetType() == Scalar::e_float) {
+ if (bit_size > std::numeric_limits<uint16_t>::max())
+ return llvm::createStringError("unsupported integer type size: %" PRIu64,
+ bit_size);
+
+ llvm::APSInt value(static_cast<unsigned>(bit_size),
+ /*isUnsigned=*/!sign);
+ bool is_exact;
+ llvm::APFloat::opStatus status = scalar.GetAPFloat().convertToInteger(
+ value, llvm::APFloat::rmTowardZero, &is_exact);
+ if (status & llvm::APFloat::opInvalidOp)
+ return llvm::createStringError(
+ "cannot convert floating-point value to integer");
+ scalar = Scalar(std::move(value));
+ } else {
+ scalar.TruncOrExtendTo(bit_size, sign);
}
- eval_ctx.stack.back().GetScalar().TruncOrExtendTo(bit_size, sign);
return llvm::Error::success();
}
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp
index 3fe8bbb8fdf56..bb89ebd52f766 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp
@@ -715,8 +715,8 @@ llvm::StringRef DWARFUnit::PeekDIEName(dw_offset_t die_offset) {
return llvm::StringRef();
}
-llvm::Expected<std::pair<uint64_t, bool>>
-DWARFUnit::GetDIEBitSizeAndSign(uint64_t relative_die_offset) const {
+llvm::Expected<std::pair<uint64_t, llvm::dwarf::TypeKind>>
+DWARFUnit::GetDIEBitSizeAndEncoding(uint64_t relative_die_offset) const {
// Retrieve the type DIE that the value is being converted to. This
// offset is compile unit relative so we need to fix it up.
const uint64_t abs_die_offset = relative_die_offset + GetOffset();
@@ -734,20 +734,17 @@ DWARFUnit::GetDIEBitSizeAndSign(uint64_t relative_die_offset) const {
bit_size = die.GetAttributeValueAsUnsigned(DW_AT_byte_size, 0) * 8;
if (!bit_size)
return llvm::createStringError("unsupported type size");
- bool sign;
switch (encoding) {
case DW_ATE_signed:
case DW_ATE_signed_char:
- sign = true;
- break;
case DW_ATE_unsigned:
case DW_ATE_unsigned_char:
- sign = false;
+ case DW_ATE_float:
break;
default:
return llvm::createStringError("unsupported encoding");
}
- return std::pair{bit_size, sign};
+ return std::pair{bit_size, static_cast<TypeKind>(encoding)};
}
lldb::offset_t
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h
index 6fde9af57fa8b..bac64ea467238 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h
@@ -157,8 +157,8 @@ class DWARFUnit : public DWARFExpression::Delegate, public UserID {
/// error or if the attribute is not present.
llvm::StringRef PeekDIEName(dw_offset_t die_offset);
- llvm::Expected<std::pair<uint64_t, bool>>
- GetDIEBitSizeAndSign(uint64_t relative_die_offset) const override;
+ llvm::Expected<std::pair<uint64_t, llvm::dwarf::TypeKind>>
+ GetDIEBitSizeAndEncoding(uint64_t relative_die_offset) const override;
lldb::offset_t GetVendorDWARFOpcodeSize(const DataExtractor &data,
const lldb::offset_t data_offset,
diff --git a/lldb/unittests/Expression/DWARFExpressionTest.cpp b/lldb/unittests/Expression/DWARFExpressionTest.cpp
index 76d2d4efcf557..aa8e17a88cc34 100644
--- a/lldb/unittests/Expression/DWARFExpressionTest.cpp
+++ b/lldb/unittests/Expression/DWARFExpressionTest.cpp
@@ -62,10 +62,10 @@ class MockDwarfDelegate : public DWARFExpression::Delegate {
uint8_t GetAddressByteSize() const override { return 4; }
- llvm::Expected<std::pair<uint64_t, bool>>
- GetDIEBitSizeAndSign(uint64_t relative_die_offset) const override {
+ llvm::Expected<std::pair<uint64_t, llvm::dwarf::TypeKind>>
+ GetDIEBitSizeAndEncoding(uint64_t relative_die_offset) const override {
return llvm::createStringError(llvm::inconvertibleErrorCode(),
- "GetDIEBitSizeAndSign not implemented");
+ "GetDIEBitSizeAndEncoding not implemented");
}
dw_addr_t ReadAddressFromDebugAddrSection(uint32_t index) const override {
@@ -488,6 +488,14 @@ TEST(DWARFExpression, DW_OP_convert) {
Form: DW_FORM_data1
- Attribute: DW_AT_bit_size
Form: DW_FORM_data1
+ - Code: 0x00000005
+ Tag: DW_TAG_base_type
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_encoding
+ Form: DW_FORM_data1
+ - Attribute: DW_AT_bit_size
+ Form: DW_FORM_data4
debug_info:
- Version: 4
AddrSize: 8
@@ -547,6 +555,26 @@ TEST(DWARFExpression, DW_OP_convert) {
- Value: 0x0000000000000005 # DW_ATE_signed
- Value: 0x0000000000000004
- Value: 0x000000000000001f
+ # 0x00000027:
+ - AbbrCode: 0x00000002
+ Values:
+ - Value: 0x0000000000000004 # DW_ATE_float
+ - Value: 0x0000000000000004
+ # 0x0000002a:
+ - AbbrCode: 0x00000002
+ Values:
+ - Value: 0x0000000000000004 # DW_ATE_float
+ - Value: 0x0000000000000008
+ # 0x0000002d:
+ - AbbrCode: 0x00000002
+ Values:
+ - Value: 0x0000000000000004 # DW_ATE_float
+ - Value: 0x000000000000000a
+ # 0x00000030:
+ - AbbrCode: 0x00000005
+ Values:
+ - Value: 0x0000000000000007 # DW_ATE_unsigned
+ - Value: 0x00000000ffffffff
- AbbrCode: 0x00000000
)";
@@ -558,6 +586,10 @@ TEST(DWARFExpression, DW_OP_convert) {
uint8_t offs_schar = 0x0000001a;
uint8_t offs_enum = 0x00000020;
uint8_t offs_sint31_t = 0x00000023;
+ uint8_t offs_float32 = 0x00000027;
+ uint8_t offs_float64 = 0x0000002a;
+ uint8_t offs_float80 = 0x0000002d;
+ uint8_t offs_huge_uint = 0x00000030;
DWARFExpressionTester t(yamldata, /*cu_index=*/1);
ASSERT_TRUE((bool)t.GetDwarfUnit());
@@ -623,6 +655,79 @@ TEST(DWARFExpression, DW_OP_convert) {
offs_sint31_t, DW_OP_stack_value}),
ExpectScalar(31, 0x40000000, is_signed));
+ // Convert zero to float and back to the generic type.
+ EXPECT_THAT_EXPECTED(t.Eval({DW_OP_lit0, DW_OP_convert, offs_float32,
+ DW_OP_convert, 0x00, DW_OP_stack_value}),
+ ExpectScalar(32, 0, not_signed));
+
+ // Float32 rounds integers above its 24-bit precision.
+ EXPECT_THAT_EXPECTED(
+ t.Eval({DW_OP_const4u, 0x01, 0x00, 0x00, 0x01, DW_OP_convert,
+ offs_float32, DW_OP_convert, 0x00, DW_OP_stack_value}),
+ ExpectScalar(32, 0x01000000, not_signed));
+
+ // Convert through float64 and back to a 64-bit integer.
+ EXPECT_THAT_EXPECTED(
+ t.Eval({DW_OP_lit3, DW_OP_convert, offs_float64, DW_OP_convert,
+ offs_uint64_t, DW_OP_stack_value}),
+ ExpectScalar(64, 3, not_signed));
+
+ // Convert through x87 extended precision and back to a 64-bit integer.
+ EXPECT_THAT_EXPECTED(
+ t.Eval({DW_OP_lit3, DW_OP_convert, offs_float80, DW_OP_convert,
+ offs_uint64_t, DW_OP_stack_value}),
+ ExpectScalar(64, 3, not_signed));
+
+ // Widening a float32 value to float64 preserves its value.
+ EXPECT_THAT_EXPECTED(
+ t.Eval({DW_OP_lit3, DW_OP_convert, offs_float32, DW_OP_convert,
+ offs_float64, DW_OP_convert, offs_uint64_t, DW_OP_stack_value}),
+ ExpectScalar(64, 3, not_signed));
+
+ // Narrowing from float64 to float32 applies the destination precision.
+ EXPECT_THAT_EXPECTED(
+ t.Eval({DW_OP_const4u, 0x01, 0x00, 0x00, 0x01, DW_OP_convert,
+ offs_float64, DW_OP_convert, offs_float32, DW_OP_convert, 0x00,
+ DW_OP_stack_value}),
+ ExpectScalar(32, 0x01000000, not_signed));
+
+ // Converting an out-of-range floating-point value to an integer fails.
+ EXPECT_THAT_ERROR(t.Eval({DW_OP_const4u, 0xff, 0xff, 0xff, 0xff,
+ DW_OP_convert, offs_float32, DW_OP_convert, 0x00})
+ .takeError(),
+ llvm::FailedWithMessage(
+ "cannot convert floating-point value to integer"));
+
+ // Inexact floating-point conversions round toward zero.
+ EXPECT_THAT_EXPECTED(
+ t.Eval({DW_OP_lit3, DW_OP_convert, offs_float32, DW_OP_lit2,
+ DW_OP_convert, offs_float32, DW_OP_div, DW_OP_convert, 0x00,
+ DW_OP_stack_value}),
+ ExpectScalar(32, 1, not_signed));
+
+ // Negative inexact floating-point conversions also round toward zero.
+ EXPECT_THAT_EXPECTED(
+ t.Eval({DW_OP_const1s, 0xfd, DW_OP_convert, offs_float32, DW_OP_lit2,
+ DW_OP_convert, offs_float32, DW_OP_div, DW_OP_convert,
+ offs_sint64_t, DW_OP_stack_value}),
+ ExpectScalar(64, 0xffffffffffffffff, is_signed));
+
+ // NaN cannot be converted to an integer.
+ EXPECT_THAT_ERROR(
+ t.Eval({DW_OP_const8u, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ DW_OP_convert, offs_float32, DW_OP_dup, DW_OP_mul, DW_OP_dup,
+ DW_OP_mul, DW_OP_dup, DW_OP_minus, DW_OP_convert, 0x00})
+ .takeError(),
+ llvm::FailedWithMessage(
+ "cannot convert floating-point value to integer"));
+
+ // Reject an excessive integer width before constructing an APSInt.
+ EXPECT_THAT_ERROR(
+ t.Eval({DW_OP_lit0, DW_OP_convert, offs_float32, DW_OP_convert,
+ offs_huge_uint})
+ .takeError(),
+ llvm::FailedWithMessage("unsupported integer type size: 4294967295"));
+
//
// Errors.
//
@@ -672,15 +777,15 @@ TEST(DWARFExpression, TypedBinaryOpsRejectMismatchedTypes) {
UnsignedShort = 3,
};
- llvm::Expected<std::pair<uint64_t, bool>>
- GetDIEBitSizeAndSign(uint64_t relative_die_offset) const override {
+ llvm::Expected<std::pair<uint64_t, llvm::dwarf::TypeKind>>
+ GetDIEBitSizeAndEncoding(uint64_t relative_die_offset) const override {
switch (relative_die_offset) {
case UnsignedChar:
- return std::pair<uint64_t, bool>{8, false};
+ return std::pair{uint64_t{8}, llvm::dwarf::DW_ATE_unsigned_char};
case SignedChar:
- return std::pair<uint64_t, bool>{8, true};
+ return std::pair{uint64_t{8}, llvm::dwarf::DW_ATE_signed_char};
case UnsignedShort:
- return std::pair<uint64_t, bool>{16, false};
+ return std::pair{uint64_t{16}, llvm::dwarf::DW_ATE_unsigned};
default:
return llvm::createStringError("unknown base type offset");
}
@@ -1036,13 +1141,13 @@ TEST(DWARFExpression, DW_OP_plus_uconst_typed) {
SignedChar = 2,
};
- llvm::Expected<std::pair<uint64_t, bool>>
- GetDIEBitSizeAndSign(uint64_t relative_die_offset) const override {
+ llvm::Expected<std::pair<uint64_t, llvm::dwarf::TypeKind>>
+ GetDIEBitSizeAndEncoding(uint64_t relative_die_offset) const override {
switch (relative_die_offset) {
case UnsignedChar:
- return std::pair<uint64_t, bool>{8, false};
+ return std::pair{uint64_t{8}, llvm::dwarf::DW_ATE_unsigned_char};
case SignedChar:
- return std::pair<uint64_t, bool>{8, true};
+ return std::pair{uint64_t{8}, llvm::dwarf::DW_ATE_signed_char};
default:
return llvm::createStringError("unknown base type offset");
}
``````````
</details>
https://github.com/llvm/llvm-project/pull/219131
More information about the lldb-commits
mailing list