[Lldb-commits] [lldb] 45e9b1a - [lldb] Display GDB remote register vector values (#222724)
via lldb-commits
lldb-commits at lists.llvm.org
Mon Sep 14 10:57:21 PDT 2026
Author: Bar Soloveychik
Date: 2026-09-14T10:57:15-07:00
New Revision: 45e9b1a1baea960e4a0d549df558ee0333c9edd4
URL: https://github.com/llvm/llvm-project/commit/45e9b1a1baea960e4a0d549df558ee0333c9edd4
DIFF: https://github.com/llvm/llvm-project/commit/45e9b1a1baea960e4a0d549df558ee0333c9edd4.diff
LOG: [lldb] Display GDB remote register vector values (#222724)
This completes GDB remote vector support in LLDB’s CLI.
- `register read <vector>` prints the raw register followed by typed
lanes.
- Supports direct, nested, three lane, byte, bool, and pointer vectors.
- Handles target byte order correctly.
- Explicit `--format` continues to show only the requested format.
- Bulk register reads remain compact.
- Unsupported flag widths safely remain raw.
Examples:
```
(lldb) register read v0
v0 = {0x00 0x00 0xc0 0x3f 0x00 0x00 0x20 0x40 0x00 0x00 0x60 0x40 0x00 0x00 0x90 0x40}
= ([0] = 1.5, [1] = 2.5, [2] = 3.5, [3] = 4.5)
(lldb) register read nested
nested = {0x00 0x00 0xc0 0x3f 0x00 0x00 0x20 0x40 0x00 0x00 0x60 0x40 0x00 0x00 0x90 0x40}
= {
[0] = ([0] = 1.5, [1] = 2.5)
[1] = ([0] = 3.5, [1] = 4.5)
}
(lldb) register read v3
v3 = {0x00 0x00 0xc0 0x3f 0x00 0x00 0x20 0x40 0x00 0x00 0x60 0x40}
= ([0] = 1.5, [1] = 2.5, [2] = 3.5)
(lldb) register read bytes
bytes = {0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19 0x1a 0x1b 0x1c 0x1d 0x1e 0x1f}
= ([0] = 0x00, [1] = 0x01, [2] = 0x02, [3] = 0x03, [4] = 0x04, [5] = 0x05, [6] = 0x06, [7] = 0x07, [8] = 0x08, [9] = 0x09, [10] = 0x0a, [11] = 0x0b, [12] = 0x0c, [13] = 0x0d, [14] = 0x0e, [15] = 0x0f, [16] = 0x10, [17] = 0x11, [18] = 0x12, [19] = 0x13, [20] = 0x14, [21] = 0x15, [22] = 0x16, [23] = 0x17, ...)
(lldb) register read bools
bools = {0x00 0x00 0xc0 0x3f 0xff 0xff 0xff 0xff 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00}
= {
[0] = false
[1] = false
[2] = true
[3] = true
[4] = true
[5] = true
[6] = true
[7] = true
[8] = false
[9] = false
[10] = false
[11] = false
[12] = false
[13] = false
[14] = false
[15] = false
}
(lldb) register read ptrs
ptrs = {0x00 0x00 0xc0 0x3f 0x00 0x00 0x20 0x40 0x00 0x00 0x60 0x40 0x00 0x00 0x90 0x40}
= ([0] = 0x402000003fc00000, [1] = 0x4090000040600000)
(lldb) register read -f hex v0
v0 = 0x4090000040600000402000003fc00000
```
---------
Co-authored-by: Bar Soloveychik <barsolo at fb.com>
Added:
Modified:
lldb/include/lldb/Core/DumpRegisterValue.h
lldb/source/Commands/CommandObjectRegister.cpp
lldb/source/Core/DumpRegisterValue.cpp
lldb/test/API/functionalities/gdb_remote_client/TestXMLRegisterFlags.py
lldb/test/API/functionalities/gdb_remote_client/TestXMLRegisterVector.py
Removed:
################################################################################
diff --git a/lldb/include/lldb/Core/DumpRegisterValue.h b/lldb/include/lldb/Core/DumpRegisterValue.h
index b89b35ac0b4ab..d24c46b815bc7 100644
--- a/lldb/include/lldb/Core/DumpRegisterValue.h
+++ b/lldb/include/lldb/Core/DumpRegisterValue.h
@@ -22,14 +22,14 @@ class Stream;
// The default value of 0 for reg_name_right_align_at means no alignment at
// all.
-// Set print_flags to true to print register fields if they are available.
+// Set print_type to true to print a structured value if one is available.
// If you do so, target_sp must be non-null for it to work.
void DumpRegisterValue(const RegisterValue ®_val, Stream &s,
const RegisterInfo ®_info, bool prefix_with_name,
bool prefix_with_alt_name, lldb::Format format,
uint32_t reg_name_right_align_at = 0,
ExecutionContextScope *exe_scope = nullptr,
- bool print_flags = false,
+ bool print_type = false,
lldb::TargetSP target_sp = nullptr);
} // namespace lldb_private
diff --git a/lldb/source/Commands/CommandObjectRegister.cpp b/lldb/source/Commands/CommandObjectRegister.cpp
index 83ff915c7b53b..192cef6a2a53b 100644
--- a/lldb/source/Commands/CommandObjectRegister.cpp
+++ b/lldb/source/Commands/CommandObjectRegister.cpp
@@ -100,8 +100,8 @@ class CommandObjectRegisterRead : public CommandObjectParsed {
eCommandProcessMustBeLaunched | eCommandProcessMustBePaused),
m_format_options(eFormatDefault, UINT64_MAX, UINT64_MAX,
{{CommandArgumentType::eArgTypeFormat,
- "Specify a format to be used for display. If this "
- "is set, register fields will not be displayed."}}) {
+ "Specify a format to be used for display. This "
+ "overrides any other register type information."}}) {
AddSimpleArgumentList(eArgTypeRegisterName, eArgRepeatStar);
// Add the "--format"
@@ -127,7 +127,7 @@ class CommandObjectRegisterRead : public CommandObjectParsed {
bool DumpRegister(const ExecutionContext &exe_ctx, Stream &strm,
RegisterContext ®_ctx, const RegisterInfo ®_info,
- bool print_flags, size_t reg_name_right_align_at) {
+ bool print_type, size_t reg_name_right_align_at) {
RegisterValue reg_value;
if (!reg_ctx.ReadRegister(®_info, reg_value))
return false;
@@ -139,7 +139,7 @@ class CommandObjectRegisterRead : public CommandObjectParsed {
DumpRegisterValue(reg_value, strm, reg_info, prefix_with_name,
prefix_with_altname, m_format_options.GetFormat(),
reg_name_right_align_at,
- exe_ctx.GetBestExecutionContextScope(), print_flags,
+ exe_ctx.GetBestExecutionContextScope(), print_type,
exe_ctx.GetTargetSP());
if ((reg_info.encoding == eEncodingUint) ||
(reg_info.encoding == eEncodingSint)) {
@@ -187,7 +187,7 @@ class CommandObjectRegisterRead : public CommandObjectParsed {
if (reg_info &&
DumpRegister(exe_ctx, strm, *reg_ctx, *reg_info,
- /*print_flags=*/false, reg_name_right_align_at))
+ /*print_type=*/false, reg_name_right_align_at))
++available_count;
else
++unavailable_count;
@@ -267,10 +267,9 @@ class CommandObjectRegisterRead : public CommandObjectParsed {
if (const RegisterInfo *reg_info =
reg_ctx->GetRegisterInfoByName(arg_str)) {
// If they have asked for a specific format don't obscure that by
- // printing flags afterwards.
- bool print_flags =
- !m_format_options.GetFormatValue().OptionWasSet();
- if (!DumpRegister(m_exe_ctx, strm, *reg_ctx, *reg_info, print_flags,
+ // printing a structured value afterwards.
+ bool print_type = !m_format_options.GetFormatValue().OptionWasSet();
+ if (!DumpRegister(m_exe_ctx, strm, *reg_ctx, *reg_info, print_type,
reg_name_right_align_at))
strm.Printf("%-12s = error: unavailable\n", reg_info->name);
} else {
diff --git a/lldb/source/Core/DumpRegisterValue.cpp b/lldb/source/Core/DumpRegisterValue.cpp
index c79af2084d2ec..dff81f9bdbe90 100644
--- a/lldb/source/Core/DumpRegisterValue.cpp
+++ b/lldb/source/Core/DumpRegisterValue.cpp
@@ -11,6 +11,7 @@
#include "lldb/DataFormatters/DumpValueObjectOptions.h"
#include "lldb/Utility/DataExtractor.h"
#include "lldb/Utility/Endian.h"
+#include "lldb/Utility/RegisterType.h"
#include "lldb/Utility/RegisterTypeFlags.h"
#include "lldb/Utility/RegisterValue.h"
#include "lldb/Utility/StreamString.h"
@@ -60,13 +61,26 @@ static void dump_type_value(const lldb_private::RegisterTypeFlags &flags_type,
strm << "error: " << toString(std::move(error));
}
+static void dump_type_value(lldb_private::CompilerType &type,
+ const lldb_private::DataExtractor &data_extractor,
+ lldb_private::ExecutionContextScope *exe_scope,
+ lldb_private::Stream &strm) {
+ lldb::ValueObjectSP vobj_sp = lldb_private::ValueObjectConstResult::Create(
+ exe_scope, type, lldb_private::ConstString(), data_extractor);
+ lldb_private::DumpValueObjectOptions dump_options;
+ dump_options.SetHideRootType(true).SetShowSummary(false);
+
+ if (llvm::Error error = vobj_sp->Dump(strm, dump_options))
+ strm << "error: " << toString(std::move(error));
+}
+
void lldb_private::DumpRegisterValue(const RegisterValue ®_val, Stream &s,
const RegisterInfo ®_info,
bool prefix_with_name,
bool prefix_with_alt_name, Format format,
uint32_t reg_name_right_align_at,
ExecutionContextScope *exe_scope,
- bool print_flags, TargetSP target_sp) {
+ bool print_type, TargetSP target_sp) {
DataExtractor data;
if (!reg_val.GetData(data))
return;
@@ -123,10 +137,15 @@ void lldb_private::DumpRegisterValue(const RegisterValue ®_val, Stream &s,
0, // item_bit_offset
exe_scope);
+ if (!print_type || !exe_scope || !target_sp)
+ return;
+
const RegisterTypeFlags *flags_type =
llvm::dyn_cast_if_present<RegisterTypeFlags>(reg_info.register_type);
- if (!print_flags || !flags_type || !exe_scope || !target_sp ||
- (reg_info.byte_size != 4 && reg_info.byte_size != 8))
+ if (!flags_type &&
+ !llvm::isa_and_present<RegisterTypeVector>(reg_info.register_type))
+ return;
+ if (flags_type && reg_info.byte_size != 4 && reg_info.byte_size != 8)
return;
CompilerType register_compiler_type = target_sp->GetRegisterType(reg_info);
@@ -136,12 +155,27 @@ void lldb_private::DumpRegisterValue(const RegisterValue ®_val, Stream &s,
// Use a new stream so we can remove a trailing newline later.
StreamString register_type_stream;
- if (reg_info.byte_size == 4) {
- dump_type_value(*flags_type, register_compiler_type, reg_val.GetAsUInt32(),
- exe_scope, register_type_stream);
+ if (flags_type) {
+ if (reg_info.byte_size == 4) {
+ dump_type_value(*flags_type, register_compiler_type,
+ reg_val.GetAsUInt32(), exe_scope, register_type_stream);
+ } else if (reg_info.byte_size == 8) {
+ dump_type_value(*flags_type, register_compiler_type,
+ reg_val.GetAsUInt64(), exe_scope, register_type_stream);
+ }
} else {
- dump_type_value(*flags_type, register_compiler_type, reg_val.GetAsUInt64(),
- exe_scope, register_type_stream);
+ lldb::ProcessSP process_sp = exe_scope->CalculateProcess();
+ if (!process_sp)
+ return;
+
+ // ValueObjectConstResult interprets its data using the target's layout.
+ DataExtractor structured_data;
+ if (!reg_val.GetData(structured_data, reg_info.byte_size,
+ process_sp->GetByteOrder()))
+ return;
+ structured_data.SetAddressByteSize(process_sp->GetAddressByteSize());
+ dump_type_value(register_compiler_type, structured_data, exe_scope,
+ register_type_stream);
}
// Registers are indented like:
diff --git a/lldb/test/API/functionalities/gdb_remote_client/TestXMLRegisterFlags.py b/lldb/test/API/functionalities/gdb_remote_client/TestXMLRegisterFlags.py
index 9f10fd5172d4a..da2a75540aa78 100644
--- a/lldb/test/API/functionalities/gdb_remote_client/TestXMLRegisterFlags.py
+++ b/lldb/test/API/functionalities/gdb_remote_client/TestXMLRegisterFlags.py
@@ -96,6 +96,21 @@ def test_no_flags(self):
self.setup_flags_test("")
self.expect("register read cpsr", substrs=["= 0xeeee7777"])
+ @skipIfXmlSupportMissing
+ @skipIfRemote
+ def test_unsupported_flags_size_stays_raw(self):
+ self.setup_register_test(
+ """\
+ <flags id="flags24" size="3">
+ <field name="field" start="0" end="0"/>
+ </flags>
+ <reg name="flags24" regnum="0" bitsize="24" type="flags24"/>
+ <reg name="pc" bitsize="64"/>"""
+ )
+
+ self.expect("register read flags24", substrs=["flags24 = 0x777777"])
+ self.expect("register read flags24", matching=False, substrs=["field ="])
+
@skipIfXmlSupportMissing
@skipIfRemote
def test_single_field_pad_msb(self):
diff --git a/lldb/test/API/functionalities/gdb_remote_client/TestXMLRegisterVector.py b/lldb/test/API/functionalities/gdb_remote_client/TestXMLRegisterVector.py
index 5f275a04944dc..224ca99ae7364 100644
--- a/lldb/test/API/functionalities/gdb_remote_client/TestXMLRegisterVector.py
+++ b/lldb/test/API/functionalities/gdb_remote_client/TestXMLRegisterVector.py
@@ -408,3 +408,67 @@ def test_big_endian_vector_sb_api(self):
vector = frame.FindRegister(name)
self.assert_float_children(vector, [1.5, 2.5])
self.assertEqual(vector.Cast(ull).GetValueAsUnsigned(), 0x3FC0000040200000)
+
+ @skipIfXmlSupportMissing
+ @skipIfRemote
+ def test_direct_vector_cli(self):
+ 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,
+ )
+
+ self.expect(
+ "register read v0",
+ substrs=[
+ "v0 = {0x00 0x00 0xc0 0x3f 0x00 0x00 0x20 0x40 "
+ "0x00 0x00 0x60 0x40 0x00 0x00 0x90 0x40}\n"
+ " = ([0] = 1.5, [1] = 2.5, [2] = 3.5, [3] = 4.5)"
+ ],
+ )
+
+ @skipIfXmlSupportMissing
+ @skipIfRemote
+ def test_nested_vector_cli(self):
+ 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,
+ )
+
+ self.expect(
+ "register read v0",
+ substrs=[
+ "[0] = ([0] = 1.5, [1] = 2.5)",
+ "[1] = ([0] = 3.5, [1] = 4.5)",
+ ],
+ )
+
+ @skipIfXmlSupportMissing
+ @skipIfRemote
+ @skipIfLLVMTargetMissing("SystemZ")
+ def test_big_endian_vector_cli(self):
+ 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",
+ )
+
+ self.expect(
+ "register read v0",
+ patterns=[
+ r"v0 = \{0x3f 0xc0 0x00 0x00 0x40 0x20 0x00 0x00\}\r?\n"
+ r" = \(\[0\] = 1\.5, \[1\] = 2\.5\)"
+ ],
+ )
+ self.expect("register read v1", substrs=["([0] = 1.5, [1] = 2.5)"])
More information about the lldb-commits
mailing list