https://github.com/kastiglione updated https://github.com/llvm/llvm-project/pull/181199
>From 40eabae47e330e17a5e1419919f786d4840654ff Mon Sep 17 00:00:00 2001 From: Dave Lee <[email protected]> Date: Thu, 12 Feb 2026 10:29:20 -0800 Subject: [PATCH 1/3] [lldb] Enable caching for BytecodeSyntheticChildren::FrontEnd::Update --- lldb/source/DataFormatters/TypeSynthetic.cpp | 34 ++++++++++++++++++- .../TestBytecodeSynthetic.py | 21 +++++++++++- .../bytecode-synthetic/main.cpp | 5 ++- 3 files changed, 57 insertions(+), 3 deletions(-) diff --git a/lldb/source/DataFormatters/TypeSynthetic.cpp b/lldb/source/DataFormatters/TypeSynthetic.cpp index 10655cdca6f05..32d035eb8a90b 100644 --- a/lldb/source/DataFormatters/TypeSynthetic.cpp +++ b/lldb/source/DataFormatters/TypeSynthetic.cpp @@ -22,6 +22,7 @@ #include "lldb/Utility/StreamString.h" #include "llvm/Support/Error.h" #include <cstdint> +#include <optional> using namespace lldb; using namespace lldb_private; @@ -291,10 +292,41 @@ lldb::ChildCacheState BytecodeSyntheticChildren::FrontEnd::Update() { return ChildCacheState::eRefetch; } + // If the top of the stack is a 0 or 1, that value is popped of the stack and + // treated as a return value of eRefetch or eReuse respectively. If the top of + // the stack is any other value, it stays on the stack and becomes part of + // `self`. + // + // This dynamic logic can lead to errors for synthetic formatter authors. + // Consider the case where an `update` implementation places the number of + // children last on the stack. LLDB will _sometimes_ (but not always) consume + // that value as a ChildCacheState value. This would cause downstream problems + // in `num_children`, because the count won't be on the stack. + // + // Bytecode authors are encouraged to explicitly push a ChildCacheState value + // on to the stack. + std::optional<ChildCacheState> cache_state = std::nullopt; + const FormatterBytecode::DataStackElement &top = data.back(); + if (auto *u = std::get_if<uint64_t>(&top)) + if (*u == 0 || *u == 1) + cache_state = static_cast<ChildCacheState>(*u); + if (auto *i = std::get_if<int64_t>(&top)) + if (*i == 0 || *i == 1) + cache_state = static_cast<ChildCacheState>(*i); + + if (cache_state) { + data.pop_back(); + if (cache_state == ChildCacheState::eReuse) + LLDB_LOG(GetLog(LLDBLog::DataFormatters), + "Bytecode formatter returned eReuse from `update` (type: `{0}`, " + "name: `{1}`)", + m_backend.GetDisplayTypeName(), m_backend.GetName()); + } + if (data.size() > 0) m_self = std::move(data); - return ChildCacheState::eRefetch; + return cache_state.value_or(ChildCacheState::eRefetch); } llvm::Expected<uint32_t> diff --git a/lldb/test/API/functionalities/data-formatter/bytecode-synthetic/TestBytecodeSynthetic.py b/lldb/test/API/functionalities/data-formatter/bytecode-synthetic/TestBytecodeSynthetic.py index c5c2811c59439..cd4be83ad8563 100644 --- a/lldb/test/API/functionalities/data-formatter/bytecode-synthetic/TestBytecodeSynthetic.py +++ b/lldb/test/API/functionalities/data-formatter/bytecode-synthetic/TestBytecodeSynthetic.py @@ -5,8 +5,9 @@ class TestCase(TestBase): + @skipUnlessDarwin - def test(self): + def test_synthetic(self): self.build() if self.TraceOn(): self.expect("log enable -v lldb formatters") @@ -21,3 +22,21 @@ def test(self): self.assertEqual(account.child[0].name, "username") self.expect("v acc", matching=False, substrs=["password"]) + + @skipUnlessDarwin + def test_update_reuse(self): + self.build() + + log = self.getBuildArtifact("formatter.log") + self.runCmd(f"log enable lldb formatters -f {log}") + + _, _, thread, _ = lldbutil.run_to_source_breakpoint( + self, "break here", lldb.SBFileSpec("main.cpp") + ) + + frame = thread.selected_frame + account = frame.var("acc") + self.assertEqual(account.num_children, 1) + + self.filecheck(f"platform shell cat {log}", __file__) + # CHECK: Bytecode formatter returned eReuse from `update` (type: `Account`, name: `acc`) diff --git a/lldb/test/API/functionalities/data-formatter/bytecode-synthetic/main.cpp b/lldb/test/API/functionalities/data-formatter/bytecode-synthetic/main.cpp index 3e6ae77bbca83..051e44c937bed 100644 --- a/lldb/test/API/functionalities/data-formatter/bytecode-synthetic/main.cpp +++ b/lldb/test/API/functionalities/data-formatter/bytecode-synthetic/main.cpp @@ -16,10 +16,13 @@ int main(int argc, char **argv) { __attribute__((used, section("__DATA_CONST,__lldbformatters"))) unsigned char _Account_synthetic[] = "\x01" // version - "\x15" // remaining record size + "\x19" // remaining record size "\x07" // type name size "Account" // type name "\x00" // flags + "\x06" // sig_update + "\x02" // program size + "\x20\x01" // `return eReuse` "\x02" // sig_get_num_children "\x02" // program size "\x20\x01" // `return 1` >From 8b2f81906d427b2399f0db9a5bd5ecd34f6c9e35 Mon Sep 17 00:00:00 2001 From: Dave Lee <[email protected]> Date: Thu, 12 Feb 2026 10:33:26 -0800 Subject: [PATCH 2/3] python formatting --- .../data-formatter/bytecode-synthetic/TestBytecodeSynthetic.py | 1 - 1 file changed, 1 deletion(-) diff --git a/lldb/test/API/functionalities/data-formatter/bytecode-synthetic/TestBytecodeSynthetic.py b/lldb/test/API/functionalities/data-formatter/bytecode-synthetic/TestBytecodeSynthetic.py index cd4be83ad8563..a90415a02f99e 100644 --- a/lldb/test/API/functionalities/data-formatter/bytecode-synthetic/TestBytecodeSynthetic.py +++ b/lldb/test/API/functionalities/data-formatter/bytecode-synthetic/TestBytecodeSynthetic.py @@ -5,7 +5,6 @@ class TestCase(TestBase): - @skipUnlessDarwin def test_synthetic(self): self.build() >From e5f73b34d3e3e297b2e7195fa791ffd8087cf2fd Mon Sep 17 00:00:00 2001 From: Dave Lee <[email protected]> Date: Tue, 17 Feb 2026 17:58:26 -0800 Subject: [PATCH 3/3] Always return eReuse from Update() --- lldb/source/DataFormatters/TypeSynthetic.cpp | 36 ++----------------- .../TestBytecodeSynthetic.py | 20 +---------- .../bytecode-synthetic/main.cpp | 5 +-- 3 files changed, 5 insertions(+), 56 deletions(-) diff --git a/lldb/source/DataFormatters/TypeSynthetic.cpp b/lldb/source/DataFormatters/TypeSynthetic.cpp index 32d035eb8a90b..7409dbf572d16 100644 --- a/lldb/source/DataFormatters/TypeSynthetic.cpp +++ b/lldb/source/DataFormatters/TypeSynthetic.cpp @@ -22,7 +22,6 @@ #include "lldb/Utility/StreamString.h" #include "llvm/Support/Error.h" #include <cstdint> -#include <optional> using namespace lldb; using namespace lldb_private; @@ -292,41 +291,12 @@ lldb::ChildCacheState BytecodeSyntheticChildren::FrontEnd::Update() { return ChildCacheState::eRefetch; } - // If the top of the stack is a 0 or 1, that value is popped of the stack and - // treated as a return value of eRefetch or eReuse respectively. If the top of - // the stack is any other value, it stays on the stack and becomes part of - // `self`. - // - // This dynamic logic can lead to errors for synthetic formatter authors. - // Consider the case where an `update` implementation places the number of - // children last on the stack. LLDB will _sometimes_ (but not always) consume - // that value as a ChildCacheState value. This would cause downstream problems - // in `num_children`, because the count won't be on the stack. - // - // Bytecode authors are encouraged to explicitly push a ChildCacheState value - // on to the stack. - std::optional<ChildCacheState> cache_state = std::nullopt; - const FormatterBytecode::DataStackElement &top = data.back(); - if (auto *u = std::get_if<uint64_t>(&top)) - if (*u == 0 || *u == 1) - cache_state = static_cast<ChildCacheState>(*u); - if (auto *i = std::get_if<int64_t>(&top)) - if (*i == 0 || *i == 1) - cache_state = static_cast<ChildCacheState>(*i); - - if (cache_state) { - data.pop_back(); - if (cache_state == ChildCacheState::eReuse) - LLDB_LOG(GetLog(LLDBLog::DataFormatters), - "Bytecode formatter returned eReuse from `update` (type: `{0}`, " - "name: `{1}`)", - m_backend.GetDisplayTypeName(), m_backend.GetName()); - } - if (data.size() > 0) m_self = std::move(data); - return cache_state.value_or(ChildCacheState::eRefetch); + // At this time it is assumed that synthetic bytecode formatters can always + // reuse the work performed in `update`. + return ChildCacheState::eReuse; } llvm::Expected<uint32_t> diff --git a/lldb/test/API/functionalities/data-formatter/bytecode-synthetic/TestBytecodeSynthetic.py b/lldb/test/API/functionalities/data-formatter/bytecode-synthetic/TestBytecodeSynthetic.py index a90415a02f99e..c5c2811c59439 100644 --- a/lldb/test/API/functionalities/data-formatter/bytecode-synthetic/TestBytecodeSynthetic.py +++ b/lldb/test/API/functionalities/data-formatter/bytecode-synthetic/TestBytecodeSynthetic.py @@ -6,7 +6,7 @@ class TestCase(TestBase): @skipUnlessDarwin - def test_synthetic(self): + def test(self): self.build() if self.TraceOn(): self.expect("log enable -v lldb formatters") @@ -21,21 +21,3 @@ def test_synthetic(self): self.assertEqual(account.child[0].name, "username") self.expect("v acc", matching=False, substrs=["password"]) - - @skipUnlessDarwin - def test_update_reuse(self): - self.build() - - log = self.getBuildArtifact("formatter.log") - self.runCmd(f"log enable lldb formatters -f {log}") - - _, _, thread, _ = lldbutil.run_to_source_breakpoint( - self, "break here", lldb.SBFileSpec("main.cpp") - ) - - frame = thread.selected_frame - account = frame.var("acc") - self.assertEqual(account.num_children, 1) - - self.filecheck(f"platform shell cat {log}", __file__) - # CHECK: Bytecode formatter returned eReuse from `update` (type: `Account`, name: `acc`) diff --git a/lldb/test/API/functionalities/data-formatter/bytecode-synthetic/main.cpp b/lldb/test/API/functionalities/data-formatter/bytecode-synthetic/main.cpp index 051e44c937bed..3e6ae77bbca83 100644 --- a/lldb/test/API/functionalities/data-formatter/bytecode-synthetic/main.cpp +++ b/lldb/test/API/functionalities/data-formatter/bytecode-synthetic/main.cpp @@ -16,13 +16,10 @@ int main(int argc, char **argv) { __attribute__((used, section("__DATA_CONST,__lldbformatters"))) unsigned char _Account_synthetic[] = "\x01" // version - "\x19" // remaining record size + "\x15" // remaining record size "\x07" // type name size "Account" // type name "\x00" // flags - "\x06" // sig_update - "\x02" // program size - "\x20\x01" // `return eReuse` "\x02" // sig_get_num_children "\x02" // program size "\x20\x01" // `return 1` _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
