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

Reply via email to