Author: MrEven132 Date: 2026-08-27T21:42:16+08:00 New Revision: 74253d0e4f01fca3c2cc526aee9d073af3fad919
URL: https://github.com/llvm/llvm-project/commit/74253d0e4f01fca3c2cc526aee9d073af3fad919 DIFF: https://github.com/llvm/llvm-project/commit/74253d0e4f01fca3c2cc526aee9d073af3fad919.diff LOG: [lldb] Support DW_ATE_float in DW_OP_convert (#219131) ## 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 Added: Modified: lldb/include/lldb/Expression/DWARFExpression.h lldb/source/Expression/DWARFExpression.cpp lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h lldb/unittests/Expression/DWARFExpressionTest.cpp Removed: ################################################################################ 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"); } _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
