llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-lldb
Author: Bar Soloveychik (barsolo2000)
<details>
<summary>Changes</summary>
This is adding GDB remote vector register support.
- Build exact Clang types from parsed vector metadata.
- Use native Clang vectors where valid and arrays where required to preserve
exact layouts.
- Support nested, pointer, boolean, and non-power-of-two vectors.
- Expose vector lanes through `SBValue` children and expression paths.
- Handle typed vector data correctly across target byte orders.
- Add builder, SB API, layout, cache, and big-endian tests.
Example:
```
vector = frame.FindRegister("v0")
vector.GetNumChildren() # 4
vector.GetChildAtIndex(2).GetValue() # "3.5"
vector.GetValueForExpressionPath("[2]").GetValue() # "3.5"
v = frame.FindRegister("v0")
print([v.GetChildAtIndex(i).GetValue()
for i in range(v.GetNumChildren())])
# ['1.5', '2.5', '3.5', '4.5']
```
Structured `register read` output is added separately in patch 5.
---
Patch is 33.63 KiB, truncated to 20.00 KiB below, full version:
https://github.com/llvm/llvm-project/pull/220707.diff
9 Files Affected:
- (modified) lldb/include/lldb/Utility/RegisterValue.h (+5)
- (modified) lldb/source/Core/Value.cpp (+8-4)
- (modified)
lldb/source/Plugins/RegisterTypeBuilder/RegisterTypeBuilderClang.cpp (+76-1)
- (modified) lldb/source/Plugins/RegisterTypeBuilder/RegisterTypeBuilderClang.h
(+4)
- (modified) lldb/source/Utility/RegisterValue.cpp (+33)
- (modified) lldb/source/ValueObject/ValueObjectRegister.cpp (+37-16)
- (modified)
lldb/test/API/functionalities/gdb_remote_client/TestXMLRegisterVector.py
(+194-11)
- (modified) lldb/unittests/Target/RegisterTypeBuilderClangTest.cpp (+116)
- (modified) lldb/unittests/Utility/RegisterValueTest.cpp (+93)
``````````diff
diff --git a/lldb/include/lldb/Utility/RegisterValue.h
b/lldb/include/lldb/Utility/RegisterValue.h
index e4a2de35559fa..d53b316fa2c97 100644
--- a/lldb/include/lldb/Utility/RegisterValue.h
+++ b/lldb/include/lldb/Utility/RegisterValue.h
@@ -99,6 +99,11 @@ class RegisterValue {
bool GetData(DataExtractor &data) const;
+ /// Copy this value into \p data using \p byte_order and the byte size from
+ /// \p reg_info.
+ bool GetData(DataExtractor &data, const RegisterInfo ®_info,
+ lldb::ByteOrder byte_order) const;
+
// Copy the register value from this object into a buffer in "dst" and obey
// the "dst_byte_order" when copying the data. Also watch out in case
// "dst_len" is longer or shorter than the register value described by
diff --git a/lldb/source/Core/Value.cpp b/lldb/source/Core/Value.cpp
index f9e65d397f05f..0a7610e62ed4a 100644
--- a/lldb/source/Core/Value.cpp
+++ b/lldb/source/Core/Value.cpp
@@ -492,10 +492,14 @@ Status Value::GetValueAsData(ExecutionContext *exe_ctx,
DataExtractor &data,
address_type = eAddressTypeHost;
if (exe_ctx) {
if (Target *target = exe_ctx->GetTargetPtr()) {
- // Registers are always stored in host endian.
- data.SetByteOrder(m_context_type == ContextType::RegisterInfo
- ? endian::InlHostByteOrder()
- : target->GetArchitecture().GetByteOrder());
+ ByteOrder byte_order = target->GetArchitecture().GetByteOrder();
+ // ValueObjectRegister stores typed vectors in target byte order and
+ // all other register buffers in host byte order.
+ if (m_context_type == ContextType::RegisterInfo &&
+ !llvm::isa_and_present<RegisterTypeVector>(
+ GetRegisterInfo()->register_type))
+ byte_order = endian::InlHostByteOrder();
+ data.SetByteOrder(byte_order);
data.SetAddressByteSize(target->GetArchitecture().GetAddressByteSize());
break;
}
diff --git
a/lldb/source/Plugins/RegisterTypeBuilder/RegisterTypeBuilderClang.cpp
b/lldb/source/Plugins/RegisterTypeBuilder/RegisterTypeBuilderClang.cpp
index 91f53975e5255..16cf0e0e6284a 100644
--- a/lldb/source/Plugins/RegisterTypeBuilder/RegisterTypeBuilderClang.cpp
+++ b/lldb/source/Plugins/RegisterTypeBuilder/RegisterTypeBuilderClang.cpp
@@ -12,6 +12,7 @@
#include "lldb/Core/PluginManager.h"
#include "lldb/Utility/RegisterType.h"
#include "lldb/lldb-enumerations.h"
+#include "llvm/ADT/bit.h"
using namespace lldb_private;
@@ -141,6 +142,78 @@ CompilerType RegisterTypeBuilderClang::BuildFlagsType(
return flags_type;
}
+CompilerType
+RegisterTypeBuilderClang::BuildVectorType(const RegisterTypeVector
*vector_type,
+ uint32_t expected_byte_size,
+ lldb::TypeSystemClangSP type_system)
{
+ if (!expected_byte_size)
+ return {};
+ if (auto type = GetExistingCompilerType(vector_type, expected_byte_size))
+ return *type;
+
+ std::optional<uint64_t> element_size =
+ vector_type->GetElementType()->GetByteSize();
+ if (!element_size) {
+ if (expected_byte_size % vector_type->GetCount())
+ return {};
+ element_size = expected_byte_size / vector_type->GetCount();
+ }
+ if (*element_size > UINT32_MAX ||
+ expected_byte_size % vector_type->GetCount() ||
+ *element_size != expected_byte_size / vector_type->GetCount())
+ return {};
+
+ CompilerType element_type;
+ const RegisterType *element_register_type = vector_type->GetElementType();
+ switch (element_register_type->getKind()) {
+ case RegisterType::eRegisterTypeKindBuiltin:
+ element_type =
+
BuildBuiltinType(llvm::cast<RegisterTypeBuiltin>(element_register_type),
+ *element_size, type_system);
+ break;
+ case RegisterType::eRegisterTypeKindVector:
+ element_type =
+ BuildVectorType(llvm::cast<RegisterTypeVector>(element_register_type),
+ *element_size, type_system);
+ break;
+ case RegisterType::eRegisterTypeKindEnum:
+ case RegisterType::eRegisterTypeKindFlags:
+ return {};
+ }
+ if (!element_type.IsValid())
+ return {};
+
+ const auto *builtin_element =
+ llvm::dyn_cast<RegisterTypeBuiltin>(element_register_type);
+ bool pointer_element =
+ builtin_element && (builtin_element->GetID() == "data_ptr" ||
+ builtin_element->GetID() == "code_ptr");
+ // Clang vectors can pad non-power-of-two element counts. Pointer, boolean
+ // and nested elements also need array layout to match the XML exactly.
+ bool use_vector = builtin_element && !pointer_element &&
+ builtin_element->GetID() != "bool" &&
+ llvm::has_single_bit(vector_type->GetCount());
+ CompilerType compiler_type = type_system->CreateArrayType(
+ element_type, vector_type->GetCount(), use_vector);
+
+ auto compiler_size =
+ llvm::expectedToOptional(compiler_type.GetByteSize(nullptr));
+ // Target ABI rules can still pad a Clang vector. Prefer an array when that
+ // gives the exact byte size described by the register XML.
+ if (compiler_size != expected_byte_size) {
+ compiler_type = type_system->CreateArrayType(
+ element_type, vector_type->GetCount(), /*is_vector=*/false);
+ compiler_size =
+ llvm::expectedToOptional(compiler_type.GetByteSize(nullptr));
+ }
+ if (compiler_size != expected_byte_size)
+ return {};
+
+ m_type_cache.try_emplace(
+ std::make_pair(vector_type->GetUID(), expected_byte_size),
compiler_type);
+ return compiler_type;
+}
+
CompilerType
RegisterTypeBuilderClang::GetRegisterType(const RegisterInfo ®_info) {
lldb::TypeSystemClangSP type_system =
@@ -173,7 +246,9 @@ RegisterTypeBuilderClang::GetRegisterType(const
RegisterInfo ®_info) {
llvm::dyn_cast<RegisterTypeEnum>(reg_info.register_type),
reg_info.byte_size, type_system);
case RegisterType::eRegisterTypeKindVector:
- return {};
+ return BuildVectorType(
+ llvm::cast<RegisterTypeVector>(reg_info.register_type),
+ reg_info.byte_size, type_system);
}
}
diff --git a/lldb/source/Plugins/RegisterTypeBuilder/RegisterTypeBuilderClang.h
b/lldb/source/Plugins/RegisterTypeBuilder/RegisterTypeBuilderClang.h
index e4734a85269d4..ab069b5642225 100644
--- a/lldb/source/Plugins/RegisterTypeBuilder/RegisterTypeBuilderClang.h
+++ b/lldb/source/Plugins/RegisterTypeBuilder/RegisterTypeBuilderClang.h
@@ -45,6 +45,10 @@ class RegisterTypeBuilderClang : public RegisterTypeBuilder {
uint32_t register_byte_size,
lldb::TypeSystemClangSP type_system);
+ CompilerType BuildVectorType(const RegisterTypeVector *vector_type,
+ uint32_t expected_byte_size,
+ lldb::TypeSystemClangSP type_system);
+
Target &m_target;
// A cache of previously created types. We do not cache by element ID because
diff --git a/lldb/source/Utility/RegisterValue.cpp
b/lldb/source/Utility/RegisterValue.cpp
index 4d762dc80e7f5..4db3ebf0cd44a 100644
--- a/lldb/source/Utility/RegisterValue.cpp
+++ b/lldb/source/Utility/RegisterValue.cpp
@@ -8,6 +8,7 @@
#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/DataBufferHeap.h"
#include "lldb/Utility/DataExtractor.h"
#include "lldb/Utility/Scalar.h"
#include "lldb/Utility/Status.h"
@@ -19,7 +20,9 @@
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h"
+#include <algorithm>
#include <cstdint>
+#include <memory>
#include <string>
#include <tuple>
#include <vector>
@@ -35,6 +38,36 @@ bool RegisterValue::GetData(DataExtractor &data) const {
return data.SetData(GetBytes(), GetByteSize(), GetByteOrder()) > 0;
}
+bool RegisterValue::GetData(DataExtractor &data, const RegisterInfo ®_info,
+ lldb::ByteOrder byte_order) const {
+ DataExtractor source;
+ if (!GetData(source) || source.GetByteSize() < reg_info.byte_size)
+ return false;
+
+ const lldb::ByteOrder source_byte_order = source.GetByteOrder();
+ if ((source_byte_order != lldb::eByteOrderBig &&
+ source_byte_order != lldb::eByteOrderLittle) ||
+ (byte_order != lldb::eByteOrderBig &&
+ byte_order != lldb::eByteOrderLittle))
+ return false;
+
+ auto buffer_sp = std::make_shared<DataBufferHeap>(reg_info.byte_size, 0);
+ size_t source_offset = source_byte_order == lldb::eByteOrderBig
+ ? source.GetByteSize() - reg_info.byte_size
+ : 0;
+ const uint8_t *source_bytes = source.GetDataStart() + source_offset;
+ uint8_t *destination_bytes = buffer_sp->GetBytes();
+ if (source_byte_order == byte_order)
+ std::copy_n(source_bytes, reg_info.byte_size, destination_bytes);
+ else
+ std::reverse_copy(source_bytes, source_bytes + reg_info.byte_size,
+ destination_bytes);
+
+ data.Clear();
+ data.SetByteOrder(byte_order);
+ return data.SetData(buffer_sp) == reg_info.byte_size;
+}
+
uint32_t RegisterValue::GetAsMemoryData(const RegisterInfo ®_info, void
*dst,
uint32_t dst_len,
lldb::ByteOrder dst_byte_order,
diff --git a/lldb/source/ValueObject/ValueObjectRegister.cpp
b/lldb/source/ValueObject/ValueObjectRegister.cpp
index 4ddf1dfee64b0..cba86ed5c252b 100644
--- a/lldb/source/ValueObject/ValueObjectRegister.cpp
+++ b/lldb/source/ValueObject/ValueObjectRegister.cpp
@@ -20,6 +20,7 @@
#include "lldb/Utility/DataExtractor.h"
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterType.h"
#include "lldb/Utility/Scalar.h"
#include "lldb/Utility/Status.h"
#include "lldb/Utility/Stream.h"
@@ -207,27 +208,38 @@
ValueObjectRegister::ValueObjectRegister(ExecutionContextScope *exe_scope,
ValueObjectRegister::~ValueObjectRegister() = default;
CompilerType ValueObjectRegister::GetCompilerTypeImpl() {
- if (!m_compiler_type.IsValid()) {
- ExecutionContext exe_ctx(GetExecutionContextRef());
- if (auto *target = exe_ctx.GetTargetPtr()) {
- if (auto *exe_module = target->GetExecutableModulePointer()) {
- auto type_system_or_err =
- exe_module->GetTypeSystemForLanguage(eLanguageTypeC);
- if (auto err = type_system_or_err.takeError()) {
- LLDB_LOG_ERROR(GetLog(LLDBLog::Types), std::move(err),
- "Unable to get CompilerType from TypeSystem: {0}");
- } else {
- if (auto ts = *type_system_or_err)
- m_compiler_type = ts->GetBuiltinTypeForEncodingAndBitSize(
- m_reg_info.encoding, m_reg_info.byte_size * 8);
- }
- }
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target && llvm::isa_and_present<RegisterTypeBuiltin, RegisterTypeVector>(
+ m_reg_info.register_type)) {
+ CompilerType register_type = target->GetRegisterType(m_reg_info);
+ if (register_type.IsValid())
+ return register_type;
+ }
+
+ if (!m_compiler_type.IsValid() && target) {
+ auto *exe_module = target->GetExecutableModulePointer();
+ if (!exe_module)
+ return m_compiler_type;
+ auto type_system_or_err =
+ exe_module->GetTypeSystemForLanguage(eLanguageTypeC);
+ if (auto err = type_system_or_err.takeError()) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::Types), std::move(err),
+ "Unable to get CompilerType from TypeSystem: {0}");
+ } else {
+ if (auto ts = *type_system_or_err)
+ m_compiler_type = ts->GetBuiltinTypeForEncodingAndBitSize(
+ m_reg_info.encoding, m_reg_info.byte_size * 8);
}
}
return m_compiler_type;
}
ConstString ValueObjectRegister::GetTypeName() {
+ if (llvm::isa_and_present<RegisterTypeBuiltin, RegisterTypeVector>(
+ m_reg_info.register_type))
+ return GetCompilerType().GetTypeName();
+
if (m_type_name.IsEmpty())
m_type_name = GetCompilerType().GetTypeName();
return m_type_name;
@@ -258,7 +270,16 @@ bool ValueObjectRegister::UpdateValue() {
if (m_reg_ctx_sp) {
RegisterValue m_old_reg_value(m_reg_value);
if (m_reg_ctx_sp->ReadRegister(&m_reg_info, m_reg_value)) {
- if (m_reg_value.GetData(m_data)) {
+ Target *target = exe_ctx.GetTargetPtr();
+ const bool has_vector_type =
+ llvm::isa_and_present<RegisterTypeVector>(m_reg_info.register_type);
+ // CompilerType children interpret bytes using the target's layout.
+ const bool got_data =
+ has_vector_type && target
+ ? m_reg_value.GetData(m_data, m_reg_info,
+ target->GetArchitecture().GetByteOrder())
+ : m_reg_value.GetData(m_data);
+ if (got_data) {
Process *process = exe_ctx.GetProcessPtr();
if (process)
m_data.SetAddressByteSize(process->GetAddressByteSize());
diff --git
a/lldb/test/API/functionalities/gdb_remote_client/TestXMLRegisterVector.py
b/lldb/test/API/functionalities/gdb_remote_client/TestXMLRegisterVector.py
index 6802e78c8ff74..4f99992d5cc59 100644
--- a/lldb/test/API/functionalities/gdb_remote_client/TestXMLRegisterVector.py
+++ b/lldb/test/API/functionalities/gdb_remote_client/TestXMLRegisterVector.py
@@ -1,4 +1,4 @@
-"""Test vector metadata from GDB remote target description XML."""
+"""Test vectors from GDB remote target description XML."""
from textwrap import dedent
@@ -43,24 +43,39 @@ def setup_multidoc_test(self, docs, register_data):
lldbutil.expect_state_changes(
self, self.dbg.GetListener(), process, [lldb.eStateStopped]
)
+ return process
- def setup_register_test(self, definitions, register_data):
- self.setup_multidoc_test(
+ def setup_register_test(
+ self, definitions, register_data, architecture="aarch64"
+ ):
+ return self.setup_multidoc_test(
{
"target.xml": dedent(
"""\
<?xml version="1.0"?>
<target version="1.0">
- <architecture>aarch64</architecture>
+ <architecture>{}</architecture>
<feature name="test.register.vectors">
{}
</feature>
</target>"""
- ).format(definitions)
+ ).format(architecture, definitions)
},
register_data,
)
+ def assert_float_children(self, value, expected):
+ self.assertTrue(value.IsValid())
+ self.assertTrue(value.MightHaveChildren())
+ self.assertEqual(value.GetNumChildren(), len(expected))
+ for index, expected_value in enumerate(expected):
+ child = value.GetChildAtIndex(index)
+ self.assertTrue(child.IsValid())
+ self.assertEqual(child.GetName(), "[{}]".format(index))
+ self.assertEqual(child.GetTypeName(), "float")
+ self.assertAlmostEqual(child.GetData().float[0], expected_value)
+ self.assertAlmostEqual(float(child.GetValue()), expected_value)
+
def assert_vector_info(self, name, byte_size, count):
self.expect(
"register info {}".format(name),
@@ -167,36 +182,54 @@ def test_invalid_vectors_are_ignored(self):
@skipIfXmlSupportMissing
@skipIfRemote
def test_duplicate_vector_id_uses_first_definition(self):
- self.setup_register_test(
+ process = self.setup_register_test(
"""\
<vector id="shared" type="ieee_single" count="2"/>
<vector id="shared" type="uint16" count="4"/>
<reg name="v0" regnum="0" bitsize="64" type="shared"/>
<reg name="pc" bitsize="64"/>""",
- "00" * 16,
+ "0000c03f00002040" + "00" * 8,
)
self.assert_vector_info("v0", 8, 2)
self.expect("register info v0", matching=False, substrs=["Vector
elements: 4"])
+ vector = (
+ process.GetThreadAtIndex(0).GetFrameAtIndex(0).FindRegister("v0")
+ )
+ self.assert_float_children(vector, [1.5, 2.5])
@skipIfXmlSupportMissing
@skipIfRemote
def test_target_type_precedes_builtin_type(self):
- self.setup_register_test(
+ process = self.setup_register_test(
"""\
<vector id="ieee_single" type="uint8" count="2"/>
<vector id="nested" type="ieee_single" count="3"/>
<reg name="v0" regnum="0" bitsize="48" type="nested"/>
<reg name="pc" bitsize="64"/>""",
- "00" * 14,
+ "010203040506" + "00" * 8,
)
self.assert_vector_info("v0", 6, 3)
+ vector = (
+ process.GetThreadAtIndex(0).GetFrameAtIndex(0).FindRegister("v0")
+ )
+ self.assertEqual(vector.GetNumChildren(), 3)
+ self.assertEqual(
+ [
+ vector.GetChildAtIndex(outer)
+ .GetChildAtIndex(inner)
+ .GetValueAsUnsigned()
+ for outer in range(3)
+ for inner in range(2)
+ ],
+ [1, 2, 3, 4, 5, 6],
+ )
@skipIfXmlSupportMissing
@skipIfRemote
def test_vector_ids_are_scoped_to_included_feature(self):
- self.setup_multidoc_test(
+ process = self.setup_multidoc_test(
{
"target.xml": dedent(
"""\
@@ -233,9 +266,159 @@ def test_vector_ids_are_scoped_to_included_feature(self):
</feature>"""
),
},
- "00" * 40,
+ "0000c03f00002040"
+ "0000c03f000020400000604000009040"
+ + "00" * 16,
)
self.assert_vector_info("first", 8, 2)
self.assert_vector_info("second", 16, 4)
self.assert_no_vector_info("unresolved")
+
+ frame = process.GetThreadAtIndex(0).GetFrameAtIndex(0)
+ self.assert_float_children(frame.FindRegister("first"), [1.5, 2.5])
+ self.assert_float_children(
+ frame.FindRegister("second"), [1.5, 2.5, 3.5, 4.5]
+ )
+
+ @skipIfXmlSupportMissing
+ @skipIfRemote
+ def test_direct_vector_sb_api(self):
+ process = self.setup_register_test(
+ """\
+ <vector id="v4f" type="ieee_single" count="4"/>
+ <reg name="v0" regnum="0" bitsize="128" type="v4f"/>
+ <reg name="pc" bitsize="64"/>""",
+ "0000c03f000020400000604000009040" + "00" * 8,
+ )
+
+ frame = process.GetThreadAtIndex(0).GetFrameAtIndex(0)
+ vector = frame.FindRegister("v0")
+ self.assertEqual(vector.GetName(), "v0")
+ self.assertEqual(vector.GetByteSize(), 16)
+ self.assertTrue(vector.GetType().IsValid())
+ self.assertEqual(vector.GetType().GetByteSize(), 16)
+ self.assertIn("float", vector.GetTypeName())
+ self.assert_float_children(vector, [1.5, 2.5, 3.5, 4.5])
+
+ lane = vector.GetValueForExpressionPath("[2]")
+ self.assertTrue(lane.IsValid())
+ self.assertAlmostEqual(lane.GetData().float[0], 3.5)
+
+ @skipIfXmlSupportMissing
+ @skipIfRemote
+ def test_byte_vector_sb_api(self):
+ process = self.setup_register_test(
+ """\
+ <vector id="bytes32" type="uint8" count="32"/>
+ <reg name="bytes" regnum="0" bitsize="256" type="bytes32"/>
+ <reg name="pc" bitsize="64"/>""",
+ "".join("{:02x}".format(value) for value in range(32)) + "00" * 8,
+ )
+
+ frame = process.GetThreadAtIndex(0).GetFrameAtIndex(0)
+ vector = frame.FindRegister("bytes")
+ self.assertEqual(vector.GetByteSize(), 32)
+ self.assertEqual(vector.GetNumChildren(), 32)
+ self.assertEqual(vector.GetData().uint8s, list(range(32)))
+
+ lane = vector.GetValueForExpressionPath("[16]")
+ self.assertTrue(lane.IsValid())
+ error = lldb.SBError()
+ self.assertEqual(lane.GetValueAsUnsigned(error, 0xDEADBEEF), 0x10)
+ self.assertTrue(error.Success())
+
+ @skipIfXmlSupportMissing
+ @skipIfRemote
+ def test_non_power_of_two_vector_exact_layout(self):
+ process = self.setup_register_test(
+ """\
+ <vector id...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/220707
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits