[Lldb-commits] [lldb] [lldb] Add array decaying (PR #210918)
Georgiy Samoylov via lldb-commits
lldb-commits at lists.llvm.org
Wed Jul 22 01:46:19 PDT 2026
https://github.com/sga-sc updated https://github.com/llvm/llvm-project/pull/210918
>From 294b4c8d8f5b76232086f78414c735f16f7f0b75 Mon Sep 17 00:00:00 2001
From: Georgiy Samoylov <Ignitor21838 at gmail.com>
Date: Wed, 22 Jul 2026 11:42:33 +0300
Subject: [PATCH 1/2] [lldb] Add decaying of arrays name
---
.../source/Interpreter/CommandInterpreter.cpp | 19 ++++++++++++++++++-
lldb/source/Interpreter/OptionArgParser.cpp | 15 +++++++++++++--
2 files changed, 31 insertions(+), 3 deletions(-)
diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp
index 9887d24112c20..08de320041153 100644
--- a/lldb/source/Interpreter/CommandInterpreter.cpp
+++ b/lldb/source/Interpreter/CommandInterpreter.cpp
@@ -51,6 +51,7 @@
#include "lldb/Core/PluginManager.h"
#include "lldb/Core/Telemetry.h"
#include "lldb/Host/StreamFile.h"
+#include "lldb/Symbol/CompilerType.h"
#include "lldb/Utility/ErrorMessages.h"
#include "lldb/Utility/FileSpec.h"
#include "lldb/Utility/LLDBLog.h"
@@ -59,6 +60,7 @@
#include "lldb/Utility/Stream.h"
#include "lldb/Utility/StructuredData.h"
#include "lldb/Utility/Timer.h"
+#include "lldb/ValueObject/ValueObject.h"
#include "lldb/Host/Config.h"
#include "lldb/lldb-forward.h"
@@ -2001,7 +2003,22 @@ Status CommandInterpreter::PreprocessToken(std::string &expr_str) {
expr_result_valobj_sp =
expr_result_valobj_sp->GetQualifiedRepresentationIfAvailable(
expr_result_valobj_sp->GetDynamicValueType(), true);
- if (expr_result_valobj_sp->ResolveValue(scalar)) {
+ // For array-typed results, C decays the array to a pointer to its first
+ // element. ResolveValue() can't produce a scalar for an aggregate, and the
+ // expression evaluator materializes arrays into a temporary result buffer
+ // whose address is not the array's real location. So decay explicitly here:
+ // use the address of the array object instead of its (non-existent) scalar
+ // value.
+ if (expr_result_valobj_sp &&
+ expr_result_valobj_sp->GetCompilerType().IsArrayType()) {
+ lldb::addr_t addr =
+ expr_result_valobj_sp->GetAddressOf(/*scalar_is_load_address=*/true)
+ .address;
+ if (addr != LLDB_INVALID_ADDRESS)
+ scalar = addr;
+ }
+
+ if (scalar.IsValid() || expr_result_valobj_sp->ResolveValue(scalar)) {
StreamString value_strm;
const bool show_type = false;
diff --git a/lldb/source/Interpreter/OptionArgParser.cpp b/lldb/source/Interpreter/OptionArgParser.cpp
index 170f65ad80a74..bdbf9859faa09 100644
--- a/lldb/source/Interpreter/OptionArgParser.cpp
+++ b/lldb/source/Interpreter/OptionArgParser.cpp
@@ -8,6 +8,7 @@
#include "lldb/Interpreter/OptionArgParser.h"
#include "lldb/DataFormatters/FormatManager.h"
+#include "lldb/Symbol/CompilerType.h"
#include "lldb/Target/ABI.h"
#include "lldb/Target/RegisterContext.h"
#include "lldb/Target/Target.h"
@@ -231,8 +232,18 @@ OptionArgParser::DoToAddress(const ExecutionContext *exe_ctx, llvm::StringRef s,
valobj_sp = valobj_sp->GetQualifiedRepresentationIfAvailable(
valobj_sp->GetDynamicValueType(), true);
// Get the address to watch.
- if (valobj_sp)
- addr = valobj_sp->GetValueAsUnsigned(0, &success);
+ if (valobj_sp) {
+ // In C an array decays to a pointer to its first element, whose value is
+ // the address of the array object itself. An aggregate has no scalar
+ // value, so GetValueAsUnsigned() would fail here; use the array's own
+ // load address instead.
+ if (valobj_sp->GetCompilerType().IsArrayType()) {
+ addr = valobj_sp->GetAddressOf(/*scalar_is_load_address=*/true).address;
+ success = addr != LLDB_INVALID_ADDRESS;
+ } else {
+ addr = valobj_sp->GetValueAsUnsigned(0, &success);
+ }
+ }
if (success) {
if (error_ptr)
error_ptr->Clear();
>From 640d20b32c92f15fb8e32d7fe48443f57012647d Mon Sep 17 00:00:00 2001
From: Georgiy Samoylov <Ignitor21838 at gmail.com>
Date: Wed, 22 Jul 2026 11:42:43 +0300
Subject: [PATCH 2/2] [lldb][test] Add case of decaying to TestMemoryRead.py
---
.../commands/memory/read/TestMemoryRead.py | 42 +++++++++++++++++++
1 file changed, 42 insertions(+)
diff --git a/lldb/test/API/commands/memory/read/TestMemoryRead.py b/lldb/test/API/commands/memory/read/TestMemoryRead.py
index bc8c45a402423..57ca7069c5ce3 100644
--- a/lldb/test/API/commands/memory/read/TestMemoryRead.py
+++ b/lldb/test/API/commands/memory/read/TestMemoryRead.py
@@ -144,6 +144,48 @@ def test_memory_read(self):
self.assertEqual(len(o), expected_object_length)
self.assertEqual(len(objects_read), 4)
+ def test_memory_read_array_name_decays_to_address(self):
+ """An array expression decays to a pointer to its first element, so
+ `memory read <array>` and `memory read `<array>`` must behave like
+ `memory read &<array>`, rather than failing or reading a bogus address."""
+ self.build_run_stop()
+
+ fmt = "memory read --format uint32_t[] --size 4 --count 3 "
+
+ # Parses the output of the command above, which looks like:
+ # 0x7fff5fbff990: {0x00000002}
+ # 0x7fff5fbff994: {0x00000004}
+ # 0x7fff5fbff998: {0x00000006}
+ # i.e. "<address>: {<value>}" per line. We return the address of the
+ # first line and the list of values across all lines.
+ def read_addr_and_values(suffix):
+ self.runCmd(fmt + suffix)
+ lines = self.res.GetOutput().splitlines()
+ addr = int(lines[0].split(":")[0], 0)
+ values = []
+ for line in lines:
+ values.extend(v.strip(" {}") for v in line.split(":")[1].split())
+ return addr, values
+
+ # Taking the address explicitly yields a pointer whose value is the
+ # array's address; this is the reference every other form must match.
+ ref_addr, ref_values = read_addr_and_values("&my_ints")
+ self.assertEqual(ref_values, ["0x00000002", "0x00000004", "0x00000006"])
+
+ # Every one of these forms must resolve to the same address and data:
+ # &my_ints pointer, via OptionArgParser::ToAddress
+ # `&my_ints` pointer, via CommandInterpreter::PreprocessToken
+ # my_ints array decay, via OptionArgParser::ToAddress
+ # `my_ints` array decay, via CommandInterpreter::PreprocessToken
+ for suffix in ["&my_ints", "`&my_ints`", "my_ints", "`my_ints`"]:
+ addr, values = read_addr_and_values(suffix)
+ self.assertEqual(
+ addr, ref_addr, "wrong address for 'memory read %s'" % suffix
+ )
+ self.assertEqual(
+ values, ref_values, "wrong data for 'memory read %s'" % suffix
+ )
+
def test_memory_read_file(self):
self.build_run_stop()
res = lldb.SBCommandReturnObject()
More information about the lldb-commits
mailing list