https://github.com/barsolo2000 created
https://github.com/llvm/llvm-project/pull/220707
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.
>From ae964c820ccfd96b48af351a8cf71c086efc9cea Mon Sep 17 00:00:00 2001
From: Bar Soloveychik <[email protected]>
Date: Mon, 24 Aug 2026 12:04:53 -0700
Subject: [PATCH] [lldb][4/5] Expose vector registers through SBValue
---
lldb/include/lldb/Utility/RegisterValue.h | 5 +
lldb/source/Core/Value.cpp | 12 +-
.../RegisterTypeBuilderClang.cpp | 77 ++++++-
.../RegisterTypeBuilderClang.h | 4 +
lldb/source/Utility/RegisterValue.cpp | 33 +++
.../ValueObject/ValueObjectRegister.cpp | 53 +++--
.../TestXMLRegisterVector.py | 205 +++++++++++++++++-
.../Target/RegisterTypeBuilderClangTest.cpp | 116 ++++++++++
lldb/unittests/Utility/RegisterValueTest.cpp | 93 ++++++++
9 files changed, 566 insertions(+), 32 deletions(-)
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="v3f" type="ieee_single" count="3"/>
+ <reg name="v0" regnum="0" bitsize="96" type="v3f"/>
+ <reg name="pc" bitsize="64"/>""",
+ "0000c03f0000204000006040" + "00" * 8,
+ )
+
+ vector = (
+ process.GetThreadAtIndex(0).GetFrameAtIndex(0).FindRegister("v0")
+ )
+ self.assertEqual(vector.GetByteSize(), 12)
+ self.assertEqual(vector.GetTypeName(), "float[3]")
+ self.assert_float_children(vector, [1.5, 2.5, 3.5])
+
+ @skipIfXmlSupportMissing
+ @skipIfRemote
+ def test_nested_vector_sb_api(self):
+ process = self.setup_register_test(
+ """\
+ <vector id="v2f" type="ieee_single" count="2"/>
+ <vector id="v2v2f" type="v2f" count="2"/>
+ <reg name="v0" regnum="0" bitsize="128" type="v2v2f"/>
+ <reg name="pc" bitsize="64"/>""",
+ "0000c03f000020400000604000009040" + "00" * 8,
+ )
+
+ vector = (
+ process.GetThreadAtIndex(0).GetFrameAtIndex(0).FindRegister("v0")
+ )
+ self.assertEqual(vector.GetNumChildren(), 2)
+ self.assert_float_children(vector.GetChildAtIndex(0), [1.5, 2.5])
+ self.assert_float_children(vector.GetChildAtIndex(1), [3.5, 4.5])
+
+ lane = vector.GetValueForExpressionPath("[1][0]")
+ self.assertTrue(lane.IsValid())
+ self.assertAlmostEqual(lane.GetData().float[0], 3.5)
+
+ @skipIfXmlSupportMissing
+ @skipIfRemote
+ def test_bool_and_single_uint128_vectors(self):
+ process = self.setup_register_test(
+ """\
+ <vector id="v4b" type="bool" count="4"/>
+ <vector id="v1u128" type="uint128" count="1"/>
+ <reg name="bools" regnum="0" bitsize="32" type="v4b"/>
+ <reg name="wide" regnum="1" bitsize="128" type="v1u128"/>
+ <reg name="pc" bitsize="64"/>""",
+ "00010100" + "01" + "00" * 15 + "00" * 8,
+ )
+
+ frame = process.GetThreadAtIndex(0).GetFrameAtIndex(0)
+ bools = frame.FindRegister("bools")
+ self.assertEqual(bools.GetTypeName(), "bool[4]")
+ self.assertEqual(bools.GetNumChildren(), 4)
+ self.assertEqual(
+ [bools.GetChildAtIndex(i).GetValueAsUnsigned() for i in range(4)],
+ [0, 1, 1, 0],
+ )
+
+ wide = frame.FindRegister("wide")
+ self.assertEqual(wide.GetNumChildren(), 1)
+ wide_child = wide.GetChildAtIndex(0)
+ expected_bytes = [1] + [0] * 15
+ self.assertEqual(wide.GetData().uint8s, expected_bytes)
+ self.assertEqual(wide_child.GetData().uint8s, expected_bytes)
+ self.assertEqual(wide_child.GetValue(), "1")
+
+ @skipIfXmlSupportMissing
+ @skipIfRemote
+ @skipIfLLVMTargetMissing("SystemZ")
+ def test_big_endian_vector_sb_api(self):
+ process = self.setup_register_test(
+ """\
+ <vector id="v2f" type="ieee_single" count="2"/>
+ <reg name="v0" regnum="0" bitsize="64" type="v2f"/>
+ <reg name="v1" regnum="1" bitsize="64" type="v2f"
+ encoding="uint"/>
+ <reg name="pswa" regnum="2" bitsize="64"/>""",
+ "3fc0000040200000" * 2 + "00" * 8,
+ architecture="s390x",
+ )
+
+ frame = process.GetThreadAtIndex(0).GetFrameAtIndex(0)
+ ull = process.GetTarget().GetBasicType(lldb.eBasicTypeUnsignedLongLong)
+ for name in ["v0", "v1"]:
+ vector = frame.FindRegister(name)
+ self.assert_float_children(vector, [1.5, 2.5])
+ self.assertEqual(
+ vector.Cast(ull).GetValueAsUnsigned(), 0x3FC0000040200000
+ )
diff --git a/lldb/unittests/Target/RegisterTypeBuilderClangTest.cpp
b/lldb/unittests/Target/RegisterTypeBuilderClangTest.cpp
index 33c014e92fc02..d9eed260d6487 100644
--- a/lldb/unittests/Target/RegisterTypeBuilderClangTest.cpp
+++ b/lldb/unittests/Target/RegisterTypeBuilderClangTest.cpp
@@ -225,4 +225,120 @@ TEST_F(RegisterTypeBuilderClangTest, RejectsSizeMismatch)
{
EXPECT_FALSE(builder.GetRegisterType(MakeRegisterInfo(pointer, 4)));
}
+TEST_F(RegisterTypeBuilderClangTest, BuildsPowerOfTwoVector) {
+ Target &target = m_debugger_sp->GetDummyTarget();
+ RegisterTypeBuiltin element_type("ieee_single", eEncodingIEEE754,
+ eFormatFloat, 4);
+ RegisterTypeVector vector_type("v4f", &element_type, 4);
+ RegisterTypeBuilderClang builder(target);
+
+ CompilerType type =
+ builder.GetRegisterType(MakeRegisterInfo(vector_type, 16));
+
+ ASSERT_TRUE(type);
+ EXPECT_EQ(llvm::expectedToOptional(type.GetByteSize(nullptr)), 16u);
+ EXPECT_NE(type.GetTypeInfo() & eTypeIsVector, 0u);
+ EXPECT_EQ(llvm::expectedToOptional(type.GetNumChildren(true, nullptr)), 4u);
+}
+
+TEST_F(RegisterTypeBuilderClangTest, PreservesThreeLaneVectorLayout) {
+ Target &target = m_debugger_sp->GetDummyTarget();
+ RegisterTypeBuiltin element_type("ieee_single", eEncodingIEEE754,
+ eFormatFloat, 4);
+ RegisterTypeVector vector_type("v3f", &element_type, 3);
+ RegisterTypeBuilderClang builder(target);
+
+ CompilerType type =
+ builder.GetRegisterType(MakeRegisterInfo(vector_type, 12));
+
+ ASSERT_TRUE(type);
+ EXPECT_EQ(llvm::expectedToOptional(type.GetByteSize(nullptr)), 12u);
+ EXPECT_NE(type.GetTypeInfo() & eTypeIsArray, 0u);
+ EXPECT_EQ(type.GetTypeInfo() & eTypeIsVector, 0u);
+ EXPECT_EQ(llvm::expectedToOptional(type.GetNumChildren(true, nullptr)), 3u);
+}
+
+TEST_F(RegisterTypeBuilderClangTest, BuildsNestedVectors) {
+ Target &target = m_debugger_sp->GetDummyTarget();
+ RegisterTypeBuiltin element_type("ieee_single", eEncodingIEEE754,
+ eFormatFloat, 4);
+ RegisterTypeVector inner_type("v2f", &element_type, 2);
+ RegisterTypeVector outer_type("v2v2f", &inner_type, 2);
+ RegisterTypeBuilderClang builder(target);
+
+ CompilerType type = builder.GetRegisterType(MakeRegisterInfo(outer_type,
16));
+
+ ASSERT_TRUE(type);
+ EXPECT_EQ(llvm::expectedToOptional(type.GetByteSize(nullptr)), 16u);
+ EXPECT_NE(type.GetTypeInfo() & eTypeIsArray, 0u);
+ EXPECT_EQ(llvm::expectedToOptional(type.GetNumChildren(true, nullptr)), 2u);
+
+ CompilerType inner = type.GetArrayElementType(nullptr);
+ ASSERT_TRUE(inner);
+ EXPECT_NE(inner.GetTypeInfo() & eTypeIsVector, 0u);
+ EXPECT_EQ(llvm::expectedToOptional(inner.GetNumChildren(true, nullptr)), 2u);
+}
+
+TEST_F(RegisterTypeBuilderClangTest, BuildsTargetSizedPointerVector) {
+ Target &target = m_debugger_sp->GetDummyTarget();
+ RegisterTypeBuiltin pointer_type("data_ptr", eEncodingUint,
+ eFormatAddressInfo, std::nullopt);
+ RegisterTypeVector vector_type("v2p", &pointer_type, 2);
+ RegisterTypeBuilderClang builder(target);
+
+ CompilerType type =
+ builder.GetRegisterType(MakeRegisterInfo(vector_type, 16));
+
+ ASSERT_TRUE(type);
+ EXPECT_EQ(llvm::expectedToOptional(type.GetByteSize(nullptr)), 16u);
+ EXPECT_NE(type.GetTypeInfo() & eTypeIsArray, 0u);
+ EXPECT_EQ(llvm::expectedToOptional(type.GetNumChildren(true, nullptr)), 2u);
+}
+
+TEST_F(RegisterTypeBuilderClangTest, BuildsBoolVectorAsArray) {
+ Target &target = m_debugger_sp->GetDummyTarget();
+ RegisterTypeBuiltin element_type("bool", eEncodingUint, eFormatBoolean, 1);
+ RegisterTypeVector vector_type("v4b", &element_type, 4);
+ RegisterTypeBuilderClang builder(target);
+
+ CompilerType type = builder.GetRegisterType(MakeRegisterInfo(vector_type,
4));
+
+ ASSERT_TRUE(type);
+ EXPECT_EQ(llvm::expectedToOptional(type.GetByteSize(nullptr)), 4u);
+ EXPECT_NE(type.GetTypeInfo() & eTypeIsArray, 0u);
+ EXPECT_EQ(type.GetTypeInfo() & eTypeIsVector, 0u);
+ EXPECT_EQ(llvm::expectedToOptional(type.GetNumChildren(true, nullptr)), 4u);
+}
+
+TEST_F(RegisterTypeBuilderClangTest, BuildsSingleUint128Vector) {
+ Target &target = m_debugger_sp->GetDummyTarget();
+ RegisterTypeBuiltin element_type("uint128", eEncodingUint, eFormatHex, 16);
+ RegisterTypeVector vector_type("v1u128", &element_type, 1);
+ RegisterTypeBuilderClang builder(target);
+
+ CompilerType type =
+ builder.GetRegisterType(MakeRegisterInfo(vector_type, 16));
+
+ ASSERT_TRUE(type);
+ EXPECT_EQ(llvm::expectedToOptional(type.GetByteSize(nullptr)), 16u);
+ EXPECT_NE(type.GetTypeInfo() & eTypeIsVector, 0u);
+ EXPECT_EQ(llvm::expectedToOptional(type.GetNumChildren(true, nullptr)), 1u);
+}
+
+TEST_F(RegisterTypeBuilderClangTest, RejectsVectorSizeMismatch) {
+ Target &target = m_debugger_sp->GetDummyTarget();
+ RegisterTypeBuiltin float_type("ieee_single", eEncodingIEEE754, eFormatFloat,
+ 4);
+ RegisterTypeVector float_vector("v4f", &float_type, 4);
+ RegisterTypeBuiltin pointer_type("data_ptr", eEncodingUint,
+ eFormatAddressInfo, std::nullopt);
+ RegisterTypeVector pointer_vector("v2p", &pointer_type, 2);
+ RegisterTypeBuilderClang builder(target);
+
+ EXPECT_FALSE(builder.GetRegisterType(MakeRegisterInfo(float_vector, 12)));
+ EXPECT_FALSE(builder.GetRegisterType(MakeRegisterInfo(float_vector, 20)));
+ EXPECT_FALSE(builder.GetRegisterType(MakeRegisterInfo(pointer_vector, 0)));
+ EXPECT_FALSE(builder.GetRegisterType(MakeRegisterInfo(pointer_vector, 15)));
+}
+
} // namespace
diff --git a/lldb/unittests/Utility/RegisterValueTest.cpp
b/lldb/unittests/Utility/RegisterValueTest.cpp
index 7b27e841cbec5..33fc4dabecea4 100644
--- a/lldb/unittests/Utility/RegisterValueTest.cpp
+++ b/lldb/unittests/Utility/RegisterValueTest.cpp
@@ -57,6 +57,99 @@ TEST(RegisterValueTest, GetScalarValue) {
APInt(128, 0x7766554433221100)));
}
+TEST(RegisterValueTest, GetDataInTargetByteOrder) {
+ uint8_t big_endian_bytes[] = {0x01, 0x02, 0x03};
+ RegisterInfo reg_info{"test",
+ nullptr,
+ sizeof(big_endian_bytes),
+ 0,
+ lldb::eEncodingUint,
+ lldb::eFormatHex,
+ {0, 0, 0, LLDB_INVALID_REGNUM, 0},
+ nullptr,
+ nullptr,
+ nullptr};
+ DataExtractor source(big_endian_bytes, sizeof(big_endian_bytes),
+ lldb::eByteOrderBig, 8);
+ RegisterValue value;
+ ASSERT_TRUE(value.SetValueFromData(reg_info, source, 0, false).Success());
+
+ DataExtractor target_data;
+ ASSERT_TRUE(value.GetData(target_data, reg_info, lldb::eByteOrderBig));
+ EXPECT_EQ(target_data.GetByteOrder(), lldb::eByteOrderBig);
+ EXPECT_EQ(
+ llvm::ArrayRef(target_data.GetDataStart(), target_data.GetByteSize()),
+ llvm::ArrayRef(big_endian_bytes));
+
+ ASSERT_TRUE(value.GetData(target_data, reg_info, lldb::eByteOrderLittle));
+ EXPECT_EQ(target_data.GetByteOrder(), lldb::eByteOrderLittle);
+ uint8_t little_endian_bytes[] = {0x03, 0x02, 0x01};
+ EXPECT_EQ(
+ llvm::ArrayRef(target_data.GetDataStart(), target_data.GetByteSize()),
+ llvm::ArrayRef(little_endian_bytes));
+
+ uint8_t padded_big_endian_bytes[] = {0x00, 0x01, 0x02, 0x03};
+ RegisterValue padded_value(llvm::ArrayRef(padded_big_endian_bytes),
+ lldb::eByteOrderBig);
+ ASSERT_TRUE(padded_value.GetData(target_data, reg_info,
lldb::eByteOrderBig));
+ EXPECT_EQ(
+ llvm::ArrayRef(target_data.GetDataStart(), target_data.GetByteSize()),
+ llvm::ArrayRef(big_endian_bytes));
+}
+
+TEST(RegisterValueTest, GetDataRejectsUnsupportedByteOrder) {
+ uint8_t bytes[] = {0x01, 0x02, 0x03};
+ RegisterInfo reg_info{"test",
+ nullptr,
+ sizeof(bytes),
+ 0,
+ lldb::eEncodingUint,
+ lldb::eFormatHex,
+ {0, 0, 0, LLDB_INVALID_REGNUM, 0},
+ nullptr,
+ nullptr,
+ nullptr};
+ DataExtractor data;
+
+ RegisterValue invalid_source(llvm::ArrayRef(bytes), lldb::eByteOrderInvalid);
+ EXPECT_FALSE(invalid_source.GetData(data, reg_info, lldb::eByteOrderLittle));
+
+ RegisterValue pdp_source(llvm::ArrayRef(bytes), lldb::eByteOrderPDP);
+ EXPECT_FALSE(pdp_source.GetData(data, reg_info, lldb::eByteOrderLittle));
+
+ RegisterValue little_source(llvm::ArrayRef(bytes), lldb::eByteOrderLittle);
+ EXPECT_FALSE(little_source.GetData(data, reg_info, lldb::eByteOrderInvalid));
+ EXPECT_FALSE(little_source.GetData(data, reg_info, lldb::eByteOrderPDP));
+}
+
+TEST(RegisterValueTest, GetScalarDataInTargetByteOrder) {
+ RegisterInfo reg_info{"test",
+ nullptr,
+ 8,
+ 0,
+ lldb::eEncodingUint,
+ lldb::eFormatHex,
+ {0, 0, 0, LLDB_INVALID_REGNUM, 0},
+ nullptr,
+ nullptr,
+ nullptr};
+ RegisterValue value(uint64_t{0x0102030405060708});
+ DataExtractor target_data;
+
+ uint8_t big_endian_bytes[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08};
+ ASSERT_TRUE(value.GetData(target_data, reg_info, lldb::eByteOrderBig));
+ EXPECT_EQ(
+ llvm::ArrayRef(target_data.GetDataStart(), target_data.GetByteSize()),
+ llvm::ArrayRef(big_endian_bytes));
+
+ uint8_t little_endian_bytes[] = {0x08, 0x07, 0x06, 0x05,
+ 0x04, 0x03, 0x02, 0x01};
+ ASSERT_TRUE(value.GetData(target_data, reg_info, lldb::eByteOrderLittle));
+ EXPECT_EQ(
+ llvm::ArrayRef(target_data.GetDataStart(), target_data.GetByteSize()),
+ llvm::ArrayRef(little_endian_bytes));
+}
+
void TestSetValueFromData(const Scalar &etalon, void *src, size_t
src_byte_size,
const lldb::ByteOrder endianness,
const RegisterValue::Type register_value_type) {
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits