[Lldb-commits] [lldb] [lldb] Add array decaying (PR #210918)
Georgiy Samoylov via lldb-commits
lldb-commits at lists.llvm.org
Tue Jul 21 02:31:42 PDT 2026
https://github.com/sga-sc created https://github.com/llvm/llvm-project/pull/210918
In C, an array name in an expression "decays" into a pointer to its first element. LLDB did not honor this: commands like `memory read my_array` did not work correctly, because an aggregate type has no scalar value, so trying to obtain one (ResolveValue/GetValueAsUnsigned) failed.
This MR adds explicit array decay: for array-typed expressions, the address of the array object itself is used instead of its (non-existent) scalar value.
>From 13836af9cef991065b9006f74a68192450fd20e8 Mon Sep 17 00:00:00 2001
From: Georgiy Samoylov <g.samoylov at syntacore.com>
Date: Tue, 14 Jul 2026 17:52:43 +0300
Subject: [PATCH 1/2] 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 dcc983b68dbd5cc135c705bf2186befdd320d208 Mon Sep 17 00:00:00 2001
From: Georgiy Samoylov <g.samoylov at syntacore.com>
Date: Tue, 14 Jul 2026 17:42:43 +0300
Subject: [PATCH 2/2] Add case of decaying to TestMemoryRead.py
---
.../commands/memory/read/TestMemoryRead.py | 38 +++++++++++++++++++
1 file changed, 38 insertions(+)
diff --git a/lldb/test/API/commands/memory/read/TestMemoryRead.py b/lldb/test/API/commands/memory/read/TestMemoryRead.py
index bc8c45a402423..2a3268f38e600 100644
--- a/lldb/test/API/commands/memory/read/TestMemoryRead.py
+++ b/lldb/test/API/commands/memory/read/TestMemoryRead.py
@@ -144,6 +144,44 @@ 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 "
+
+ 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