[lldb] [llvm] Stateful variable-location annotations in Disassembler::PrintInstructions() (follow-up to #147460) (PR #152887)
Abdullah Mohammad Amin via llvm-commits
llvm-commits at lists.llvm.org
Sun Aug 24 15:28:35 PDT 2025
https://github.com/UltimateForce21 updated https://github.com/llvm/llvm-project/pull/152887
>From 8ed8c540e7600d720a63bc2882a81a2c65c11d41 Mon Sep 17 00:00:00 2001
From: ultimateforce21 <abdullahmohammad155 at gmail.com>
Date: Wed, 11 Jun 2025 00:11:09 -0400
Subject: [PATCH 01/48] [lldb] Add DWARFExpressionEntry and
GetExpressionEntryAtAddress() to DWARFExpressionList
This introduces a new API for retrieving DWARF expression metadata associated with variable location entries at a given PC address. It provides the base, end, and expression pointer for downstream consumers such as disassembler annotations.
Intended for use in richer instruction annotations in Instruction::Dump().
---
.../lldb/Expression/DWARFExpressionList.h | 12 +++++++++++
.../source/Expression/DWARFExpressionList.cpp | 21 +++++++++++++++++++
2 files changed, 33 insertions(+)
diff --git a/lldb/include/lldb/Expression/DWARFExpressionList.h b/lldb/include/lldb/Expression/DWARFExpressionList.h
index d8f8ec247ed56..a329b37393018 100644
--- a/lldb/include/lldb/Expression/DWARFExpressionList.h
+++ b/lldb/include/lldb/Expression/DWARFExpressionList.h
@@ -59,6 +59,18 @@ class DWARFExpressionList {
lldb::addr_t GetFuncFileAddress() { return m_func_file_addr; }
+ /// Represents an entry in the DWARFExpressionList with all needed metadata
+ struct DWARFExpressionEntry {
+ lldb::addr_t base;
+ lldb::addr_t end;
+ const DWARFExpression *expr;
+ };
+
+ /// Returns the entry (base, end, data) for a given PC address
+ llvm::Expected<DWARFExpressionEntry>
+ GetExpressionEntryAtAddress(lldb::addr_t func_load_addr,
+ lldb::addr_t load_addr) const;
+
const DWARFExpression *GetExpressionAtAddress(lldb::addr_t func_load_addr,
lldb::addr_t load_addr) const;
diff --git a/lldb/source/Expression/DWARFExpressionList.cpp b/lldb/source/Expression/DWARFExpressionList.cpp
index 04592a1eb7ff4..b55bc7120c4af 100644
--- a/lldb/source/Expression/DWARFExpressionList.cpp
+++ b/lldb/source/Expression/DWARFExpressionList.cpp
@@ -53,6 +53,27 @@ bool DWARFExpressionList::ContainsAddress(lldb::addr_t func_load_addr,
return GetExpressionAtAddress(func_load_addr, addr) != nullptr;
}
+llvm::Expected<DWARFExpressionList::DWARFExpressionEntry>
+DWARFExpressionList::GetExpressionEntryAtAddress(lldb::addr_t func_load_addr,
+ lldb::addr_t load_addr) const {
+ if (const DWARFExpression *expr = GetAlwaysValidExpr()) {
+ return DWARFExpressionEntry{0, LLDB_INVALID_ADDRESS, expr};
+ }
+
+ if (func_load_addr == LLDB_INVALID_ADDRESS)
+ func_load_addr = m_func_file_addr;
+
+ addr_t addr = load_addr - func_load_addr + m_func_file_addr;
+ uint32_t index = m_exprs.FindEntryIndexThatContains(addr);
+ if (index == UINT32_MAX) {
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "No DWARF expression found for address 0x%llx", addr);
+ }
+
+ const Entry &entry = *m_exprs.GetEntryAtIndex(index);
+ return DWARFExpressionEntry{entry.base, entry.GetRangeEnd(), &entry.data};
+}
+
const DWARFExpression *
DWARFExpressionList::GetExpressionAtAddress(lldb::addr_t func_load_addr,
lldb::addr_t load_addr) const {
>From 1db5002a69dba4f88aaac56d61520b7b4b214b01 Mon Sep 17 00:00:00 2001
From: Abdullah Mohammad Amin
<67847674+UltimateForce21 at users.noreply.github.com>
Date: Thu, 19 Jun 2025 11:55:35 -0400
Subject: [PATCH 02/48] Update
lldb/include/lldb/Expression/DWARFExpressionList.h
Co-authored-by: Jonas Devlieghere <jonas at devlieghere.com>
---
lldb/include/lldb/Expression/DWARFExpressionList.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lldb/include/lldb/Expression/DWARFExpressionList.h b/lldb/include/lldb/Expression/DWARFExpressionList.h
index a329b37393018..89e55ffc07659 100644
--- a/lldb/include/lldb/Expression/DWARFExpressionList.h
+++ b/lldb/include/lldb/Expression/DWARFExpressionList.h
@@ -59,7 +59,7 @@ class DWARFExpressionList {
lldb::addr_t GetFuncFileAddress() { return m_func_file_addr; }
- /// Represents an entry in the DWARFExpressionList with all needed metadata
+ /// Represents an entry in the DWARFExpressionList with all needed metadata.
struct DWARFExpressionEntry {
lldb::addr_t base;
lldb::addr_t end;
>From a26010b06e5067b8b3b223cbd76e8848ecb9a289 Mon Sep 17 00:00:00 2001
From: Abdullah Mohammad Amin
<67847674+UltimateForce21 at users.noreply.github.com>
Date: Thu, 19 Jun 2025 11:58:28 -0400
Subject: [PATCH 03/48] Update
lldb/include/lldb/Expression/DWARFExpressionList.h
Updated comment for GetExpressionEntryAtAddress to directly refer to struct DWARFExpressionEntry
Co-authored-by: Jonas Devlieghere <jonas at devlieghere.com>
---
lldb/include/lldb/Expression/DWARFExpressionList.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lldb/include/lldb/Expression/DWARFExpressionList.h b/lldb/include/lldb/Expression/DWARFExpressionList.h
index 89e55ffc07659..f6a269809decc 100644
--- a/lldb/include/lldb/Expression/DWARFExpressionList.h
+++ b/lldb/include/lldb/Expression/DWARFExpressionList.h
@@ -66,7 +66,7 @@ class DWARFExpressionList {
const DWARFExpression *expr;
};
- /// Returns the entry (base, end, data) for a given PC address
+ /// Returns the DWARFExpressionEntry for a given PC address.
llvm::Expected<DWARFExpressionEntry>
GetExpressionEntryAtAddress(lldb::addr_t func_load_addr,
lldb::addr_t load_addr) const;
>From 72237b75a12daa94f887f7492b2dfc141519b8a8 Mon Sep 17 00:00:00 2001
From: Abdullah Mohammad Amin
<67847674+UltimateForce21 at users.noreply.github.com>
Date: Thu, 19 Jun 2025 11:59:35 -0400
Subject: [PATCH 04/48] Update lldb/source/Expression/DWARFExpressionList.cpp
updating code style for function GetExpressionEntryAtAddress
Co-authored-by: Jonas Devlieghere <jonas at devlieghere.com>
---
lldb/source/Expression/DWARFExpressionList.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/lldb/source/Expression/DWARFExpressionList.cpp b/lldb/source/Expression/DWARFExpressionList.cpp
index b55bc7120c4af..ebf57dd457769 100644
--- a/lldb/source/Expression/DWARFExpressionList.cpp
+++ b/lldb/source/Expression/DWARFExpressionList.cpp
@@ -56,9 +56,8 @@ bool DWARFExpressionList::ContainsAddress(lldb::addr_t func_load_addr,
llvm::Expected<DWARFExpressionList::DWARFExpressionEntry>
DWARFExpressionList::GetExpressionEntryAtAddress(lldb::addr_t func_load_addr,
lldb::addr_t load_addr) const {
- if (const DWARFExpression *expr = GetAlwaysValidExpr()) {
+ if (const DWARFExpression *expr = GetAlwaysValidExpr())
return DWARFExpressionEntry{0, LLDB_INVALID_ADDRESS, expr};
- }
if (func_load_addr == LLDB_INVALID_ADDRESS)
func_load_addr = m_func_file_addr;
>From 94e4951ac8eb39f078b783c2d3a7006c395ae4b2 Mon Sep 17 00:00:00 2001
From: Abdullah Mohammad Amin
<67847674+UltimateForce21 at users.noreply.github.com>
Date: Tue, 24 Jun 2025 16:28:14 -0400
Subject: [PATCH 05/48] Update DWARFExpressionList.h
Replace raw base/end with `AddressRange` in `DWARFExpressionEntry` and cleans up helper comments to follow Doxygen convention.
Using `AddressRange` makes the intent clearer, avoids duplication of basic `AddressRange` logic usage
---
lldb/include/lldb/Expression/DWARFExpressionList.h | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/lldb/include/lldb/Expression/DWARFExpressionList.h b/lldb/include/lldb/Expression/DWARFExpressionList.h
index f6a269809decc..4af6f99b9c23a 100644
--- a/lldb/include/lldb/Expression/DWARFExpressionList.h
+++ b/lldb/include/lldb/Expression/DWARFExpressionList.h
@@ -9,6 +9,7 @@
#ifndef LLDB_EXPRESSION_DWARFEXPRESSIONLIST_H
#define LLDB_EXPRESSION_DWARFEXPRESSIONLIST_H
+#include "lldb/Core/AddressRange.h"
#include "lldb/Core/Value.h"
#include "lldb/Expression/DWARFExpression.h"
#include "lldb/Utility/RangeMap.h"
@@ -58,15 +59,16 @@ class DWARFExpressionList {
}
lldb::addr_t GetFuncFileAddress() { return m_func_file_addr; }
-
+
/// Represents an entry in the DWARFExpressionList with all needed metadata.
struct DWARFExpressionEntry {
- lldb::addr_t base;
- lldb::addr_t end;
+ AddressRange file_range; /// Represents a DWARF location range in the DWARF unit’s file‐address space
const DWARFExpression *expr;
};
- /// Returns the DWARFExpressionEntry for a given PC address.
+ /// Returns a DWARFExpressionEntry whose file_range contains the given
+ /// load‐address. `func_load_addr` is the load‐address of the function
+ /// start; `load_addr` is the full runtime PC. On success, `expr` is non-null.
llvm::Expected<DWARFExpressionEntry>
GetExpressionEntryAtAddress(lldb::addr_t func_load_addr,
lldb::addr_t load_addr) const;
>From e8142dab5a1c90f05deb659a57059313c055b99d Mon Sep 17 00:00:00 2001
From: Abdullah Mohammad Amin
<67847674+UltimateForce21 at users.noreply.github.com>
Date: Tue, 24 Jun 2025 16:36:41 -0400
Subject: [PATCH 06/48] Update DWARFExpressionList.cpp
Converts `GetExpressionEntryAtAddress` to return `llvm::Expected<DWARFExpressionEntry>` using the updated `DWARFExpressionEntry`. Updates the implementation to compute a single `AddressRange file_range` for each DWARF location interval.
---
.../source/Expression/DWARFExpressionList.cpp | 26 +++++++++++--------
1 file changed, 15 insertions(+), 11 deletions(-)
diff --git a/lldb/source/Expression/DWARFExpressionList.cpp b/lldb/source/Expression/DWARFExpressionList.cpp
index ebf57dd457769..8b8378eb895d3 100644
--- a/lldb/source/Expression/DWARFExpressionList.cpp
+++ b/lldb/source/Expression/DWARFExpressionList.cpp
@@ -6,6 +6,7 @@
//
//===----------------------------------------------------------------------===//
+#include "lldb/Core/AddressRange.h"
#include "lldb/Expression/DWARFExpressionList.h"
#include "lldb/Symbol/Function.h"
#include "lldb/Target/RegisterContext.h"
@@ -55,22 +56,25 @@ bool DWARFExpressionList::ContainsAddress(lldb::addr_t func_load_addr,
llvm::Expected<DWARFExpressionList::DWARFExpressionEntry>
DWARFExpressionList::GetExpressionEntryAtAddress(lldb::addr_t func_load_addr,
- lldb::addr_t load_addr) const {
- if (const DWARFExpression *expr = GetAlwaysValidExpr())
- return DWARFExpressionEntry{0, LLDB_INVALID_ADDRESS, expr};
+ lldb::addr_t load_addr) const {
+ if (const DWARFExpression *always = GetAlwaysValidExpr()) {
+ AddressRange full_range(m_func_file_addr, /*size=*/LLDB_INVALID_ADDRESS);
+ return DWARFExpressionEntry{full_range, always};
+ }
if (func_load_addr == LLDB_INVALID_ADDRESS)
func_load_addr = m_func_file_addr;
+ lldb::addr_t file_pc = load_addr - func_load_addr + m_func_file_addr;
- addr_t addr = load_addr - func_load_addr + m_func_file_addr;
- uint32_t index = m_exprs.FindEntryIndexThatContains(addr);
- if (index == UINT32_MAX) {
- return llvm::createStringError(llvm::inconvertibleErrorCode(),
- "No DWARF expression found for address 0x%llx", addr);
- }
+ uint32_t idx = m_exprs.FindEntryIndexThatContains(file_pc);
+ if (idx == UINT32_MAX)
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "no DWARF location list entry for PC 0x%" PRIx64, load_addr);
- const Entry &entry = *m_exprs.GetEntryAtIndex(index);
- return DWARFExpressionEntry{entry.base, entry.GetRangeEnd(), &entry.data};
+ const auto &entry = *m_exprs.GetEntryAtIndex(idx);
+ AddressRange range_in_file(entry.base, entry.GetRangeEnd() - entry.base);
+ return DWARFExpressionEntry{range_in_file, &entry.data};
}
const DWARFExpression *
>From 7e8741edfefa6989d06b4e50e11dfd4a47d57d28 Mon Sep 17 00:00:00 2001
From: Abdullah Mohammad Amin
<67847674+UltimateForce21 at users.noreply.github.com>
Date: Sat, 28 Jun 2025 12:59:08 -0400
Subject: [PATCH 07/48] Update DWARFExpressionList.h
Updated commenting style for struct DWARFExpressionEntry
---
lldb/include/lldb/Expression/DWARFExpressionList.h | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/lldb/include/lldb/Expression/DWARFExpressionList.h b/lldb/include/lldb/Expression/DWARFExpressionList.h
index 4af6f99b9c23a..31b852eb1ec80 100644
--- a/lldb/include/lldb/Expression/DWARFExpressionList.h
+++ b/lldb/include/lldb/Expression/DWARFExpressionList.h
@@ -62,7 +62,8 @@ class DWARFExpressionList {
/// Represents an entry in the DWARFExpressionList with all needed metadata.
struct DWARFExpressionEntry {
- AddressRange file_range; /// Represents a DWARF location range in the DWARF unit’s file‐address space
+ /// Represents a DWARF location range in the DWARF unit’s file‐address space
+ AddressRange file_range;
const DWARFExpression *expr;
};
>From c4cd77fa3605b3f1653b3987ed0a65b4c264f655 Mon Sep 17 00:00:00 2001
From: Abdullah Mohammad Amin
<67847674+UltimateForce21 at users.noreply.github.com>
Date: Sat, 28 Jun 2025 13:24:57 -0400
Subject: [PATCH 08/48] Update DWARFExpressionList.cpp
Updated function `llvm::Expected<DWARFExpressionList::DWARFExpressionEntry>
DWARFExpressionList::GetExpressionEntryAtAddress` to use `FindEntryThatContains` instead of `FindEntryIndexThatContains`
---
lldb/source/Expression/DWARFExpressionList.cpp | 18 ++++++++++--------
1 file changed, 10 insertions(+), 8 deletions(-)
diff --git a/lldb/source/Expression/DWARFExpressionList.cpp b/lldb/source/Expression/DWARFExpressionList.cpp
index 8b8378eb895d3..9a6cccf445028 100644
--- a/lldb/source/Expression/DWARFExpressionList.cpp
+++ b/lldb/source/Expression/DWARFExpressionList.cpp
@@ -64,17 +64,19 @@ DWARFExpressionList::GetExpressionEntryAtAddress(lldb::addr_t func_load_addr,
if (func_load_addr == LLDB_INVALID_ADDRESS)
func_load_addr = m_func_file_addr;
+
+ // translate to file-relative PC
lldb::addr_t file_pc = load_addr - func_load_addr + m_func_file_addr;
- uint32_t idx = m_exprs.FindEntryIndexThatContains(file_pc);
- if (idx == UINT32_MAX)
- return llvm::createStringError(
- llvm::inconvertibleErrorCode(),
- "no DWARF location list entry for PC 0x%" PRIx64, load_addr);
+ if (const auto *entry = m_exprs.FindEntryThatContains(file_pc)) {
+ AddressRange range_in_file(entry->GetRangeBase(),
+ entry->GetRangeEnd() - entry->GetRangeBase());
+ return DWARFExpressionEntry{range_in_file, &entry->data};
+ }
- const auto &entry = *m_exprs.GetEntryAtIndex(idx);
- AddressRange range_in_file(entry.base, entry.GetRangeEnd() - entry.base);
- return DWARFExpressionEntry{range_in_file, &entry.data};
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "no DWARF location list entry for PC 0x%" PRIx64, load_addr);
}
const DWARFExpression *
>From 62c02a9f1afd2018e0bd11541bf001047e4f7917 Mon Sep 17 00:00:00 2001
From: ultimateforce21 <abdullahmohammad155 at gmail.com>
Date: Sat, 28 Jun 2025 21:46:46 -0400
Subject: [PATCH 09/48] Change GetExpressionEntryAtAddress to return
std::optional instead of llvm::Expected
---
lldb/include/lldb/Expression/DWARFExpressionList.h | 2 +-
lldb/source/Expression/DWARFExpressionList.cpp | 7 +++----
2 files changed, 4 insertions(+), 5 deletions(-)
diff --git a/lldb/include/lldb/Expression/DWARFExpressionList.h b/lldb/include/lldb/Expression/DWARFExpressionList.h
index 31b852eb1ec80..b21b694694ddc 100644
--- a/lldb/include/lldb/Expression/DWARFExpressionList.h
+++ b/lldb/include/lldb/Expression/DWARFExpressionList.h
@@ -70,7 +70,7 @@ class DWARFExpressionList {
/// Returns a DWARFExpressionEntry whose file_range contains the given
/// load‐address. `func_load_addr` is the load‐address of the function
/// start; `load_addr` is the full runtime PC. On success, `expr` is non-null.
- llvm::Expected<DWARFExpressionEntry>
+ std::optional<DWARFExpressionEntry>
GetExpressionEntryAtAddress(lldb::addr_t func_load_addr,
lldb::addr_t load_addr) const;
diff --git a/lldb/source/Expression/DWARFExpressionList.cpp b/lldb/source/Expression/DWARFExpressionList.cpp
index 9a6cccf445028..763dcb568d2ec 100644
--- a/lldb/source/Expression/DWARFExpressionList.cpp
+++ b/lldb/source/Expression/DWARFExpressionList.cpp
@@ -54,7 +54,7 @@ bool DWARFExpressionList::ContainsAddress(lldb::addr_t func_load_addr,
return GetExpressionAtAddress(func_load_addr, addr) != nullptr;
}
-llvm::Expected<DWARFExpressionList::DWARFExpressionEntry>
+std::optional<DWARFExpressionList::DWARFExpressionEntry>
DWARFExpressionList::GetExpressionEntryAtAddress(lldb::addr_t func_load_addr,
lldb::addr_t load_addr) const {
if (const DWARFExpression *always = GetAlwaysValidExpr()) {
@@ -74,9 +74,8 @@ DWARFExpressionList::GetExpressionEntryAtAddress(lldb::addr_t func_load_addr,
return DWARFExpressionEntry{range_in_file, &entry->data};
}
- return llvm::createStringError(
- llvm::inconvertibleErrorCode(),
- "no DWARF location list entry for PC 0x%" PRIx64, load_addr);
+ // No entry covers this PC:
+ return std::nullopt;
}
const DWARFExpression *
>From d015971a280d41a68e17530b00258c193ea46a82 Mon Sep 17 00:00:00 2001
From: Abdullah Mohammad Amin
<67847674+UltimateForce21 at users.noreply.github.com>
Date: Wed, 2 Jul 2025 11:45:48 -0400
Subject: [PATCH 10/48] Update DWARFExpressionList.cpp
Co-authored-by: Adrian Prantl <adrian.prantl at gmail.com>
---
lldb/source/Expression/DWARFExpressionList.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lldb/source/Expression/DWARFExpressionList.cpp b/lldb/source/Expression/DWARFExpressionList.cpp
index 763dcb568d2ec..2f0f034b613db 100644
--- a/lldb/source/Expression/DWARFExpressionList.cpp
+++ b/lldb/source/Expression/DWARFExpressionList.cpp
@@ -65,7 +65,7 @@ DWARFExpressionList::GetExpressionEntryAtAddress(lldb::addr_t func_load_addr,
if (func_load_addr == LLDB_INVALID_ADDRESS)
func_load_addr = m_func_file_addr;
- // translate to file-relative PC
+ // Translate to file-relative PC.
lldb::addr_t file_pc = load_addr - func_load_addr + m_func_file_addr;
if (const auto *entry = m_exprs.FindEntryThatContains(file_pc)) {
>From 60898ea58bf178a2aa87f86b44ddfe7fd8b5d6da Mon Sep 17 00:00:00 2001
From: ultimateforce21 <abdullahmohammad155 at gmail.com>
Date: Thu, 3 Jul 2025 00:05:26 -0400
Subject: [PATCH 11/48] Add underflow/overflow checks to
GetExpressionEntryAtAddressi This patch adds explicit checks: - ensure
`load_addr >= func_load_addr` to avoid underflow, - compute and verify a
temporary delta variable, then verify `delta + m_func_file_addr` does not
exceed `addr_t` max to avoid overflow.
---
lldb/source/Expression/DWARFExpressionList.cpp | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/lldb/source/Expression/DWARFExpressionList.cpp b/lldb/source/Expression/DWARFExpressionList.cpp
index 2f0f034b613db..772c23fd96d0f 100644
--- a/lldb/source/Expression/DWARFExpressionList.cpp
+++ b/lldb/source/Expression/DWARFExpressionList.cpp
@@ -64,10 +64,18 @@ DWARFExpressionList::GetExpressionEntryAtAddress(lldb::addr_t func_load_addr,
if (func_load_addr == LLDB_INVALID_ADDRESS)
func_load_addr = m_func_file_addr;
-
- // Translate to file-relative PC.
- lldb::addr_t file_pc = load_addr - func_load_addr + m_func_file_addr;
+ // Guard against underflow when translating a load address back into file space.
+ if (load_addr < func_load_addr)
+ return std::nullopt;
+
+ // Guard against overflow.
+ lldb::addr_t delta = load_addr - func_load_addr;
+ if (delta > std::numeric_limits<lldb::addr_t>::max() - m_func_file_addr)
+ return std::nullopt;
+
+ lldb::addr_t file_pc = (load_addr - func_load_addr) + m_func_file_addr;
+
if (const auto *entry = m_exprs.FindEntryThatContains(file_pc)) {
AddressRange range_in_file(entry->GetRangeBase(),
entry->GetRangeEnd() - entry->GetRangeBase());
>From 3462165da7811d190d1896ed41372af25548abd5 Mon Sep 17 00:00:00 2001
From: ultimateforce21 <abdullahmohammad155 at gmail.com>
Date: Mon, 7 Jul 2025 22:39:45 -0400
Subject: [PATCH 12/48] Make file_range optional in DWARFExpressionEntry for
always-valid expr
---
lldb/include/lldb/Expression/DWARFExpressionList.h | 2 +-
lldb/source/Expression/DWARFExpressionList.cpp | 3 +--
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/lldb/include/lldb/Expression/DWARFExpressionList.h b/lldb/include/lldb/Expression/DWARFExpressionList.h
index b21b694694ddc..1bd762a9836e8 100644
--- a/lldb/include/lldb/Expression/DWARFExpressionList.h
+++ b/lldb/include/lldb/Expression/DWARFExpressionList.h
@@ -63,7 +63,7 @@ class DWARFExpressionList {
/// Represents an entry in the DWARFExpressionList with all needed metadata.
struct DWARFExpressionEntry {
/// Represents a DWARF location range in the DWARF unit’s file‐address space
- AddressRange file_range;
+ std::optional<AddressRange> file_range; ///< None = always-valid single expr
const DWARFExpression *expr;
};
diff --git a/lldb/source/Expression/DWARFExpressionList.cpp b/lldb/source/Expression/DWARFExpressionList.cpp
index 772c23fd96d0f..b51317a98365c 100644
--- a/lldb/source/Expression/DWARFExpressionList.cpp
+++ b/lldb/source/Expression/DWARFExpressionList.cpp
@@ -58,8 +58,7 @@ std::optional<DWARFExpressionList::DWARFExpressionEntry>
DWARFExpressionList::GetExpressionEntryAtAddress(lldb::addr_t func_load_addr,
lldb::addr_t load_addr) const {
if (const DWARFExpression *always = GetAlwaysValidExpr()) {
- AddressRange full_range(m_func_file_addr, /*size=*/LLDB_INVALID_ADDRESS);
- return DWARFExpressionEntry{full_range, always};
+ return DWARFExpressionEntry{std::nullopt, always};
}
if (func_load_addr == LLDB_INVALID_ADDRESS)
>From 2ed84430163412eec217f8cabc653191f5be4766 Mon Sep 17 00:00:00 2001
From: ultimateforce21 <abdullahmohammad155 at gmail.com>
Date: Thu, 3 Jul 2025 19:54:27 -0400
Subject: [PATCH 13/48] Annotate Instruction::Dump() with DWARF variable
locations
---
lldb/source/Core/Disassembler.cpp | 49 +++++++++++++++++++++++++++++++
1 file changed, 49 insertions(+)
diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp
index dce3d59457c0e..f818eb20bdad5 100644
--- a/lldb/source/Core/Disassembler.cpp
+++ b/lldb/source/Core/Disassembler.cpp
@@ -26,7 +26,10 @@
#include "lldb/Symbol/Function.h"
#include "lldb/Symbol/Symbol.h"
#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/Symbol/VariableList.h"
#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
#include "lldb/Target/SectionLoadList.h"
#include "lldb/Target/StackFrame.h"
#include "lldb/Target/Target.h"
@@ -702,6 +705,52 @@ void Instruction::Dump(lldb_private::Stream *s, uint32_t max_opcode_byte_size,
ss.FillLastLineToColumn(opcode_pos + opcode_column_width, ' ');
ss.PutCString(mnemonics);
+ if (exe_ctx && exe_ctx->GetFramePtr()) {
+ StackFrame *frame = exe_ctx->GetFramePtr();
+ TargetSP target_sp = exe_ctx->GetTargetSP();
+ if (frame && target_sp) {
+ addr_t current_pc = m_address.GetLoadAddress(target_sp.get());
+ addr_t original_pc = frame->GetFrameCodeAddress().GetLoadAddress(target_sp.get());
+ if (frame->ChangePC(current_pc)) {
+ VariableListSP var_list_sp = frame->GetInScopeVariableList(true);
+ SymbolContext sc = frame->GetSymbolContext(eSymbolContextFunction);
+ addr_t func_load_addr = LLDB_INVALID_ADDRESS;
+ if (sc.function)
+ func_load_addr = sc.function->GetAddress().GetLoadAddress(target_sp.get());
+
+ if (var_list_sp) {
+ for (size_t i = 0; i < var_list_sp->GetSize(); ++i) {
+ VariableSP var_sp = var_list_sp->GetVariableAtIndex(i);
+ if (!var_sp)
+ continue;
+
+ const char *name = var_sp->GetName().AsCString();
+ auto &expr_list = var_sp->LocationExpressionList();
+
+ // Handle std::optional<DWARFExpressionEntry>.
+ if (auto entryOrErr = expr_list.GetExpressionEntryAtAddress(func_load_addr, current_pc)) {
+ auto entry = *entryOrErr;
+
+ // Translate file-range to load-space start.
+ addr_t file_base = entry.file_range.GetBaseAddress().GetFileAddress();
+ addr_t start_load_addr = file_base + (func_load_addr - expr_list.GetFuncFileAddress());
+
+ if (current_pc == start_load_addr) {
+ StreamString loc_str;
+ ABI *abi = exe_ctx->GetProcessPtr()->GetABI().get();
+ entry.expr->DumpLocation(&loc_str, eDescriptionLevelBrief, abi);
+ ss.FillLastLineToColumn(opcode_pos + opcode_column_width + operand_column_width, ' ');
+ ss.Printf(" ; %s = %s", name, loc_str.GetString().str().c_str());
+ }
+ }
+ }
+ }
+
+ frame->ChangePC(original_pc);
+ }
+ }
+ }
+
if (!m_comment.empty()) {
ss.FillLastLineToColumn(
opcode_pos + opcode_column_width + operand_column_width, ' ');
>From 8c6b22dab6ef744742ae46e948f204d6b27f8145 Mon Sep 17 00:00:00 2001
From: ultimateforce21 <abdullahmohammad155 at gmail.com>
Date: Sat, 5 Jul 2025 01:22:33 -0400
Subject: [PATCH 14/48] Added Initial Basic API test for rich variable
annotation in disassembly output. Right now just checks if DW annotations
show up for a basic program and that a variable location is annotated (i.e 'a
= DW_OP_reg...').
---
.../rich-disassembler/Makefile | 4 ++
.../rich-disassembler/TestRichDisassembler.py | 38 ++++++++++++++++++
.../rich-disassembler/main.cpp | 15 +++++++
.../rich-disassembler/unoptimized_output | Bin 0 -> 17184 bytes
4 files changed, 57 insertions(+)
create mode 100644 lldb/test/API/functionalities/rich-disassembler/Makefile
create mode 100644 lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py
create mode 100644 lldb/test/API/functionalities/rich-disassembler/main.cpp
create mode 100755 lldb/test/API/functionalities/rich-disassembler/unoptimized_output
diff --git a/lldb/test/API/functionalities/rich-disassembler/Makefile b/lldb/test/API/functionalities/rich-disassembler/Makefile
new file mode 100644
index 0000000000000..5fcbc722e7559
--- /dev/null
+++ b/lldb/test/API/functionalities/rich-disassembler/Makefile
@@ -0,0 +1,4 @@
+CXX := clang++
+CXX_SOURCES := main.cpp
+CXXFLAGS_EXTRAS := -g -O0 -fno-inline
+include Makefile.rules
diff --git a/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py b/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py
new file mode 100644
index 0000000000000..2a8b7df1947c1
--- /dev/null
+++ b/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py
@@ -0,0 +1,38 @@
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+
+class TestRichDisassembler(TestBase):
+ """
+ Tests that the disassembler includes DWARF variable annotations in output.
+ Specifically checks that variables like 'a' and 'temp' are shown with DW_OP locations.
+ """
+ def test_variable_annotation(self):
+ print("Building with:", self.getCompiler())
+ self.build()
+ exe = self.getBuildArtifact("a.out")
+ target = self.dbg.CreateTarget(exe)
+ self.assertTrue(target)
+
+ src_file = lldb.SBFileSpec("main.cpp")
+ breakpoint = target.BreakpointCreateByName("test")
+ print("Breakpoint locations:", breakpoint.GetNumLocations())
+ self.assertGreater(breakpoint.GetNumLocations(), 0)
+
+ process = target.LaunchSimple(None, None, self.get_process_working_directory())
+ print("Process state:", process.GetState())
+ print("Exit status:", process.GetExitStatus())
+ print("Exit description:", process.GetExitDescription())
+
+ self.assertTrue(process, "Failed to launch process")
+ self.assertEqual(process.GetState(), lldb.eStateStopped, "Process did not stop")
+
+ frame = process.GetSelectedThread().GetSelectedFrame()
+ disasm = frame.Disassemble()
+ print(disasm)
+
+ # Check that at least one DWARF annotation is shown.
+ self.assertIn("DW_OP_", disasm)
+
+ # Check that at least one variable name is annotated.
+ self.assertRegex(disasm, r'[a-zA-Z_]\w*\s*=\s*DW_OP_')
+
diff --git a/lldb/test/API/functionalities/rich-disassembler/main.cpp b/lldb/test/API/functionalities/rich-disassembler/main.cpp
new file mode 100644
index 0000000000000..4fa182b9fa0de
--- /dev/null
+++ b/lldb/test/API/functionalities/rich-disassembler/main.cpp
@@ -0,0 +1,15 @@
+// main.cpp
+
+
+__attribute__((noinline))
+void test(int a) {
+ for (int i = 0; i < 3; ++i) { // i should be in a register
+ int temp = i + 1; // temp also likely register
+ }
+}
+
+int main() {
+ test(5);
+ return 0;
+}
+
diff --git a/lldb/test/API/functionalities/rich-disassembler/unoptimized_output b/lldb/test/API/functionalities/rich-disassembler/unoptimized_output
new file mode 100755
index 0000000000000000000000000000000000000000..3ead1df962fa45b59eb04f5957d72195cfbe1332
GIT binary patch
literal 17184
zcmeHOYiu0V6~43UUE4VEBamVe6mKA=;o-5 at aY7Ⓢuj*MhTC^fwUzI<K3~nWgljD
zHaLOMAVbv@(k2z6sHJMsRz)h6pcTK6+8PA`{Zlm+KTv<PZUmA+&>BHoQ&6_wnK@^?
znXXY=sr|!TYv#M>yXU+2&bfD9-nm~H+_fX5X-ZO9-K5A$w7N`!3ubQM3?QL4s- at U>
zsx>MKyUyX<@`MX0b<-Q63$cOuFpzZZl&OK2yR;IN9uksnvD82vLK0Mb9Hfg-0o#5&
z%?$`jxz}Ed#7Hq`@8P>_3Rdf=E3;j$!|av|F~M<3GK!8&(#^AOo^^sV+)qJ?C&h%e
zjT}!u#!yBn=Ijw4M$zqOogcTepCI{D+WVHBro6u$v18<f6m#|q?cK(@Ixg^IANvbR
zd$&W!L(3kY4l>MpxV^>3p`GI|$l+Dp+04j>zV2+gE1SueC%Yy$Zs^+3r<V$PuhTH~
zi`t+$weyyJDs)s$6DNAgUxJOoO6Au#-nsJLGcUIN;*KNvdzuIKR~~=;5vhYT<U@)4
zWlE4ud1`}v+~a>3IiAbFv(|5N4zb9NYF$>NjQSp{!Bac6`0EzocYwbF+rK2k7|Rv%
zM#(mdwqYn^Xn41gwu;tRres^i;oSq-Lf#rSN3xb%R=dPVO`67NCU0glhv8^oR8m}L
z&dlT$?2?^MU3)E!%my-ZUT!P}SF+;7r(AyLuAyxMMz6lEPf4~{Ur)UcBgXXSMxXE-
zU}YY?aPmonKdhc&uDl}CNTc&Zk|N3ZE$3CN&E at C#{1twND~u)Fa^b~(W2?*4Skt&m
znyKPxj3mug at ovxElDR6LPi?1QzKW;$7o;EpK?H&b1Q7@#5JVt|KoEh?Rs=q1S at n<P
z)VmGI$0C2~Rx0`Ev>mGaHaYcj!x<+KmG$R9S32LszNI69+z*f&Jt0vdef+(lV`uM9
z9y^zudUNi!;lcQH{MX6jTiWoPT3P-8+&>)ETRI+bB;bDrCV4#a6M`G&?d53tZpx$S
z#>z}f$5C=T!wCO}9sl)*39de$JT{kn>Can}FU^OO+AGPk=j}^SK^IrhP?;HR>Da!Y
zr~Vz?vIWJeeC at vE)RuEPvdLp_*-Mg-ZCM3=_UcNdGMmQkmB at 0~+5xofwNLuD??)l`
zXCI`;j(qa!;5_wm>d1V!<@lw at JvKN$HLV>R{7^FUZ~qah17{Wv-RT$U5%1Pl2S1?-
zenrptv(LaYK8>Ka9-yduW8#ehxdthSKoEf-0zm|V2m}!bA`nC%h(Hj5AOb-If(ZQg
z5uo3>w3XpaJ*a}-MK7PLRMNoffR6(=19y at Aex>pwU>5i?kbc{wq#fR)w8>U&<&ye_
z6L{e#o_ at 2R{Y#~C6*+9LcYg6&gDm}~`#gAuXl~un+<tRQ)BO!c)z%f8u35L{3UVj?
zUD%$2pZqpPJk at DqJBt3qiRf=`{c31nX*7$5VN&@M*iNB*HP9=+%Pl7=NI?XG2m}!b
zA`nC%h(Hj5AOb-If(Qf=_$)*~zDJbr59J%eF+O;)5Blt*KAXOYq_o5Z?*qxDY(K%@
zJzmDPd>6TrZF#3#!M1!KN$+ozr2MbvD+RJo at yCnuE#&?>V&z*{rC#Imi(Fbg$JoT!
z&bXXWfMrT0T&A82lH8E=W14w<?CB(_ at 8gQwKah^=dA8eGk6(11{!3i(TI$#hT>cT;
zUO%}0|2N2Y<%_i|-=*#x7`QREcHc-jZ<k~7xZbDtbj8b#8Gop^SMTY&nrX#OsjQhF
ziygFzrA#3oi$hQ)!8f at Sj)>Bn=xJ&-=Rk&(i9PWlMX&nW9pO3^35QNTSg#^^5OvyG
z>?5I*lMO2B5Fr{K5O$-gCju!NUX8tWM?^IdQy-1gDXmTG?7TP<?krx~dg;o}ww24G
zCH#0F(lyE^+Vh>^&KsLHHEqT04M$X{-8F|UMw?BJ4_fKFfRF0R)ru==>({7o=S at vb
zGzpJji{NOkr`~AV$IVL#Xj))Hrh~F7taXH1BCE8$WZZk_I}f})_TAxozV+Ptr_Xm>
zg(=?{>fsaXbCD$%l_Sfeb|idrq%}GgSr*+KY1|xH5#1CCM^8kw=$FvkC6Vw&Z+$=1
zfENDE;h at i{_0+_K>K-rTtnPBw&g4wn8Z8u4R&TsJn?0E8nkW{&Y^7`%>5*>RD%sur
zw+(fVmh&kaV~lUK?Tl6GE at o2WUFl58ES0PrzV9w7<BNUdol&$`&WGEXyp3VDt=t3_
zk+>0(BfOrEG|4!@_&?~FchJ{4eW2hg^?7Xc at 31Lsk7GND?MZCk!S;1*Ph(p&(P-AG
z_bYw}nxGm=9^?}~#txWTHT_qC52<D~;rz3VodGfCPx<(D%**v1rjv%S*VJWdZNjyk
z*@i5w3()Z2Ho5+~rAj at B47Drw3*o=!_+RXO=Pvv+$kaOC#g6NFr=DeMHYPPJV6A$7
z=@hrAI-mbp at S0k#{MWDFJ3LP61Ts}=4*6v`E}3L;F8c`Bs at lEyh@^x-#lW{Ggkc2V
z1_cV8#J3~W0^a!tCc6n-1D at KINxYg1y1}=F+Lc^i#eXyThgy`}M}^<!)Fajc-tX|u
z-g+(Pf37GtoO*Bp^34+qJhdzHL29}WJjKC(ybn42WvVA3o-R%?@2}^Z;A0oMI at Ys`
z at GmaHtG%hB9oGw3^I?u;4BH%2AWC+5bW~5Ng at q)BoikFjl%xdDbio+Q7Dmjhk+uuP
zl3|u7RjQDi$Xd3Q)_ZDu(DIUuVHS(#A;Zes#Y1YeXyz;<UC!kWK~iNI at U$;*^)4WW
zv13pF?m=VlmhH6K#lU(VV=&2Na{C at -Z2!V7{kw+-l(BQyt=sx{8Mp4(v3GFT81CP;
zYY+;#L>gAwv`wsuxfSbN(wV$bE?H?*SiKM?QN47<xt!wIO9f-x%%^ER4IL3AzQr=0
z3Tt1SBY_Ln;dou9cHOl#3W1<DM at l6jK5h9<jg>w%SMw;nbSP(=BS5?8;<)5+Kd_1u
zO3xQ;OCQUZb=+$vtfGCWiW(_rupTRuW~zVNP#4V$$9>!^jVnEUD32yww2N*DKY?6e
z7$_ at RS(5}xr%_e<MAp{F3dq>jB=$}W^<u#p4c!{&c`%-aR4iBOP6$^adDLs>GAT4#
zu&HKriAGgNM3s&ynZry2XN{D{FchN&$(ti1MeCqgSbAnj9)XgqnWj#-VsgP4c{Z{}
zVRW=)*`*q1;eSrib30B^+!Nf?&i5}td2Z)o$5--%S7M`QYthU4K*1Qws+03LSVe3=
zzFNPZ+ZUvVhw3EPW8vujSFM-(uV73Z8U6Y@!T$}<>e7FC9}$%HX}uODfB#Kz^cX05
zd0!DcK_VpTBgI+t@}7|aM~5tWdH)fV_Z_%6iR!3M_raid5z)*0hoHR2N&m%9 at FD2w
z-9~JAzY*-=#{B)4J{(7eVk&xhzY?5gM`_=G{NF}79g~!M_cOk)$$KSN<FqAt!oOpy
z*2h>U$fhR|Tkv~6eGeB1N`IvSu?2tZ(<fLjc)M at Fq+O}+r#`(r7YNFGxA^<#@6TE9
ztpSa(C!^nAY{3^<FXJcAVS at CVZgmo!aIYalhba1uF}FnU1=sZU5;q}!hfFgzKJC5d
zPg&3F_ZM66574$$>E(HMt_viR__@}CE&Mwupt&dQ%k!qJ&z|A&qxnxsbTW>o!I6vT
zWqs!x-Q=y*S{J;%=mkGONwr=j*tN$+xlS<awJ-WlP*AOx=eu^+OF#X3u|p73UDAJf
z-doFhX-E92t?E<{f;e{fibTK1W894B1zTC~b5n=8LjPS(bmD*cBKr4OPu~<&C%>Ma
z-~Al<oBRUtaJ3kDzwZXt`;Y$|>rXGDmwNhH?{iaAy)LuXS0Fl}ceCE#{<EyVOB at +R
zFL;3UJ~vgr&Sm`dicb8=mUCY?!i}uIhbPvR;Fe*N`6TnY5uD6DWWDqhJs2Rp?rYFz
T;$5w_KD*J8s|laLXR3b!>b;%1
literal 0
HcmV?d00001
>From 842a9e51bd89282277e067a66cd6f1651fa61a74 Mon Sep 17 00:00:00 2001
From: ultimateforce21 <abdullahmohammad155 at gmail.com>
Date: Sun, 6 Jul 2025 01:09:36 -0400
Subject: [PATCH 15/48] Improved DWARF variable annotation printing and
alignment
- Fixed an issue where variable location annotations were not shown if the current instruction address did not exactly match the DWARF base address. Now, annotations are shown as long as the PC is within the valid range.
- Improved alignment of annotation comments in Instruction::Dump(). While `FillLastLineToColumn` can sometimes overcompensate due to internal formatting or byte-width mismatches, the overall alignment is now significantly more consistent and readable.
---
lldb/source/Core/Disassembler.cpp | 64 ++++++++++++++++++++-----------
1 file changed, 41 insertions(+), 23 deletions(-)
diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp
index f818eb20bdad5..a3f8efe3487ef 100644
--- a/lldb/source/Core/Disassembler.cpp
+++ b/lldb/source/Core/Disassembler.cpp
@@ -705,6 +705,8 @@ void Instruction::Dump(lldb_private::Stream *s, uint32_t max_opcode_byte_size,
ss.FillLastLineToColumn(opcode_pos + opcode_column_width, ' ');
ss.PutCString(mnemonics);
+ const size_t annotation_column = 150;
+
if (exe_ctx && exe_ctx->GetFramePtr()) {
StackFrame *frame = exe_ctx->GetFramePtr();
TargetSP target_sp = exe_ctx->GetTargetSP();
@@ -718,29 +720,45 @@ void Instruction::Dump(lldb_private::Stream *s, uint32_t max_opcode_byte_size,
if (sc.function)
func_load_addr = sc.function->GetAddress().GetLoadAddress(target_sp.get());
- if (var_list_sp) {
- for (size_t i = 0; i < var_list_sp->GetSize(); ++i) {
- VariableSP var_sp = var_list_sp->GetVariableAtIndex(i);
- if (!var_sp)
- continue;
-
- const char *name = var_sp->GetName().AsCString();
- auto &expr_list = var_sp->LocationExpressionList();
-
- // Handle std::optional<DWARFExpressionEntry>.
- if (auto entryOrErr = expr_list.GetExpressionEntryAtAddress(func_load_addr, current_pc)) {
- auto entry = *entryOrErr;
-
- // Translate file-range to load-space start.
- addr_t file_base = entry.file_range.GetBaseAddress().GetFileAddress();
- addr_t start_load_addr = file_base + (func_load_addr - expr_list.GetFuncFileAddress());
-
- if (current_pc == start_load_addr) {
- StreamString loc_str;
- ABI *abi = exe_ctx->GetProcessPtr()->GetABI().get();
- entry.expr->DumpLocation(&loc_str, eDescriptionLevelBrief, abi);
- ss.FillLastLineToColumn(opcode_pos + opcode_column_width + operand_column_width, ' ');
- ss.Printf(" ; %s = %s", name, loc_str.GetString().str().c_str());
+
+ if(ss.GetSizeOfLastLine() < annotation_column) {
+
+ std::vector<std::string> annotations;
+
+ if (var_list_sp) {
+ for (size_t i = 0; i < var_list_sp->GetSize(); ++i) {
+ VariableSP var_sp = var_list_sp->GetVariableAtIndex(i);
+ if (!var_sp)
+ continue;
+
+ const char *name = var_sp->GetName().AsCString();
+ auto &expr_list = var_sp->LocationExpressionList();
+ if (!expr_list.IsValid())
+ continue;
+ // Handle std::optional<DWARFExpressionEntry>.
+ if (auto entryOrErr = expr_list.GetExpressionEntryAtAddress(func_load_addr, current_pc)) {
+ auto entry = *entryOrErr;
+
+ // Translate file-range to load-space start.
+ addr_t file_base = entry.file_range.GetBaseAddress().GetFileAddress();
+ addr_t start_load_addr = file_base + (func_load_addr - expr_list.GetFuncFileAddress());
+
+ if (current_pc >= start_load_addr) {
+ StreamString loc_str;
+ ABI *abi = exe_ctx->GetProcessPtr()->GetABI().get();
+ entry.expr->DumpLocation(&loc_str, eDescriptionLevelBrief, abi);
+ annotations.push_back(llvm::formatv("{0} = {1}", name, loc_str.GetString()));
+ }
+ }
+ }
+
+ if (!annotations.empty()) {
+ ss.FillLastLineToColumn(annotation_column, ' ');
+ ss.PutCString(" ; ");
+ for (size_t i = 0; i < annotations.size(); ++i) {
+ if (i > 0)
+ ss.PutCString(", ");
+ ss.PutCString(annotations[i]);
}
}
}
>From 2fa6d243b24927510af426cfbd57c112c7289b0e Mon Sep 17 00:00:00 2001
From: ultimateforce21 <abdullahmohammad155 at gmail.com>
Date: Sun, 6 Jul 2025 11:10:46 -0400
Subject: [PATCH 16/48] Filter out partial DWARF decoding errors from
disassembly annotations
Previously, when a DWARF expression contained any decoding error,
the entire variable location annotation was printed with the error,
e.g. `c = DW_OP_addr 0x0, <decoding error> 00 00 00`. This was
misleading and cluttered the disassembly view.
This patch improves the formatting by stripping out the
`<decoding error ...>` fragments while preserving the valid portions
of the expression, so that partial information like
`c = DW_OP_addr 0x0` can still be shown.
This allows the rich disassembler to give more meaningful variable
annotations, especially in optimized (-O1/-O2) builds where partial
DWARF corruption or unsupported expressions may occur.
---
lldb/source/Core/Disassembler.cpp | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp
index a3f8efe3487ef..0982c98fd80c0 100644
--- a/lldb/source/Core/Disassembler.cpp
+++ b/lldb/source/Core/Disassembler.cpp
@@ -49,6 +49,7 @@
#include <cstdint>
#include <cstring>
+#include <regex>
#include <utility>
#include <cassert>
@@ -747,7 +748,19 @@ void Instruction::Dump(lldb_private::Stream *s, uint32_t max_opcode_byte_size,
StreamString loc_str;
ABI *abi = exe_ctx->GetProcessPtr()->GetABI().get();
entry.expr->DumpLocation(&loc_str, eDescriptionLevelBrief, abi);
- annotations.push_back(llvm::formatv("{0} = {1}", name, loc_str.GetString()));
+
+ std::string loc_output = loc_str.GetString().str();
+
+ // Remove ", <decoding error> ..." segments.
+ std::regex decoding_err_re(", <decoding error>[^,]*");
+ loc_output = std::regex_replace(loc_output, decoding_err_re, "");
+
+ llvm::StringRef cleaned_output = llvm::StringRef(loc_output).trim();
+
+ // Only keep this annotation if there is still something useful left.
+ if (!cleaned_output.empty()) {
+ annotations.push_back(llvm::formatv("{0} = {1}", name, cleaned_output));
+ }
}
}
}
>From 6bbc8aaa45e8df045ccb5be4db52ab23fa169aa2 Mon Sep 17 00:00:00 2001
From: ultimateforce21 <abdullahmohammad155 at gmail.com>
Date: Sun, 6 Jul 2025 11:45:19 -0400
Subject: [PATCH 17/48] Ignore annotations with only decoding errors
Handled edge case where the entire DWARF expression is a `<decoding error>`, ensuring no misleading or empty annotations are printed for such variables.
---
lldb/source/Core/Disassembler.cpp | 35 +++++++++++++++++++++++++------
1 file changed, 29 insertions(+), 6 deletions(-)
diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp
index 0982c98fd80c0..710856404b67a 100644
--- a/lldb/source/Core/Disassembler.cpp
+++ b/lldb/source/Core/Disassembler.cpp
@@ -751,15 +751,38 @@ void Instruction::Dump(lldb_private::Stream *s, uint32_t max_opcode_byte_size,
std::string loc_output = loc_str.GetString().str();
- // Remove ", <decoding error> ..." segments.
- std::regex decoding_err_re(", <decoding error>[^,]*");
- loc_output = std::regex_replace(loc_output, decoding_err_re, "");
+ // // Remove ", <decoding error> ..." segments.
+ // std::regex decoding_err_re(", <decoding error>[^,]*");
+ // loc_output = std::regex_replace(loc_output, decoding_err_re, "");
- llvm::StringRef cleaned_output = llvm::StringRef(loc_output).trim();
+ // llvm::StringRef cleaned_output = llvm::StringRef(loc_output).trim();
+
+ // // Only keep this annotation if there is still something useful left.
+ // if (!cleaned_output.empty()) {
+ // annotations.push_back(llvm::formatv("{0} = {1}", name, cleaned_output));
+ // }
+
+ llvm::SmallVector<llvm::StringRef, 4> parts;
+ llvm::StringRef(loc_str.GetString()).split(parts, ", ");
+
+ // Reconstruct the string without the decoding error chunks
+ std::string cleaned_output;
+ bool first = true;
+
+ for (const auto &part : parts) {
+ if (part.contains("<decoding error>"))
+ continue;
+
+ if (!first)
+ cleaned_output += ", ";
+ cleaned_output += part.str();
+ first = false;
+ }
// Only keep this annotation if there is still something useful left.
- if (!cleaned_output.empty()) {
- annotations.push_back(llvm::formatv("{0} = {1}", name, cleaned_output));
+ llvm::StringRef cleaned_ref = llvm::StringRef(cleaned_output).trim();
+ if (!cleaned_ref.empty()) {
+ annotations.push_back(llvm::formatv("{0} = {1}", name, cleaned_ref));
}
}
}
>From cbbc9241e0b620f7cc8e925fd83fe36dd94536a4 Mon Sep 17 00:00:00 2001
From: ultimateforce21 <abdullahmohammad155 at gmail.com>
Date: Sun, 6 Jul 2025 14:19:12 -0400
Subject: [PATCH 18/48] Add tests for disassembly variable annotations and
decoding edge cases
This patch adds API tests to verify that DWARF variable location
annotations are correctly displayed in the disassembly output.
The tests cover:
- Local variables in loops and functions
- Multiple stack variables
- Control flow edge cases
- Different optimization levels (-O0, -O1, -O2)
- Ensuring decoding errors are excluded from output
---
.../rich-disassembler/Makefile | 8 +-
.../rich-disassembler/TestRichDisassembler.py | 295 +++++++++++++++++-
.../a_loop_with_local_variable.c | 8 +
.../b_multiple_stack_variables.c | 9 +
.../c_variable_passed_to_another_function.c | 11 +
.../rich-disassembler/d_original_example.c | 7 +
.../rich-disassembler/e_control_flow_edge.c | 10 +
.../rich-disassembler/main.cpp | 15 -
.../rich-disassembler/unoptimized_output | Bin 17184 -> 0 bytes
9 files changed, 328 insertions(+), 35 deletions(-)
create mode 100644 lldb/test/API/functionalities/rich-disassembler/a_loop_with_local_variable.c
create mode 100644 lldb/test/API/functionalities/rich-disassembler/b_multiple_stack_variables.c
create mode 100644 lldb/test/API/functionalities/rich-disassembler/c_variable_passed_to_another_function.c
create mode 100644 lldb/test/API/functionalities/rich-disassembler/d_original_example.c
create mode 100644 lldb/test/API/functionalities/rich-disassembler/e_control_flow_edge.c
delete mode 100644 lldb/test/API/functionalities/rich-disassembler/main.cpp
delete mode 100755 lldb/test/API/functionalities/rich-disassembler/unoptimized_output
diff --git a/lldb/test/API/functionalities/rich-disassembler/Makefile b/lldb/test/API/functionalities/rich-disassembler/Makefile
index 5fcbc722e7559..ae3330e632a0e 100644
--- a/lldb/test/API/functionalities/rich-disassembler/Makefile
+++ b/lldb/test/API/functionalities/rich-disassembler/Makefile
@@ -1,4 +1,6 @@
-CXX := clang++
-CXX_SOURCES := main.cpp
-CXXFLAGS_EXTRAS := -g -O0 -fno-inline
+
+# CXX_SOURCES := a_loop_with_local_variable.c b_multiple_stack_variables.c c_variable_passed_to_another_function.c d_original_example.c e_control_flow_edge.c
+C_SOURCES := a_loop_with_local_variable.c b_multiple_stack_variables.c c_variable_passed_to_another_function.c d_original_example.c e_control_flow_edge.c
+
+
include Makefile.rules
diff --git a/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py b/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py
index 2a8b7df1947c1..d85e6488575b7 100644
--- a/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py
+++ b/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py
@@ -2,37 +2,298 @@
from lldbsuite.test.decorators import *
class TestRichDisassembler(TestBase):
- """
- Tests that the disassembler includes DWARF variable annotations in output.
- Specifically checks that variables like 'a' and 'temp' are shown with DW_OP locations.
- """
- def test_variable_annotation(self):
- print("Building with:", self.getCompiler())
- self.build()
+
+ @no_debug_info_test
+ def test_a_loop_with_local_variable(self):
+ """
+ Tests that the disassembler includes basic DWARF variable annotation in output.
+ Specifically checks that local variables in a loop are shown with DW_OP locations.
+ Additionally, it verifies that the disassembly does not contain decoding errors.
+ """
+ self.build(dictionary={
+ 'C_SOURCES': 'a_loop_with_local_variable.c',
+ 'CFLAGS_EXTRAS': '-g -O0'
+ })
+ exe = self.getBuildArtifact("a.out")
+ target = self.dbg.CreateTarget(exe)
+ self.assertTrue(target)
+
+ # Set a breakpoint inside main's loop
+ src_file = lldb.SBFileSpec("test_loop_function_call.c")
+ breakpoint = target.BreakpointCreateByName("main")
+ self.assertGreater(breakpoint.GetNumLocations(), 0)
+
+ process = target.LaunchSimple(None, None, self.get_process_working_directory())
+ self.assertTrue(process, "Failed to launch process")
+ self.assertEqual(process.GetState(), lldb.eStateStopped)
+
+ frame = process.GetSelectedThread().GetSelectedFrame()
+ disasm = frame.Disassemble()
+ print(disasm)
+
+ # Check that we have DWARF annotations for variables
+ self.assertIn("i = ", disasm)
+ self.assertIn("DW_OP", disasm)
+ self.assertNotIn("<decoding error>", disasm)
+
+
+ @no_debug_info_test
+ def test_b_multiple_stack_variables_O0(self):
+ """
+ Tests disassembler output for b_multiple_stack_variables.c built with -O0.
+ This test checks that multiple local variables are annotated with DWARF
+ and that their locations are distinct. It also ensures that no decoding errors appear.
+ """
+ self.build(dictionary={
+ 'C_SOURCES': 'b_multiple_stack_variables.c',
+ 'CFLAGS_EXTRAS': '-g -O0'
+ })
+ exe = self.getBuildArtifact("a.out")
+ target = self.dbg.CreateTarget(exe)
+ self.assertTrue(target)
+
+ # Set a breakpoint inside main's loop
+ src_file = lldb.SBFileSpec("test_loop_function_call.c")
+ breakpoint = target.BreakpointCreateByName("main")
+ self.assertGreater(breakpoint.GetNumLocations(), 0)
+
+ process = target.LaunchSimple(None, None, self.get_process_working_directory())
+ self.assertTrue(process, "Failed to launch process")
+ self.assertEqual(process.GetState(), lldb.eStateStopped)
+
+ frame = process.GetSelectedThread().GetSelectedFrame()
+ disasm = frame.Disassemble()
+ print(disasm)
+
+ # Check that we have DWARF annotations for variables
+ self.assertIn("a = ", disasm)
+ self.assertIn("b = ", disasm)
+ self.assertIn("c = ", disasm)
+ self.assertIn("DW_OP", disasm)
+ self.assertNotIn("<decoding error>", disasm)
+
+
+ @no_debug_info_test
+ def test_b_multiple_stack_variables_O1(self):
+ """
+ Tests disassembler output for b_multiple_stack_variables.c built with -O1.
+ Due to optimizations, some variables may be optimized out.
+ We only check for 'c' and ensure no decoding errors appear.
+ """
+ self.build(dictionary={
+ 'C_SOURCES': 'b_multiple_stack_variables.c',
+ 'CFLAGS_EXTRAS': '-g -O1'
+ })
+ exe = self.getBuildArtifact("a.out")
+ target = self.dbg.CreateTarget(exe)
+ self.assertTrue(target)
+
+ breakpoint = target.BreakpointCreateByName("main")
+ self.assertGreater(breakpoint.GetNumLocations(), 0)
+
+ process = target.LaunchSimple(None, None, self.get_process_working_directory())
+ self.assertTrue(process, "Failed to launch process")
+ self.assertEqual(process.GetState(), lldb.eStateStopped)
+
+ frame = process.GetSelectedThread().GetSelectedFrame()
+ disasm = frame.Disassemble()
+ print(disasm)
+
+ self.assertIn("c = ", disasm)
+ self.assertIn("DW_OP", disasm)
+ self.assertNotIn("<decoding error>", disasm)
+
+
+ @no_debug_info_test
+ def test_c_variable_passed_to_another_function(self):
+ """
+ Tests disassembler output for c_variable_passed_to_another_function.c.
+ This test checks that a variable passed to another function is annotated
+ with DWARF and that its location is distinct. It also ensures that no decoding errors appear.
+ """
+ self.build(dictionary={
+ 'C_SOURCES': 'c_variable_passed_to_another_function.c',
+ 'CFLAGS_EXTRAS': '-g -O0'
+ })
+ exe = self.getBuildArtifact("a.out")
+ target = self.dbg.CreateTarget(exe)
+ self.assertTrue(target)
+
+ breakpoint = target.BreakpointCreateByName("main")
+ self.assertGreater(breakpoint.GetNumLocations(), 0)
+
+ process = target.LaunchSimple(None, None, self.get_process_working_directory())
+ self.assertTrue(process, "Failed to launch process")
+ self.assertEqual(process.GetState(), lldb.eStateStopped)
+
+ frame = process.GetSelectedThread().GetSelectedFrame()
+ disasm = frame.Disassemble()
+ print(disasm)
+
+ self.assertIn("x = ", disasm)
+ self.assertIn("DW_OP", disasm)
+ self.assertNotIn("<decoding error>", disasm)
+
+
+ @no_debug_info_test
+ def test_c_variable_passed_to_another_function_O1(self):
+ """
+ Tests disassembler output for c_variable_passed_to_another_function.c built with -O1.
+ """
+ self.build(dictionary={
+ 'C_SOURCES': 'c_variable_passed_to_another_function.c',
+ 'CFLAGS_EXTRAS': '-g -O1'
+ })
+ exe = self.getBuildArtifact("a.out")
+ target = self.dbg.CreateTarget(exe)
+ self.assertTrue(target)
+
+ breakpoint = target.BreakpointCreateByName("main")
+ self.assertGreater(breakpoint.GetNumLocations(), 0)
+
+ process = target.LaunchSimple(None, None, self.get_process_working_directory())
+ self.assertTrue(process, "Failed to launch process")
+ self.assertEqual(process.GetState(), lldb.eStateStopped)
+
+ frame = process.GetSelectedThread().GetSelectedFrame()
+ disasm = frame.Disassemble()
+ print(disasm)
+
+ self.assertIn("x = ", disasm)
+ self.assertIn("arg = ", disasm)
+ self.assertIn("DW_OP", disasm)
+ self.assertNotIn("<decoding error>", disasm)
+
+ @no_debug_info_test
+ def test_d_original_example(self):
+ """
+ Tests disassembler output for d_original_example.c.
+ This test checks that the disassembly includes basic DWARF variable annotations
+ and that local variables in the main function are shown with DW_OP locations.
+ Additionally, it verifies that the disassembly does not contain decoding errors.
+ """
+ self.build(dictionary={
+ 'C_SOURCES': 'd_original_example.c',
+ 'CFLAGS_EXTRAS': '-g -O0'
+ })
exe = self.getBuildArtifact("a.out")
target = self.dbg.CreateTarget(exe)
self.assertTrue(target)
- src_file = lldb.SBFileSpec("main.cpp")
- breakpoint = target.BreakpointCreateByName("test")
- print("Breakpoint locations:", breakpoint.GetNumLocations())
+ breakpoint = target.BreakpointCreateByName("main")
self.assertGreater(breakpoint.GetNumLocations(), 0)
process = target.LaunchSimple(None, None, self.get_process_working_directory())
- print("Process state:", process.GetState())
- print("Exit status:", process.GetExitStatus())
- print("Exit description:", process.GetExitDescription())
+ self.assertTrue(process, "Failed to launch process")
+ self.assertEqual(process.GetState(), lldb.eStateStopped)
+
+ frame = process.GetSelectedThread().GetSelectedFrame()
+ disasm = frame.Disassemble()
+ print(disasm)
+
+ self.assertIn("argc = ", disasm)
+ self.assertIn("argv = ", disasm)
+ self.assertIn("i = ", disasm)
+ self.assertIn("DW_OP", disasm)
+ self.assertNotIn("<decoding error>", disasm)
+
+ @no_debug_info_test
+ def test_d_original_example_O1(self):
+ """
+ Tests disassembler output for d_original_example.c built with -O1.
+ """
+ self.build(dictionary={
+ 'C_SOURCES': 'd_original_example.c',
+ 'CFLAGS_EXTRAS': '-g -O1'
+ })
+ exe = self.getBuildArtifact("a.out")
+ target = self.dbg.CreateTarget(exe)
+ self.assertTrue(target)
+
+ breakpoint = target.BreakpointCreateByName("main")
+ self.assertGreater(breakpoint.GetNumLocations(), 0)
+
+ process = target.LaunchSimple(None, None, self.get_process_working_directory())
+ self.assertTrue(process, "Failed to launch process")
+ self.assertEqual(process.GetState(), lldb.eStateStopped)
+
+ frame = process.GetSelectedThread().GetSelectedFrame()
+ disasm = frame.Disassemble()
+ print(disasm)
+
+ self.assertIn("argc = ", disasm)
+ self.assertIn("argv = ", disasm)
+ self.assertIn("i = ", disasm)
+ self.assertIn("DW_OP_reg", disasm)
+ self.assertIn("DW_OP_stack_value", disasm)
+ self.assertNotIn("<decoding error>", disasm)
+
+
+ @no_debug_info_test
+ def test_e_control_flow_edge(self):
+ """
+ Tests disassembler output for e_control_flow_edge.c with a focus on control flow edges.
+ This test checks that the disassembly includes basic DWARF variable annotations
+ and that local variables in the main function are shown with DW_OP locations.
+ Additionally, it verifies that the disassembly does not contain decoding errors.
+ """
+ self.build(dictionary={
+ 'C_SOURCES': 'e_control_flow_edge.c',
+ 'CFLAGS_EXTRAS': '-g -O0'
+ })
+ exe = self.getBuildArtifact("a.out")
+ target = self.dbg.CreateTarget(exe)
+ self.assertTrue(target)
+
+ breakpoint = target.BreakpointCreateByName("main")
+ self.assertGreater(breakpoint.GetNumLocations(), 0)
+ process = target.LaunchSimple(None, None, self.get_process_working_directory())
self.assertTrue(process, "Failed to launch process")
- self.assertEqual(process.GetState(), lldb.eStateStopped, "Process did not stop")
+ self.assertEqual(process.GetState(), lldb.eStateStopped)
frame = process.GetSelectedThread().GetSelectedFrame()
disasm = frame.Disassemble()
print(disasm)
- # Check that at least one DWARF annotation is shown.
+ self.assertIn("a = ", disasm)
+ self.assertIn("b = ", disasm)
self.assertIn("DW_OP_", disasm)
+ self.assertNotIn("<decoding error>", disasm)
+
+ @no_debug_info_test
+ def test_e_control_flow_edge_O1(self):
+ """
+ Tests disassembler output for e_control_flow_edge.c built with -O1.
+ This test checks that the disassembly annotation does not contain decoding errors.
+ """
+ self.build(dictionary={
+ 'C_SOURCES': 'e_control_flow_edge.c',
+ 'CFLAGS_EXTRAS': '-g -O1'
+ })
+ exe = self.getBuildArtifact("a.out")
+ target = self.dbg.CreateTarget(exe)
+ self.assertTrue(target)
+
+ breakpoint = target.BreakpointCreateByName("main")
+ self.assertGreater(breakpoint.GetNumLocations(), 0)
+
+ process = target.LaunchSimple(None, None, self.get_process_working_directory())
+ self.assertTrue(process, "Failed to launch process")
+ self.assertEqual(process.GetState(), lldb.eStateStopped)
+
+ frame = process.GetSelectedThread().GetSelectedFrame()
+ disasm = frame.Disassemble()
+ print(disasm)
+
+ self.assertNotIn("<decoding error>", disasm)
+
+
+
+
+
+
+
+
- # Check that at least one variable name is annotated.
- self.assertRegex(disasm, r'[a-zA-Z_]\w*\s*=\s*DW_OP_')
diff --git a/lldb/test/API/functionalities/rich-disassembler/a_loop_with_local_variable.c b/lldb/test/API/functionalities/rich-disassembler/a_loop_with_local_variable.c
new file mode 100644
index 0000000000000..6555f3822187f
--- /dev/null
+++ b/lldb/test/API/functionalities/rich-disassembler/a_loop_with_local_variable.c
@@ -0,0 +1,8 @@
+#include <stdio.h>
+
+int main() {
+ for (int i = 0; i < 3; ++i) {
+ puts("Hi");
+ }
+ return 0;
+}
diff --git a/lldb/test/API/functionalities/rich-disassembler/b_multiple_stack_variables.c b/lldb/test/API/functionalities/rich-disassembler/b_multiple_stack_variables.c
new file mode 100644
index 0000000000000..d3cd447b43d65
--- /dev/null
+++ b/lldb/test/API/functionalities/rich-disassembler/b_multiple_stack_variables.c
@@ -0,0 +1,9 @@
+#include <stdio.h>
+
+int main() {
+ int a = 1;
+ int b = 2;
+ int c = a + b;
+ return c;
+}
+
diff --git a/lldb/test/API/functionalities/rich-disassembler/c_variable_passed_to_another_function.c b/lldb/test/API/functionalities/rich-disassembler/c_variable_passed_to_another_function.c
new file mode 100644
index 0000000000000..9603bdc636043
--- /dev/null
+++ b/lldb/test/API/functionalities/rich-disassembler/c_variable_passed_to_another_function.c
@@ -0,0 +1,11 @@
+#include <stdio.h>
+
+void foo(int arg) {
+ printf("%d\n", arg);
+}
+
+int main() {
+ int x = 10;
+ foo(x);
+ return 0;
+}
diff --git a/lldb/test/API/functionalities/rich-disassembler/d_original_example.c b/lldb/test/API/functionalities/rich-disassembler/d_original_example.c
new file mode 100644
index 0000000000000..4f245f518a182
--- /dev/null
+++ b/lldb/test/API/functionalities/rich-disassembler/d_original_example.c
@@ -0,0 +1,7 @@
+#include <stdio.h>
+
+int main(int argc, char **argv) {
+ for (int i = 1; i < argc; ++i)
+ puts(argv[i]);
+ return 0;
+}
diff --git a/lldb/test/API/functionalities/rich-disassembler/e_control_flow_edge.c b/lldb/test/API/functionalities/rich-disassembler/e_control_flow_edge.c
new file mode 100644
index 0000000000000..d8d69f501eb4f
--- /dev/null
+++ b/lldb/test/API/functionalities/rich-disassembler/e_control_flow_edge.c
@@ -0,0 +1,10 @@
+#include <stdio.h>
+
+int main() {
+ int a = 1;
+ if (a > 0) {
+ int b = 2;
+ return b;
+ }
+ return a;
+}
diff --git a/lldb/test/API/functionalities/rich-disassembler/main.cpp b/lldb/test/API/functionalities/rich-disassembler/main.cpp
deleted file mode 100644
index 4fa182b9fa0de..0000000000000
--- a/lldb/test/API/functionalities/rich-disassembler/main.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-// main.cpp
-
-
-__attribute__((noinline))
-void test(int a) {
- for (int i = 0; i < 3; ++i) { // i should be in a register
- int temp = i + 1; // temp also likely register
- }
-}
-
-int main() {
- test(5);
- return 0;
-}
-
diff --git a/lldb/test/API/functionalities/rich-disassembler/unoptimized_output b/lldb/test/API/functionalities/rich-disassembler/unoptimized_output
deleted file mode 100755
index 3ead1df962fa45b59eb04f5957d72195cfbe1332..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 17184
zcmeHOYiu0V6~43UUE4VEBamVe6mKA=;o-5 at aY7Ⓢuj*MhTC^fwUzI<K3~nWgljD
zHaLOMAVbv@(k2z6sHJMsRz)h6pcTK6+8PA`{Zlm+KTv<PZUmA+&>BHoQ&6_wnK@^?
znXXY=sr|!TYv#M>yXU+2&bfD9-nm~H+_fX5X-ZO9-K5A$w7N`!3ubQM3?QL4s- at U>
zsx>MKyUyX<@`MX0b<-Q63$cOuFpzZZl&OK2yR;IN9uksnvD82vLK0Mb9Hfg-0o#5&
z%?$`jxz}Ed#7Hq`@8P>_3Rdf=E3;j$!|av|F~M<3GK!8&(#^AOo^^sV+)qJ?C&h%e
zjT}!u#!yBn=Ijw4M$zqOogcTepCI{D+WVHBro6u$v18<f6m#|q?cK(@Ixg^IANvbR
zd$&W!L(3kY4l>MpxV^>3p`GI|$l+Dp+04j>zV2+gE1SueC%Yy$Zs^+3r<V$PuhTH~
zi`t+$weyyJDs)s$6DNAgUxJOoO6Au#-nsJLGcUIN;*KNvdzuIKR~~=;5vhYT<U@)4
zWlE4ud1`}v+~a>3IiAbFv(|5N4zb9NYF$>NjQSp{!Bac6`0EzocYwbF+rK2k7|Rv%
zM#(mdwqYn^Xn41gwu;tRres^i;oSq-Lf#rSN3xb%R=dPVO`67NCU0glhv8^oR8m}L
z&dlT$?2?^MU3)E!%my-ZUT!P}SF+;7r(AyLuAyxMMz6lEPf4~{Ur)UcBgXXSMxXE-
zU}YY?aPmonKdhc&uDl}CNTc&Zk|N3ZE$3CN&E at C#{1twND~u)Fa^b~(W2?*4Skt&m
znyKPxj3mug at ovxElDR6LPi?1QzKW;$7o;EpK?H&b1Q7@#5JVt|KoEh?Rs=q1S at n<P
z)VmGI$0C2~Rx0`Ev>mGaHaYcj!x<+KmG$R9S32LszNI69+z*f&Jt0vdef+(lV`uM9
z9y^zudUNi!;lcQH{MX6jTiWoPT3P-8+&>)ETRI+bB;bDrCV4#a6M`G&?d53tZpx$S
z#>z}f$5C=T!wCO}9sl)*39de$JT{kn>Can}FU^OO+AGPk=j}^SK^IrhP?;HR>Da!Y
zr~Vz?vIWJeeC at vE)RuEPvdLp_*-Mg-ZCM3=_UcNdGMmQkmB at 0~+5xofwNLuD??)l`
zXCI`;j(qa!;5_wm>d1V!<@lw at JvKN$HLV>R{7^FUZ~qah17{Wv-RT$U5%1Pl2S1?-
zenrptv(LaYK8>Ka9-yduW8#ehxdthSKoEf-0zm|V2m}!bA`nC%h(Hj5AOb-If(ZQg
z5uo3>w3XpaJ*a}-MK7PLRMNoffR6(=19y at Aex>pwU>5i?kbc{wq#fR)w8>U&<&ye_
z6L{e#o_ at 2R{Y#~C6*+9LcYg6&gDm}~`#gAuXl~un+<tRQ)BO!c)z%f8u35L{3UVj?
zUD%$2pZqpPJk at DqJBt3qiRf=`{c31nX*7$5VN&@M*iNB*HP9=+%Pl7=NI?XG2m}!b
zA`nC%h(Hj5AOb-If(Qf=_$)*~zDJbr59J%eF+O;)5Blt*KAXOYq_o5Z?*qxDY(K%@
zJzmDPd>6TrZF#3#!M1!KN$+ozr2MbvD+RJo at yCnuE#&?>V&z*{rC#Imi(Fbg$JoT!
z&bXXWfMrT0T&A82lH8E=W14w<?CB(_ at 8gQwKah^=dA8eGk6(11{!3i(TI$#hT>cT;
zUO%}0|2N2Y<%_i|-=*#x7`QREcHc-jZ<k~7xZbDtbj8b#8Gop^SMTY&nrX#OsjQhF
ziygFzrA#3oi$hQ)!8f at Sj)>Bn=xJ&-=Rk&(i9PWlMX&nW9pO3^35QNTSg#^^5OvyG
z>?5I*lMO2B5Fr{K5O$-gCju!NUX8tWM?^IdQy-1gDXmTG?7TP<?krx~dg;o}ww24G
zCH#0F(lyE^+Vh>^&KsLHHEqT04M$X{-8F|UMw?BJ4_fKFfRF0R)ru==>({7o=S at vb
zGzpJji{NOkr`~AV$IVL#Xj))Hrh~F7taXH1BCE8$WZZk_I}f})_TAxozV+Ptr_Xm>
zg(=?{>fsaXbCD$%l_Sfeb|idrq%}GgSr*+KY1|xH5#1CCM^8kw=$FvkC6Vw&Z+$=1
zfENDE;h at i{_0+_K>K-rTtnPBw&g4wn8Z8u4R&TsJn?0E8nkW{&Y^7`%>5*>RD%sur
zw+(fVmh&kaV~lUK?Tl6GE at o2WUFl58ES0PrzV9w7<BNUdol&$`&WGEXyp3VDt=t3_
zk+>0(BfOrEG|4!@_&?~FchJ{4eW2hg^?7Xc at 31Lsk7GND?MZCk!S;1*Ph(p&(P-AG
z_bYw}nxGm=9^?}~#txWTHT_qC52<D~;rz3VodGfCPx<(D%**v1rjv%S*VJWdZNjyk
z*@i5w3()Z2Ho5+~rAj at B47Drw3*o=!_+RXO=Pvv+$kaOC#g6NFr=DeMHYPPJV6A$7
z=@hrAI-mbp at S0k#{MWDFJ3LP61Ts}=4*6v`E}3L;F8c`Bs at lEyh@^x-#lW{Ggkc2V
z1_cV8#J3~W0^a!tCc6n-1D at KINxYg1y1}=F+Lc^i#eXyThgy`}M}^<!)Fajc-tX|u
z-g+(Pf37GtoO*Bp^34+qJhdzHL29}WJjKC(ybn42WvVA3o-R%?@2}^Z;A0oMI at Ys`
z at GmaHtG%hB9oGw3^I?u;4BH%2AWC+5bW~5Ng at q)BoikFjl%xdDbio+Q7Dmjhk+uuP
zl3|u7RjQDi$Xd3Q)_ZDu(DIUuVHS(#A;Zes#Y1YeXyz;<UC!kWK~iNI at U$;*^)4WW
zv13pF?m=VlmhH6K#lU(VV=&2Na{C at -Z2!V7{kw+-l(BQyt=sx{8Mp4(v3GFT81CP;
zYY+;#L>gAwv`wsuxfSbN(wV$bE?H?*SiKM?QN47<xt!wIO9f-x%%^ER4IL3AzQr=0
z3Tt1SBY_Ln;dou9cHOl#3W1<DM at l6jK5h9<jg>w%SMw;nbSP(=BS5?8;<)5+Kd_1u
zO3xQ;OCQUZb=+$vtfGCWiW(_rupTRuW~zVNP#4V$$9>!^jVnEUD32yww2N*DKY?6e
z7$_ at RS(5}xr%_e<MAp{F3dq>jB=$}W^<u#p4c!{&c`%-aR4iBOP6$^adDLs>GAT4#
zu&HKriAGgNM3s&ynZry2XN{D{FchN&$(ti1MeCqgSbAnj9)XgqnWj#-VsgP4c{Z{}
zVRW=)*`*q1;eSrib30B^+!Nf?&i5}td2Z)o$5--%S7M`QYthU4K*1Qws+03LSVe3=
zzFNPZ+ZUvVhw3EPW8vujSFM-(uV73Z8U6Y@!T$}<>e7FC9}$%HX}uODfB#Kz^cX05
zd0!DcK_VpTBgI+t@}7|aM~5tWdH)fV_Z_%6iR!3M_raid5z)*0hoHR2N&m%9 at FD2w
z-9~JAzY*-=#{B)4J{(7eVk&xhzY?5gM`_=G{NF}79g~!M_cOk)$$KSN<FqAt!oOpy
z*2h>U$fhR|Tkv~6eGeB1N`IvSu?2tZ(<fLjc)M at Fq+O}+r#`(r7YNFGxA^<#@6TE9
ztpSa(C!^nAY{3^<FXJcAVS at CVZgmo!aIYalhba1uF}FnU1=sZU5;q}!hfFgzKJC5d
zPg&3F_ZM66574$$>E(HMt_viR__@}CE&Mwupt&dQ%k!qJ&z|A&qxnxsbTW>o!I6vT
zWqs!x-Q=y*S{J;%=mkGONwr=j*tN$+xlS<awJ-WlP*AOx=eu^+OF#X3u|p73UDAJf
z-doFhX-E92t?E<{f;e{fibTK1W894B1zTC~b5n=8LjPS(bmD*cBKr4OPu~<&C%>Ma
z-~Al<oBRUtaJ3kDzwZXt`;Y$|>rXGDmwNhH?{iaAy)LuXS0Fl}ceCE#{<EyVOB at +R
zFL;3UJ~vgr&Sm`dicb8=mUCY?!i}uIhbPvR;Fe*N`6TnY5uD6DWWDqhJs2Rp?rYFz
T;$5w_KD*J8s|laLXR3b!>b;%1
>From b887db2cb51c170ec8c6b0159768344d919cab10 Mon Sep 17 00:00:00 2001
From: ultimateforce21 <abdullahmohammad155 at gmail.com>
Date: Mon, 7 Jul 2025 23:52:24 -0400
Subject: [PATCH 19/48] Rebase disassembler annotations branch onto updated
DWARFExpressionEntry API
This rebases the `add-disassembler-annotations` work onto the
latest `add-dwarfexprentry-api` branch so that the instruction
annotation patches sit cleanly atop the new DWARFExpressionEntry
struct and helper API. All conflicts have been resolved and the
annotation code now integrates with the updated std::optional<AddressRange>-based
GetExpressionEntryAtAddress signature.
---
lldb/source/Core/Disassembler.cpp | 19 +++----------------
1 file changed, 3 insertions(+), 16 deletions(-)
diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp
index 710856404b67a..30a028d005dc1 100644
--- a/lldb/source/Core/Disassembler.cpp
+++ b/lldb/source/Core/Disassembler.cpp
@@ -49,7 +49,6 @@
#include <cstdint>
#include <cstring>
-#include <regex>
#include <utility>
#include <cassert>
@@ -740,28 +739,16 @@ void Instruction::Dump(lldb_private::Stream *s, uint32_t max_opcode_byte_size,
if (auto entryOrErr = expr_list.GetExpressionEntryAtAddress(func_load_addr, current_pc)) {
auto entry = *entryOrErr;
- // Translate file-range to load-space start.
- addr_t file_base = entry.file_range.GetBaseAddress().GetFileAddress();
- addr_t start_load_addr = file_base + (func_load_addr - expr_list.GetFuncFileAddress());
+ // Check if entry has a file_range, and filter on address if so.
+ if (!entry.file_range || entry.file_range->ContainsFileAddress(
+ (current_pc - func_load_addr) + expr_list.GetFuncFileAddress())) {
- if (current_pc >= start_load_addr) {
StreamString loc_str;
ABI *abi = exe_ctx->GetProcessPtr()->GetABI().get();
entry.expr->DumpLocation(&loc_str, eDescriptionLevelBrief, abi);
std::string loc_output = loc_str.GetString().str();
- // // Remove ", <decoding error> ..." segments.
- // std::regex decoding_err_re(", <decoding error>[^,]*");
- // loc_output = std::regex_replace(loc_output, decoding_err_re, "");
-
- // llvm::StringRef cleaned_output = llvm::StringRef(loc_output).trim();
-
- // // Only keep this annotation if there is still something useful left.
- // if (!cleaned_output.empty()) {
- // annotations.push_back(llvm::formatv("{0} = {1}", name, cleaned_output));
- // }
-
llvm::SmallVector<llvm::StringRef, 4> parts;
llvm::StringRef(loc_str.GetString()).split(parts, ", ");
>From 912ba6d5a7a00c9111aae12a89ef84ccf0de00b4 Mon Sep 17 00:00:00 2001
From: ultimateforce21 <abdullahmohammad155 at gmail.com>
Date: Wed, 9 Jul 2025 11:48:09 -0400
Subject: [PATCH 20/48] Add `PrintRegisterOnly` flag in `struct DIDumpOptions`
and created new `DWARFExpression::DumpLocationWithOptions` for simplified
expression printing
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This patch introduces a PrintRegisterOnly flag to the DIDumpOptions struct, enabling concise rendering of DWARF expressions for disassembler annotations.
Key changes:
- Added DumpLocationWithOptions to DWARFExpression for flexible dumping with DIDumpOptions.
- Updated DWARFExpression::print and Operation::print to respect PrintRegisterOnly, rendering registers like RDI without DW_OP_ or llvm: prefixes.
- Suppressed <decoding error> output when PrintRegisterOnly is true to avoid clutter during register-only disassembly output.
These changes are motivated by LLDB’s rich disassembler feature, where annotations should match user-facing register names without DWARF-level detail.
Test impact:
Some rich-disassembler tests that relied on DW_OP_ for validation were deprecated. Updated tests aligned with the new formatting will be added next.
---
.../include/lldb/Expression/DWARFExpression.h | 1 +
lldb/source/Core/Disassembler.cpp | 33 +--
lldb/source/Expression/DWARFExpression.cpp | 13 +-
.../rich-disassembler/TestRichDisassembler.py | 260 +-----------------
.../a_loop_with_local_variable.c | 8 -
.../b_multiple_stack_variables.c | 9 -
.../c_variable_passed_to_another_function.c | 11 -
.../rich-disassembler/e_control_flow_edge.c | 10 -
llvm/include/llvm/DebugInfo/DIContext.h | 1 +
llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp | 118 ++++----
10 files changed, 84 insertions(+), 380 deletions(-)
delete mode 100644 lldb/test/API/functionalities/rich-disassembler/a_loop_with_local_variable.c
delete mode 100644 lldb/test/API/functionalities/rich-disassembler/b_multiple_stack_variables.c
delete mode 100644 lldb/test/API/functionalities/rich-disassembler/c_variable_passed_to_another_function.c
delete mode 100644 lldb/test/API/functionalities/rich-disassembler/e_control_flow_edge.c
diff --git a/lldb/include/lldb/Expression/DWARFExpression.h b/lldb/include/lldb/Expression/DWARFExpression.h
index 0adbe3e8df2ee..6eb421bc94e90 100644
--- a/lldb/include/lldb/Expression/DWARFExpression.h
+++ b/lldb/include/lldb/Expression/DWARFExpression.h
@@ -158,6 +158,7 @@ class DWARFExpression {
}
void DumpLocation(Stream *s, lldb::DescriptionLevel level, ABI *abi) const;
+ void DumpLocationWithOptions(Stream *s, lldb::DescriptionLevel level, ABI *abi, llvm::DIDumpOptions options) const;
bool MatchesOperand(StackFrame &frame, const Instruction::Operand &op) const;
diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp
index 30a028d005dc1..683db0f541389 100644
--- a/lldb/source/Core/Disassembler.cpp
+++ b/lldb/source/Core/Disassembler.cpp
@@ -742,34 +742,19 @@ void Instruction::Dump(lldb_private::Stream *s, uint32_t max_opcode_byte_size,
// Check if entry has a file_range, and filter on address if so.
if (!entry.file_range || entry.file_range->ContainsFileAddress(
(current_pc - func_load_addr) + expr_list.GetFuncFileAddress())) {
-
+
StreamString loc_str;
ABI *abi = exe_ctx->GetProcessPtr()->GetABI().get();
- entry.expr->DumpLocation(&loc_str, eDescriptionLevelBrief, abi);
-
- std::string loc_output = loc_str.GetString().str();
-
- llvm::SmallVector<llvm::StringRef, 4> parts;
- llvm::StringRef(loc_str.GetString()).split(parts, ", ");
+ llvm::DIDumpOptions opts;
+ opts.ShowAddresses = false;
+ opts.PrintRegisterOnly = true; // <-- important: suppress DW_OP_... annotations, etc.
- // Reconstruct the string without the decoding error chunks
- std::string cleaned_output;
- bool first = true;
-
- for (const auto &part : parts) {
- if (part.contains("<decoding error>"))
- continue;
-
- if (!first)
- cleaned_output += ", ";
- cleaned_output += part.str();
- first = false;
- }
+ entry.expr->DumpLocationWithOptions(&loc_str, eDescriptionLevelBrief, abi, opts);
- // Only keep this annotation if there is still something useful left.
- llvm::StringRef cleaned_ref = llvm::StringRef(cleaned_output).trim();
- if (!cleaned_ref.empty()) {
- annotations.push_back(llvm::formatv("{0} = {1}", name, cleaned_ref));
+ // Only include if not empty
+ llvm::StringRef loc_clean = llvm::StringRef(loc_str.GetString()).trim();
+ if (!loc_clean.empty()) {
+ annotations.push_back(llvm::formatv("{0} = {1}", name, loc_clean));
}
}
}
diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp
index 4b2b111e08e6d..d0d4278c98ae5 100644
--- a/lldb/source/Expression/DWARFExpression.cpp
+++ b/lldb/source/Expression/DWARFExpression.cpp
@@ -67,6 +67,12 @@ void DWARFExpression::UpdateValue(uint64_t const_value,
void DWARFExpression::DumpLocation(Stream *s, lldb::DescriptionLevel level,
ABI *abi) const {
+ llvm::DIDumpOptions DumpOpts;
+ this->DumpLocationWithOptions(s, level, abi, DumpOpts);
+}
+
+void DWARFExpression::DumpLocationWithOptions(Stream *s, lldb::DescriptionLevel level,
+ ABI *abi, llvm::DIDumpOptions options) const {
auto *MCRegInfo = abi ? &abi->GetMCRegisterInfo() : nullptr;
auto GetRegName = [&MCRegInfo](uint64_t DwarfRegNum,
bool IsEH) -> llvm::StringRef {
@@ -78,10 +84,9 @@ void DWARFExpression::DumpLocation(Stream *s, lldb::DescriptionLevel level,
return llvm::StringRef(RegName);
return {};
};
- llvm::DIDumpOptions DumpOpts;
- DumpOpts.GetNameForDWARFReg = GetRegName;
- llvm::DWARFExpression(m_data.GetAsLLVM(), m_data.GetAddressByteSize())
- .print(s->AsRawOstream(), DumpOpts, nullptr);
+ options.GetNameForDWARFReg = GetRegName;
+ llvm::DWARFExpression expression (m_data.GetAsLLVM(), m_data.GetAddressByteSize());
+ expression.print(s->AsRawOstream(), options, nullptr);
}
RegisterKind DWARFExpression::GetRegisterKind() const { return m_reg_kind; }
diff --git a/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py b/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py
index d85e6488575b7..0164402dfa587 100644
--- a/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py
+++ b/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py
@@ -2,202 +2,6 @@
from lldbsuite.test.decorators import *
class TestRichDisassembler(TestBase):
-
- @no_debug_info_test
- def test_a_loop_with_local_variable(self):
- """
- Tests that the disassembler includes basic DWARF variable annotation in output.
- Specifically checks that local variables in a loop are shown with DW_OP locations.
- Additionally, it verifies that the disassembly does not contain decoding errors.
- """
- self.build(dictionary={
- 'C_SOURCES': 'a_loop_with_local_variable.c',
- 'CFLAGS_EXTRAS': '-g -O0'
- })
- exe = self.getBuildArtifact("a.out")
- target = self.dbg.CreateTarget(exe)
- self.assertTrue(target)
-
- # Set a breakpoint inside main's loop
- src_file = lldb.SBFileSpec("test_loop_function_call.c")
- breakpoint = target.BreakpointCreateByName("main")
- self.assertGreater(breakpoint.GetNumLocations(), 0)
-
- process = target.LaunchSimple(None, None, self.get_process_working_directory())
- self.assertTrue(process, "Failed to launch process")
- self.assertEqual(process.GetState(), lldb.eStateStopped)
-
- frame = process.GetSelectedThread().GetSelectedFrame()
- disasm = frame.Disassemble()
- print(disasm)
-
- # Check that we have DWARF annotations for variables
- self.assertIn("i = ", disasm)
- self.assertIn("DW_OP", disasm)
- self.assertNotIn("<decoding error>", disasm)
-
-
- @no_debug_info_test
- def test_b_multiple_stack_variables_O0(self):
- """
- Tests disassembler output for b_multiple_stack_variables.c built with -O0.
- This test checks that multiple local variables are annotated with DWARF
- and that their locations are distinct. It also ensures that no decoding errors appear.
- """
- self.build(dictionary={
- 'C_SOURCES': 'b_multiple_stack_variables.c',
- 'CFLAGS_EXTRAS': '-g -O0'
- })
- exe = self.getBuildArtifact("a.out")
- target = self.dbg.CreateTarget(exe)
- self.assertTrue(target)
-
- # Set a breakpoint inside main's loop
- src_file = lldb.SBFileSpec("test_loop_function_call.c")
- breakpoint = target.BreakpointCreateByName("main")
- self.assertGreater(breakpoint.GetNumLocations(), 0)
-
- process = target.LaunchSimple(None, None, self.get_process_working_directory())
- self.assertTrue(process, "Failed to launch process")
- self.assertEqual(process.GetState(), lldb.eStateStopped)
-
- frame = process.GetSelectedThread().GetSelectedFrame()
- disasm = frame.Disassemble()
- print(disasm)
-
- # Check that we have DWARF annotations for variables
- self.assertIn("a = ", disasm)
- self.assertIn("b = ", disasm)
- self.assertIn("c = ", disasm)
- self.assertIn("DW_OP", disasm)
- self.assertNotIn("<decoding error>", disasm)
-
-
- @no_debug_info_test
- def test_b_multiple_stack_variables_O1(self):
- """
- Tests disassembler output for b_multiple_stack_variables.c built with -O1.
- Due to optimizations, some variables may be optimized out.
- We only check for 'c' and ensure no decoding errors appear.
- """
- self.build(dictionary={
- 'C_SOURCES': 'b_multiple_stack_variables.c',
- 'CFLAGS_EXTRAS': '-g -O1'
- })
- exe = self.getBuildArtifact("a.out")
- target = self.dbg.CreateTarget(exe)
- self.assertTrue(target)
-
- breakpoint = target.BreakpointCreateByName("main")
- self.assertGreater(breakpoint.GetNumLocations(), 0)
-
- process = target.LaunchSimple(None, None, self.get_process_working_directory())
- self.assertTrue(process, "Failed to launch process")
- self.assertEqual(process.GetState(), lldb.eStateStopped)
-
- frame = process.GetSelectedThread().GetSelectedFrame()
- disasm = frame.Disassemble()
- print(disasm)
-
- self.assertIn("c = ", disasm)
- self.assertIn("DW_OP", disasm)
- self.assertNotIn("<decoding error>", disasm)
-
-
- @no_debug_info_test
- def test_c_variable_passed_to_another_function(self):
- """
- Tests disassembler output for c_variable_passed_to_another_function.c.
- This test checks that a variable passed to another function is annotated
- with DWARF and that its location is distinct. It also ensures that no decoding errors appear.
- """
- self.build(dictionary={
- 'C_SOURCES': 'c_variable_passed_to_another_function.c',
- 'CFLAGS_EXTRAS': '-g -O0'
- })
- exe = self.getBuildArtifact("a.out")
- target = self.dbg.CreateTarget(exe)
- self.assertTrue(target)
-
- breakpoint = target.BreakpointCreateByName("main")
- self.assertGreater(breakpoint.GetNumLocations(), 0)
-
- process = target.LaunchSimple(None, None, self.get_process_working_directory())
- self.assertTrue(process, "Failed to launch process")
- self.assertEqual(process.GetState(), lldb.eStateStopped)
-
- frame = process.GetSelectedThread().GetSelectedFrame()
- disasm = frame.Disassemble()
- print(disasm)
-
- self.assertIn("x = ", disasm)
- self.assertIn("DW_OP", disasm)
- self.assertNotIn("<decoding error>", disasm)
-
-
- @no_debug_info_test
- def test_c_variable_passed_to_another_function_O1(self):
- """
- Tests disassembler output for c_variable_passed_to_another_function.c built with -O1.
- """
- self.build(dictionary={
- 'C_SOURCES': 'c_variable_passed_to_another_function.c',
- 'CFLAGS_EXTRAS': '-g -O1'
- })
- exe = self.getBuildArtifact("a.out")
- target = self.dbg.CreateTarget(exe)
- self.assertTrue(target)
-
- breakpoint = target.BreakpointCreateByName("main")
- self.assertGreater(breakpoint.GetNumLocations(), 0)
-
- process = target.LaunchSimple(None, None, self.get_process_working_directory())
- self.assertTrue(process, "Failed to launch process")
- self.assertEqual(process.GetState(), lldb.eStateStopped)
-
- frame = process.GetSelectedThread().GetSelectedFrame()
- disasm = frame.Disassemble()
- print(disasm)
-
- self.assertIn("x = ", disasm)
- self.assertIn("arg = ", disasm)
- self.assertIn("DW_OP", disasm)
- self.assertNotIn("<decoding error>", disasm)
-
- @no_debug_info_test
- def test_d_original_example(self):
- """
- Tests disassembler output for d_original_example.c.
- This test checks that the disassembly includes basic DWARF variable annotations
- and that local variables in the main function are shown with DW_OP locations.
- Additionally, it verifies that the disassembly does not contain decoding errors.
- """
- self.build(dictionary={
- 'C_SOURCES': 'd_original_example.c',
- 'CFLAGS_EXTRAS': '-g -O0'
- })
- exe = self.getBuildArtifact("a.out")
- target = self.dbg.CreateTarget(exe)
- self.assertTrue(target)
-
- breakpoint = target.BreakpointCreateByName("main")
- self.assertGreater(breakpoint.GetNumLocations(), 0)
-
- process = target.LaunchSimple(None, None, self.get_process_working_directory())
- self.assertTrue(process, "Failed to launch process")
- self.assertEqual(process.GetState(), lldb.eStateStopped)
-
- frame = process.GetSelectedThread().GetSelectedFrame()
- disasm = frame.Disassemble()
- print(disasm)
-
- self.assertIn("argc = ", disasm)
- self.assertIn("argv = ", disasm)
- self.assertIn("i = ", disasm)
- self.assertIn("DW_OP", disasm)
- self.assertNotIn("<decoding error>", disasm)
-
- @no_debug_info_test
def test_d_original_example_O1(self):
"""
Tests disassembler output for d_original_example.c built with -O1.
@@ -224,68 +28,8 @@ def test_d_original_example_O1(self):
self.assertIn("argc = ", disasm)
self.assertIn("argv = ", disasm)
self.assertIn("i = ", disasm)
- self.assertIn("DW_OP_reg", disasm)
- self.assertIn("DW_OP_stack_value", disasm)
- self.assertNotIn("<decoding error>", disasm)
-
-
- @no_debug_info_test
- def test_e_control_flow_edge(self):
- """
- Tests disassembler output for e_control_flow_edge.c with a focus on control flow edges.
- This test checks that the disassembly includes basic DWARF variable annotations
- and that local variables in the main function are shown with DW_OP locations.
- Additionally, it verifies that the disassembly does not contain decoding errors.
- """
- self.build(dictionary={
- 'C_SOURCES': 'e_control_flow_edge.c',
- 'CFLAGS_EXTRAS': '-g -O0'
- })
- exe = self.getBuildArtifact("a.out")
- target = self.dbg.CreateTarget(exe)
- self.assertTrue(target)
-
- breakpoint = target.BreakpointCreateByName("main")
- self.assertGreater(breakpoint.GetNumLocations(), 0)
-
- process = target.LaunchSimple(None, None, self.get_process_working_directory())
- self.assertTrue(process, "Failed to launch process")
- self.assertEqual(process.GetState(), lldb.eStateStopped)
-
- frame = process.GetSelectedThread().GetSelectedFrame()
- disasm = frame.Disassemble()
- print(disasm)
-
- self.assertIn("a = ", disasm)
- self.assertIn("b = ", disasm)
- self.assertIn("DW_OP_", disasm)
- self.assertNotIn("<decoding error>", disasm)
-
- @no_debug_info_test
- def test_e_control_flow_edge_O1(self):
- """
- Tests disassembler output for e_control_flow_edge.c built with -O1.
- This test checks that the disassembly annotation does not contain decoding errors.
- """
- self.build(dictionary={
- 'C_SOURCES': 'e_control_flow_edge.c',
- 'CFLAGS_EXTRAS': '-g -O1'
- })
- exe = self.getBuildArtifact("a.out")
- target = self.dbg.CreateTarget(exe)
- self.assertTrue(target)
-
- breakpoint = target.BreakpointCreateByName("main")
- self.assertGreater(breakpoint.GetNumLocations(), 0)
-
- process = target.LaunchSimple(None, None, self.get_process_working_directory())
- self.assertTrue(process, "Failed to launch process")
- self.assertEqual(process.GetState(), lldb.eStateStopped)
-
- frame = process.GetSelectedThread().GetSelectedFrame()
- disasm = frame.Disassemble()
- print(disasm)
-
+ # self.assertIn("DW_OP_reg", disasm)
+ # self.assertIn("DW_OP_stack_value", disasm)
self.assertNotIn("<decoding error>", disasm)
diff --git a/lldb/test/API/functionalities/rich-disassembler/a_loop_with_local_variable.c b/lldb/test/API/functionalities/rich-disassembler/a_loop_with_local_variable.c
deleted file mode 100644
index 6555f3822187f..0000000000000
--- a/lldb/test/API/functionalities/rich-disassembler/a_loop_with_local_variable.c
+++ /dev/null
@@ -1,8 +0,0 @@
-#include <stdio.h>
-
-int main() {
- for (int i = 0; i < 3; ++i) {
- puts("Hi");
- }
- return 0;
-}
diff --git a/lldb/test/API/functionalities/rich-disassembler/b_multiple_stack_variables.c b/lldb/test/API/functionalities/rich-disassembler/b_multiple_stack_variables.c
deleted file mode 100644
index d3cd447b43d65..0000000000000
--- a/lldb/test/API/functionalities/rich-disassembler/b_multiple_stack_variables.c
+++ /dev/null
@@ -1,9 +0,0 @@
-#include <stdio.h>
-
-int main() {
- int a = 1;
- int b = 2;
- int c = a + b;
- return c;
-}
-
diff --git a/lldb/test/API/functionalities/rich-disassembler/c_variable_passed_to_another_function.c b/lldb/test/API/functionalities/rich-disassembler/c_variable_passed_to_another_function.c
deleted file mode 100644
index 9603bdc636043..0000000000000
--- a/lldb/test/API/functionalities/rich-disassembler/c_variable_passed_to_another_function.c
+++ /dev/null
@@ -1,11 +0,0 @@
-#include <stdio.h>
-
-void foo(int arg) {
- printf("%d\n", arg);
-}
-
-int main() {
- int x = 10;
- foo(x);
- return 0;
-}
diff --git a/lldb/test/API/functionalities/rich-disassembler/e_control_flow_edge.c b/lldb/test/API/functionalities/rich-disassembler/e_control_flow_edge.c
deleted file mode 100644
index d8d69f501eb4f..0000000000000
--- a/lldb/test/API/functionalities/rich-disassembler/e_control_flow_edge.c
+++ /dev/null
@@ -1,10 +0,0 @@
-#include <stdio.h>
-
-int main() {
- int a = 1;
- if (a > 0) {
- int b = 2;
- return b;
- }
- return a;
-}
diff --git a/llvm/include/llvm/DebugInfo/DIContext.h b/llvm/include/llvm/DebugInfo/DIContext.h
index 0347f90c236d1..e7e87bbfebf38 100644
--- a/llvm/include/llvm/DebugInfo/DIContext.h
+++ b/llvm/include/llvm/DebugInfo/DIContext.h
@@ -209,6 +209,7 @@ struct DIDumpOptions {
bool IsEH = false;
bool DumpNonSkeleton = false;
bool ShowAggregateErrors = false;
+ bool PrintRegisterOnly = false;
std::string JsonErrSummaryFile;
std::function<llvm::StringRef(uint64_t DwarfRegNum, bool IsEH)>
GetNameForDWARFReg;
diff --git a/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp b/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp
index 2ae5ff3efc8c5..fbf6fbf2cd368 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp
@@ -302,14 +302,16 @@ std::optional<unsigned> DWARFExpression::Operation::getSubCode() const {
bool DWARFExpression::Operation::print(raw_ostream &OS, DIDumpOptions DumpOpts,
const DWARFExpression *Expr,
DWARFUnit *U) const {
- if (Error) {
+ if (Error && !DumpOpts.PrintRegisterOnly) {
OS << "<decoding error>";
return false;
}
- StringRef Name = OperationEncodingString(Opcode);
- assert(!Name.empty() && "DW_OP has no name!");
- OS << Name;
+ if (!DumpOpts.PrintRegisterOnly) {
+ StringRef Name = OperationEncodingString(Opcode);
+ assert(!Name.empty() && "DW_OP has no name!");
+ OS << Name;
+ }
if ((Opcode >= DW_OP_breg0 && Opcode <= DW_OP_breg31) ||
(Opcode >= DW_OP_reg0 && Opcode <= DW_OP_reg31) ||
@@ -318,44 +320,46 @@ bool DWARFExpression::Operation::print(raw_ostream &OS, DIDumpOptions DumpOpts,
if (prettyPrintRegisterOp(U, OS, DumpOpts, Opcode, Operands))
return true;
- for (unsigned Operand = 0; Operand < Desc.Op.size(); ++Operand) {
- unsigned Size = Desc.Op[Operand];
- unsigned Signed = Size & Operation::SignBit;
-
- if (Size == Operation::SizeSubOpLEB) {
- StringRef SubName = SubOperationEncodingString(Opcode, Operands[Operand]);
- assert(!SubName.empty() && "DW_OP SubOp has no name!");
- OS << " " << SubName;
- } else if (Size == Operation::BaseTypeRef && U) {
- // For DW_OP_convert the operand may be 0 to indicate that conversion to
- // the generic type should be done. The same holds for DW_OP_reinterpret,
- // which is currently not supported.
- if (Opcode == DW_OP_convert && Operands[Operand] == 0)
- OS << " 0x0";
- else
- prettyPrintBaseTypeRef(U, OS, DumpOpts, Operands, Operand);
- } else if (Size == Operation::WasmLocationArg) {
- assert(Operand == 1);
- switch (Operands[0]) {
- case 0:
- case 1:
- case 2:
- case 3: // global as uint32
- case 4:
- OS << format(" 0x%" PRIx64, Operands[Operand]);
- break;
- default: assert(false);
+ if (!DumpOpts.PrintRegisterOnly) {
+ for (unsigned Operand = 0; Operand < Desc.Op.size(); ++Operand) {
+ unsigned Size = Desc.Op[Operand];
+ unsigned Signed = Size & Operation::SignBit;
+
+ if (Size == Operation::SizeSubOpLEB) {
+ StringRef SubName = SubOperationEncodingString(Opcode, Operands[Operand]);
+ assert(!SubName.empty() && "DW_OP SubOp has no name!");
+ OS << " " << SubName;
+ } else if (Size == Operation::BaseTypeRef && U) {
+ // For DW_OP_convert the operand may be 0 to indicate that conversion to
+ // the generic type should be done. The same holds for DW_OP_reinterpret,
+ // which is currently not supported.
+ if (Opcode == DW_OP_convert && Operands[Operand] == 0)
+ OS << " 0x0";
+ else
+ prettyPrintBaseTypeRef(U, OS, DumpOpts, Operands, Operand);
+ } else if (Size == Operation::WasmLocationArg) {
+ assert(Operand == 1);
+ switch (Operands[0]) {
+ case 0:
+ case 1:
+ case 2:
+ case 3: // global as uint32
+ case 4:
+ OS << format(" 0x%" PRIx64, Operands[Operand]);
+ break;
+ default: assert(false);
+ }
+ } else if (Size == Operation::SizeBlock) {
+ uint64_t Offset = Operands[Operand];
+ for (unsigned i = 0; i < Operands[Operand - 1]; ++i)
+ OS << format(" 0x%02x", Expr->Data.getU8(&Offset));
+ } else {
+ if (Signed)
+ OS << format(" %+" PRId64, (int64_t)Operands[Operand]);
+ else if (Opcode != DW_OP_entry_value &&
+ Opcode != DW_OP_GNU_entry_value)
+ OS << format(" 0x%" PRIx64, Operands[Operand]);
}
- } else if (Size == Operation::SizeBlock) {
- uint64_t Offset = Operands[Operand];
- for (unsigned i = 0; i < Operands[Operand - 1]; ++i)
- OS << format(" 0x%02x", Expr->Data.getU8(&Offset));
- } else {
- if (Signed)
- OS << format(" %+" PRId64, (int64_t)Operands[Operand]);
- else if (Opcode != DW_OP_entry_value &&
- Opcode != DW_OP_GNU_entry_value)
- OS << format(" 0x%" PRIx64, Operands[Operand]);
}
}
return true;
@@ -370,29 +374,31 @@ void DWARFExpression::print(raw_ostream &OS, DIDumpOptions DumpOpts,
for (auto &Op : *this) {
DumpOpts.IsEH = IsEH;
- if (!Op.print(OS, DumpOpts, this, U)) {
+ if (!Op.print(OS, DumpOpts, this, U) && !DumpOpts.PrintRegisterOnly) {
uint64_t FailOffset = Op.getEndOffset();
while (FailOffset < Data.getData().size())
OS << format(" %02x", Data.getU8(&FailOffset));
return;
}
- if (Op.getCode() == DW_OP_entry_value ||
- Op.getCode() == DW_OP_GNU_entry_value) {
- OS << "(";
- EntryValExprSize = Op.getRawOperand(0);
- EntryValStartOffset = Op.getEndOffset();
- continue;
- }
+ if (!DumpOpts.PrintRegisterOnly){
+ if (Op.getCode() == DW_OP_entry_value ||
+ Op.getCode() == DW_OP_GNU_entry_value) {
+ OS << "(";
+ EntryValExprSize = Op.getRawOperand(0);
+ EntryValStartOffset = Op.getEndOffset();
+ continue;
+ }
- if (EntryValExprSize) {
- EntryValExprSize -= Op.getEndOffset() - EntryValStartOffset;
- if (EntryValExprSize == 0)
- OS << ")";
+ if (EntryValExprSize) {
+ EntryValExprSize -= Op.getEndOffset() - EntryValStartOffset;
+ if (EntryValExprSize == 0)
+ OS << ")";
+ }
+
+ if (Op.getEndOffset() < Data.getData().size())
+ OS << ", ";
}
-
- if (Op.getEndOffset() < Data.getData().size())
- OS << ", ";
}
}
>From 09c4d04b33e16e25da3fbfb826c609c8bb42264e Mon Sep 17 00:00:00 2001
From: ultimateforce21 <abdullahmohammad155 at gmail.com>
Date: Sun, 20 Jul 2025 15:24:40 -0400
Subject: [PATCH 21/48] Add high-level comment explaining rich disassembly
annotation logic in Instruction::Dump
---
lldb/source/Core/Disassembler.cpp | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp
index 683db0f541389..af1c7209fbce7 100644
--- a/lldb/source/Core/Disassembler.cpp
+++ b/lldb/source/Core/Disassembler.cpp
@@ -705,6 +705,22 @@ void Instruction::Dump(lldb_private::Stream *s, uint32_t max_opcode_byte_size,
ss.FillLastLineToColumn(opcode_pos + opcode_column_width, ' ');
ss.PutCString(mnemonics);
+ // Add rich variable location annotations to the disassembly output.
+ //
+ // For each instruction, this block attempts to resolve in-scope variables
+ // and determine if the current PC falls within their
+ // DWARF location entry. If so, it prints a simplified annotation using the
+ // variable name and its resolved location (e.g., "var = reg; " ).
+ //
+ // Annotations are only included if the variable has a valid DWARF location
+ // entry, and the location string is non-empty after filtering. Decoding
+ // errors and DWARF opcodes are intentionally omitted to keep the output
+ // concise and user-friendly.
+ //
+ // The goal is to give users helpful live variable hints alongside the
+ // disassembled instruction stream, similar to how debug information
+ // enhances source-level debugging.
+
const size_t annotation_column = 150;
if (exe_ctx && exe_ctx->GetFramePtr()) {
>From 6e17f77c4300654c7664b52efc045983bb59721d Mon Sep 17 00:00:00 2001
From: ultimateforce21 <abdullahmohammad155 at gmail.com>
Date: Sun, 20 Jul 2025 15:38:01 -0400
Subject: [PATCH 22/48] Add comment clarifying annotation column length check
in Instruction::Dump
---
lldb/source/Core/Disassembler.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp
index af1c7209fbce7..7661cd9a77cb5 100644
--- a/lldb/source/Core/Disassembler.cpp
+++ b/lldb/source/Core/Disassembler.cpp
@@ -736,7 +736,8 @@ void Instruction::Dump(lldb_private::Stream *s, uint32_t max_opcode_byte_size,
if (sc.function)
func_load_addr = sc.function->GetAddress().GetLoadAddress(target_sp.get());
-
+ // Only annotate if the current disassembly line is short enough
+ // to keep annotations aligned past the desired annotation_column.
if(ss.GetSizeOfLastLine() < annotation_column) {
std::vector<std::string> annotations;
>From 31431c0c1e8348dec7761dc7052ca355bfc950dd Mon Sep 17 00:00:00 2001
From: ultimateforce21 <abdullahmohammad155 at gmail.com>
Date: Sun, 20 Jul 2025 16:03:50 -0400
Subject: [PATCH 23/48] Refactor variable annotation logic in
`Instruction::Dump` using `annotate_variable` lambda function
---
lldb/source/Core/Disassembler.cpp | 131 ++++++++++++++++--------------
1 file changed, 70 insertions(+), 61 deletions(-)
diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp
index 7661cd9a77cb5..053e41f9d9aa2 100644
--- a/lldb/source/Core/Disassembler.cpp
+++ b/lldb/source/Core/Disassembler.cpp
@@ -723,75 +723,83 @@ void Instruction::Dump(lldb_private::Stream *s, uint32_t max_opcode_byte_size,
const size_t annotation_column = 150;
- if (exe_ctx && exe_ctx->GetFramePtr()) {
+ auto annotate_variables = [&]() {
StackFrame *frame = exe_ctx->GetFramePtr();
TargetSP target_sp = exe_ctx->GetTargetSP();
- if (frame && target_sp) {
- addr_t current_pc = m_address.GetLoadAddress(target_sp.get());
- addr_t original_pc = frame->GetFrameCodeAddress().GetLoadAddress(target_sp.get());
- if (frame->ChangePC(current_pc)) {
- VariableListSP var_list_sp = frame->GetInScopeVariableList(true);
- SymbolContext sc = frame->GetSymbolContext(eSymbolContextFunction);
- addr_t func_load_addr = LLDB_INVALID_ADDRESS;
- if (sc.function)
- func_load_addr = sc.function->GetAddress().GetLoadAddress(target_sp.get());
-
- // Only annotate if the current disassembly line is short enough
- // to keep annotations aligned past the desired annotation_column.
- if(ss.GetSizeOfLastLine() < annotation_column) {
-
- std::vector<std::string> annotations;
-
- if (var_list_sp) {
- for (size_t i = 0; i < var_list_sp->GetSize(); ++i) {
- VariableSP var_sp = var_list_sp->GetVariableAtIndex(i);
- if (!var_sp)
- continue;
-
- const char *name = var_sp->GetName().AsCString();
- auto &expr_list = var_sp->LocationExpressionList();
- if (!expr_list.IsValid())
- continue;
- // Handle std::optional<DWARFExpressionEntry>.
- if (auto entryOrErr = expr_list.GetExpressionEntryAtAddress(func_load_addr, current_pc)) {
- auto entry = *entryOrErr;
-
- // Check if entry has a file_range, and filter on address if so.
- if (!entry.file_range || entry.file_range->ContainsFileAddress(
- (current_pc - func_load_addr) + expr_list.GetFuncFileAddress())) {
-
- StreamString loc_str;
- ABI *abi = exe_ctx->GetProcessPtr()->GetABI().get();
- llvm::DIDumpOptions opts;
- opts.ShowAddresses = false;
- opts.PrintRegisterOnly = true; // <-- important: suppress DW_OP_... annotations, etc.
-
- entry.expr->DumpLocationWithOptions(&loc_str, eDescriptionLevelBrief, abi, opts);
-
- // Only include if not empty
- llvm::StringRef loc_clean = llvm::StringRef(loc_str.GetString()).trim();
- if (!loc_clean.empty()) {
- annotations.push_back(llvm::formatv("{0} = {1}", name, loc_clean));
- }
- }
- }
- }
+ if (!frame || !target_sp)
+ return;
- if (!annotations.empty()) {
- ss.FillLastLineToColumn(annotation_column, ' ');
- ss.PutCString(" ; ");
- for (size_t i = 0; i < annotations.size(); ++i) {
- if (i > 0)
- ss.PutCString(", ");
- ss.PutCString(annotations[i]);
- }
- }
+ addr_t current_pc = m_address.GetLoadAddress(target_sp.get());
+ addr_t original_pc = frame->GetFrameCodeAddress().GetLoadAddress(target_sp.get());
+
+ if (!frame->ChangePC(current_pc))
+ return;
+
+ VariableListSP var_list_sp = frame->GetInScopeVariableList(true);
+ if (!var_list_sp)
+ return;
+
+ SymbolContext sc = frame->GetSymbolContext(eSymbolContextFunction);
+ addr_t func_load_addr = LLDB_INVALID_ADDRESS;
+ if (sc.function)
+ func_load_addr = sc.function->GetAddress().GetLoadAddress(target_sp.get());
+
+ // Only annotate if the current disassembly line is short enough
+ // to keep annotations aligned past the desired annotation_column.
+ if (ss.GetSizeOfLastLine() >= annotation_column)
+ return;
+
+ std::vector<std::string> annotations;
+
+ for (size_t i = 0; i < var_list_sp->GetSize(); ++i) {
+ VariableSP var_sp = var_list_sp->GetVariableAtIndex(i);
+ if (!var_sp)
+ continue;
+
+ const char *name = var_sp->GetName().AsCString();
+ auto &expr_list = var_sp->LocationExpressionList();
+ if (!expr_list.IsValid())
+ continue;
+
+ // Handle std::optional<DWARFExpressionEntry>.
+ if (auto entryOrErr = expr_list.GetExpressionEntryAtAddress(func_load_addr, current_pc)) {
+ auto entry = *entryOrErr;
+ // Check if entry has a file_range, and filter on address if so.
+ if (!entry.file_range || entry.file_range->ContainsFileAddress(
+ (current_pc - func_load_addr) + expr_list.GetFuncFileAddress())) {
+
+ StreamString loc_str;
+ ABI *abi = exe_ctx->GetProcessPtr()->GetABI().get();
+ llvm::DIDumpOptions opts;
+ opts.ShowAddresses = false;
+ opts.PrintRegisterOnly = true; // <-- important: suppress DW_OP_... annotations, etc.
+
+ entry.expr->DumpLocationWithOptions(&loc_str, eDescriptionLevelBrief, abi, opts);
+
+ // Only include if not empty.
+ llvm::StringRef loc_clean = llvm::StringRef(loc_str.GetString()).trim();
+ if (!loc_clean.empty()) {
+ annotations.push_back(llvm::formatv("{0} = {1}", name, loc_clean));
}
}
+ }
+ }
- frame->ChangePC(original_pc);
+ if (!annotations.empty()) {
+ ss.FillLastLineToColumn(annotation_column, ' ');
+ ss.PutCString(" ; ");
+ for (size_t i = 0; i < annotations.size(); ++i) {
+ if (i > 0)
+ ss.PutCString(", ");
+ ss.PutCString(annotations[i]);
}
}
+
+ frame->ChangePC(original_pc);
+ };
+
+ if (exe_ctx && exe_ctx->GetFramePtr()) {
+ annotate_variables();
}
if (!m_comment.empty()) {
@@ -803,6 +811,7 @@ void Instruction::Dump(lldb_private::Stream *s, uint32_t max_opcode_byte_size,
s->PutCString(ss.GetString());
}
+
bool Instruction::DumpEmulation(const ArchSpec &arch) {
std::unique_ptr<EmulateInstruction> insn_emulator_up(
EmulateInstruction::FindPlugin(arch, eInstructionTypeAny, nullptr));
>From 9c5cb8fa6d622cdd12db4c6a291265a991d3eb5e Mon Sep 17 00:00:00 2001
From: ultimateforce21 <abdullahmohammad155 at gmail.com>
Date: Sun, 20 Jul 2025 16:19:12 -0400
Subject: [PATCH 24/48] Use range-based for loop for variable list iteration in
Instruction::Dump
---
lldb/source/Core/Disassembler.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp
index 053e41f9d9aa2..74112faa44af1 100644
--- a/lldb/source/Core/Disassembler.cpp
+++ b/lldb/source/Core/Disassembler.cpp
@@ -751,8 +751,7 @@ void Instruction::Dump(lldb_private::Stream *s, uint32_t max_opcode_byte_size,
std::vector<std::string> annotations;
- for (size_t i = 0; i < var_list_sp->GetSize(); ++i) {
- VariableSP var_sp = var_list_sp->GetVariableAtIndex(i);
+ for (const VariableSP &var_sp : *var_list_sp) {
if (!var_sp)
continue;
>From ca8510c41add8d34a03eef9f8fce57fd42759b79 Mon Sep 17 00:00:00 2001
From: ultimateforce21 <abdullahmohammad155 at gmail.com>
Date: Sun, 20 Jul 2025 16:52:45 -0400
Subject: [PATCH 25/48] Consolidated DumpLocation and DumpLocationWithOptions
using default DIDumpOptions instead of having to introduce new function
---
lldb/include/lldb/Expression/DWARFExpression.h | 4 ++--
lldb/source/Core/Disassembler.cpp | 2 +-
lldb/source/Expression/DWARFExpression.cpp | 6 ------
3 files changed, 3 insertions(+), 9 deletions(-)
diff --git a/lldb/include/lldb/Expression/DWARFExpression.h b/lldb/include/lldb/Expression/DWARFExpression.h
index 6eb421bc94e90..07c6325254907 100644
--- a/lldb/include/lldb/Expression/DWARFExpression.h
+++ b/lldb/include/lldb/Expression/DWARFExpression.h
@@ -157,8 +157,8 @@ class DWARFExpression {
return data.GetByteSize() > 0;
}
- void DumpLocation(Stream *s, lldb::DescriptionLevel level, ABI *abi) const;
- void DumpLocationWithOptions(Stream *s, lldb::DescriptionLevel level, ABI *abi, llvm::DIDumpOptions options) const;
+ void DumpLocation(Stream *s, lldb::DescriptionLevel level, ABI *abi,
+ llvm::DIDumpOptions options = {}) const;
bool MatchesOperand(StackFrame &frame, const Instruction::Operand &op) const;
diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp
index 74112faa44af1..e14468ad5bcd2 100644
--- a/lldb/source/Core/Disassembler.cpp
+++ b/lldb/source/Core/Disassembler.cpp
@@ -773,7 +773,7 @@ void Instruction::Dump(lldb_private::Stream *s, uint32_t max_opcode_byte_size,
opts.ShowAddresses = false;
opts.PrintRegisterOnly = true; // <-- important: suppress DW_OP_... annotations, etc.
- entry.expr->DumpLocationWithOptions(&loc_str, eDescriptionLevelBrief, abi, opts);
+ entry.expr->DumpLocation(&loc_str, eDescriptionLevelBrief, abi, opts);
// Only include if not empty.
llvm::StringRef loc_clean = llvm::StringRef(loc_str.GetString()).trim();
diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp
index d0d4278c98ae5..3db9cad1fd260 100644
--- a/lldb/source/Expression/DWARFExpression.cpp
+++ b/lldb/source/Expression/DWARFExpression.cpp
@@ -66,12 +66,6 @@ void DWARFExpression::UpdateValue(uint64_t const_value,
}
void DWARFExpression::DumpLocation(Stream *s, lldb::DescriptionLevel level,
- ABI *abi) const {
- llvm::DIDumpOptions DumpOpts;
- this->DumpLocationWithOptions(s, level, abi, DumpOpts);
-}
-
-void DWARFExpression::DumpLocationWithOptions(Stream *s, lldb::DescriptionLevel level,
ABI *abi, llvm::DIDumpOptions options) const {
auto *MCRegInfo = abi ? &abi->GetMCRegisterInfo() : nullptr;
auto GetRegName = [&MCRegInfo](uint64_t DwarfRegNum,
>From ffefe5f08ba1f62c8ebea4e28f421e50aa193f66 Mon Sep 17 00:00:00 2001
From: ultimateforce21 <abdullahmohammad155 at gmail.com>
Date: Sun, 20 Jul 2025 17:54:23 -0400
Subject: [PATCH 26/48] Use `llvm::join` to simplify annotation output
formatting
---
lldb/source/Core/Disassembler.cpp | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp
index e14468ad5bcd2..cb3241da8b1a3 100644
--- a/lldb/source/Core/Disassembler.cpp
+++ b/lldb/source/Core/Disassembler.cpp
@@ -787,11 +787,7 @@ void Instruction::Dump(lldb_private::Stream *s, uint32_t max_opcode_byte_size,
if (!annotations.empty()) {
ss.FillLastLineToColumn(annotation_column, ' ');
ss.PutCString(" ; ");
- for (size_t i = 0; i < annotations.size(); ++i) {
- if (i > 0)
- ss.PutCString(", ");
- ss.PutCString(annotations[i]);
- }
+ ss.PutCString(llvm::join(annotations, ", "));
}
frame->ChangePC(original_pc);
>From dcddf1618c9841246a315f99fb44c9f5e8a2f9e5 Mon Sep 17 00:00:00 2001
From: ultimateforce21 <abdullahmohammad155 at gmail.com>
Date: Mon, 4 Aug 2025 16:01:05 -0400
Subject: [PATCH 27/48] Fix formatting to match LLVM style
---
.../lldb/Expression/DWARFExpressionList.h | 5 +++--
lldb/source/Expression/DWARFExpression.cpp | 3 ++-
lldb/source/Expression/DWARFExpressionList.cpp | 12 ++++++------
.../DebugInfo/DWARF/DWARFExpressionPrinter.cpp | 17 +++++++++--------
4 files changed, 20 insertions(+), 17 deletions(-)
diff --git a/lldb/include/lldb/Expression/DWARFExpressionList.h b/lldb/include/lldb/Expression/DWARFExpressionList.h
index 1bd762a9836e8..d303ad834b354 100644
--- a/lldb/include/lldb/Expression/DWARFExpressionList.h
+++ b/lldb/include/lldb/Expression/DWARFExpressionList.h
@@ -59,7 +59,7 @@ class DWARFExpressionList {
}
lldb::addr_t GetFuncFileAddress() { return m_func_file_addr; }
-
+
/// Represents an entry in the DWARFExpressionList with all needed metadata.
struct DWARFExpressionEntry {
/// Represents a DWARF location range in the DWARF unit’s file‐address space
@@ -69,7 +69,8 @@ class DWARFExpressionList {
/// Returns a DWARFExpressionEntry whose file_range contains the given
/// load‐address. `func_load_addr` is the load‐address of the function
- /// start; `load_addr` is the full runtime PC. On success, `expr` is non-null.
+ /// start; `load_addr` is the full runtime PC. On success, `expr` is
+ /// non-null.
std::optional<DWARFExpressionEntry>
GetExpressionEntryAtAddress(lldb::addr_t func_load_addr,
lldb::addr_t load_addr) const;
diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp
index c2c9f11642273..df56bcf5eb43e 100644
--- a/lldb/source/Expression/DWARFExpression.cpp
+++ b/lldb/source/Expression/DWARFExpression.cpp
@@ -67,7 +67,8 @@ void DWARFExpression::UpdateValue(uint64_t const_value,
}
void DWARFExpression::DumpLocation(Stream *s, lldb::DescriptionLevel level,
- ABI *abi, llvm::DIDumpOptions options) const {
+ ABI *abi,
+ llvm::DIDumpOptions options) const {
auto *MCRegInfo = abi ? &abi->GetMCRegisterInfo() : nullptr;
auto GetRegName = [&MCRegInfo](uint64_t DwarfRegNum,
bool IsEH) -> llvm::StringRef {
diff --git a/lldb/source/Expression/DWARFExpressionList.cpp b/lldb/source/Expression/DWARFExpressionList.cpp
index 88305fc17d1f0..ef7333518f008 100644
--- a/lldb/source/Expression/DWARFExpressionList.cpp
+++ b/lldb/source/Expression/DWARFExpressionList.cpp
@@ -6,7 +6,6 @@
//
//===----------------------------------------------------------------------===//
-#include "lldb/Core/AddressRange.h"
#include "lldb/Expression/DWARFExpressionList.h"
#include "lldb/Core/AddressRange.h"
#include "lldb/Symbol/Function.h"
@@ -57,7 +56,7 @@ bool DWARFExpressionList::ContainsAddress(lldb::addr_t func_load_addr,
std::optional<DWARFExpressionList::DWARFExpressionEntry>
DWARFExpressionList::GetExpressionEntryAtAddress(lldb::addr_t func_load_addr,
- lldb::addr_t load_addr) const {
+ lldb::addr_t load_addr) const {
if (const DWARFExpression *always = GetAlwaysValidExpr()) {
return DWARFExpressionEntry{std::nullopt, always};
}
@@ -65,9 +64,10 @@ DWARFExpressionList::GetExpressionEntryAtAddress(lldb::addr_t func_load_addr,
if (func_load_addr == LLDB_INVALID_ADDRESS)
func_load_addr = m_func_file_addr;
- // Guard against underflow when translating a load address back into file space.
+ // Guard against underflow when translating a load address back into file
+ // space.
if (load_addr < func_load_addr)
- return std::nullopt;
+ return std::nullopt;
// Guard against overflow.
lldb::addr_t delta = load_addr - func_load_addr;
@@ -75,10 +75,10 @@ DWARFExpressionList::GetExpressionEntryAtAddress(lldb::addr_t func_load_addr,
return std::nullopt;
lldb::addr_t file_pc = (load_addr - func_load_addr) + m_func_file_addr;
-
+
if (const auto *entry = m_exprs.FindEntryThatContains(file_pc)) {
AddressRange range_in_file(entry->GetRangeBase(),
- entry->GetRangeEnd() - entry->GetRangeBase());
+ entry->GetRangeEnd() - entry->GetRangeBase());
return DWARFExpressionEntry{range_in_file, &entry->data};
}
diff --git a/llvm/lib/DebugInfo/DWARF/DWARFExpressionPrinter.cpp b/llvm/lib/DebugInfo/DWARF/DWARFExpressionPrinter.cpp
index 2e22f40094ea6..3622ff3a886a1 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFExpressionPrinter.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFExpressionPrinter.cpp
@@ -68,23 +68,24 @@ static bool printOp(const DWARFExpression::Operation *Op, raw_ostream &OS,
if (!DumpOpts.PrintRegisterOnly) {
for (unsigned Operand = 0; Operand < Op->getDescription().Op.size();
- ++Operand) {
+ ++Operand) {
unsigned Size = Op->getDescription().Op[Operand];
unsigned Signed = Size & DWARFExpression::Operation::SignBit;
if (Size == DWARFExpression::Operation::SizeSubOpLEB) {
- StringRef SubName =
- SubOperationEncodingString(Op->getCode(), Op->getRawOperand(Operand));
+ StringRef SubName = SubOperationEncodingString(
+ Op->getCode(), Op->getRawOperand(Operand));
assert(!SubName.empty() && "DW_OP SubOp has no name!");
OS << " " << SubName;
} else if (Size == DWARFExpression::Operation::BaseTypeRef && U) {
// For DW_OP_convert the operand may be 0 to indicate that conversion to
- // the generic type should be done. The same holds for DW_OP_reinterpret,
- // which is currently not supported.
+ // the generic type should be done. The same holds for
+ // DW_OP_reinterpret, which is currently not supported.
if (Op->getCode() == DW_OP_convert && Op->getRawOperand(Operand) == 0)
OS << " 0x0";
else
- prettyPrintBaseTypeRef(U, OS, DumpOpts, Op->getRawOperands(), Operand);
+ prettyPrintBaseTypeRef(U, OS, DumpOpts, Op->getRawOperands(),
+ Operand);
} else if (Size == DWARFExpression::Operation::WasmLocationArg) {
assert(Operand == 1);
switch (Op->getRawOperand(0)) {
@@ -102,12 +103,12 @@ static bool printOp(const DWARFExpression::Operation *Op, raw_ostream &OS,
uint64_t Offset = Op->getRawOperand(Operand);
for (unsigned i = 0; i < Op->getRawOperand(Operand - 1); ++i)
OS << format(" 0x%02x",
- static_cast<uint8_t>(Expr->getData()[Offset++]));
+ static_cast<uint8_t>(Expr->getData()[Offset++]));
} else {
if (Signed)
OS << format(" %+" PRId64, (int64_t)Op->getRawOperand(Operand));
else if (Op->getCode() != DW_OP_entry_value &&
- Op->getCode() != DW_OP_GNU_entry_value)
+ Op->getCode() != DW_OP_GNU_entry_value)
OS << format(" 0x%" PRIx64, Op->getRawOperand(Operand));
}
}
>From 7bac074eca37d88d64eefec5cdc86f5dd440b39c Mon Sep 17 00:00:00 2001
From: ultimateforce21 <abdullahmohammad155 at gmail.com>
Date: Mon, 4 Aug 2025 16:10:24 -0400
Subject: [PATCH 28/48] More formatting fixes
---
.../rich-disassembler/TestRichDisassembler.py | 19 ++++---------------
1 file changed, 4 insertions(+), 15 deletions(-)
diff --git a/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py b/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py
index 0164402dfa587..7d29c3a9b6119 100644
--- a/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py
+++ b/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py
@@ -6,10 +6,9 @@ def test_d_original_example_O1(self):
"""
Tests disassembler output for d_original_example.c built with -O1.
"""
- self.build(dictionary={
- 'C_SOURCES': 'd_original_example.c',
- 'CFLAGS_EXTRAS': '-g -O1'
- })
+ self.build(
+ dictionary={'C_SOURCES': 'd_original_example.c', 'CFLAGS_EXTRAS': '-g -O1'}
+ )
exe = self.getBuildArtifact("a.out")
target = self.dbg.CreateTarget(exe)
self.assertTrue(target)
@@ -30,14 +29,4 @@ def test_d_original_example_O1(self):
self.assertIn("i = ", disasm)
# self.assertIn("DW_OP_reg", disasm)
# self.assertIn("DW_OP_stack_value", disasm)
- self.assertNotIn("<decoding error>", disasm)
-
-
-
-
-
-
-
-
-
-
+ self.assertNotIn("<decoding error>", disasm)
\ No newline at end of file
>From 79c0a9e1e7da0f727c41d27c9c6ff8a28bb7d06f Mon Sep 17 00:00:00 2001
From: ultimateforce21 <abdullahmohammad155 at gmail.com>
Date: Mon, 4 Aug 2025 22:22:59 -0400
Subject: [PATCH 29/48] Fix formatting for code and tests
---
lldb/source/Core/Disassembler.cpp | 31 ++++++++++---------
.../rich-disassembler/TestRichDisassembler.py | 3 +-
.../rich-disassembler/d_original_example.c | 6 ++--
3 files changed, 21 insertions(+), 19 deletions(-)
diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp
index d8924ea444ebd..0f68d38e07bed 100644
--- a/lldb/source/Core/Disassembler.cpp
+++ b/lldb/source/Core/Disassembler.cpp
@@ -732,7 +732,8 @@ void Instruction::Dump(lldb_private::Stream *s, uint32_t max_opcode_byte_size,
return;
addr_t current_pc = m_address.GetLoadAddress(target_sp.get());
- addr_t original_pc = frame->GetFrameCodeAddress().GetLoadAddress(target_sp.get());
+ addr_t original_pc =
+ frame->GetFrameCodeAddress().GetLoadAddress(target_sp.get());
if (!frame->ChangePC(current_pc))
return;
@@ -744,7 +745,8 @@ void Instruction::Dump(lldb_private::Stream *s, uint32_t max_opcode_byte_size,
SymbolContext sc = frame->GetSymbolContext(eSymbolContextFunction);
addr_t func_load_addr = LLDB_INVALID_ADDRESS;
if (sc.function)
- func_load_addr = sc.function->GetAddress().GetLoadAddress(target_sp.get());
+ func_load_addr =
+ sc.function->GetAddress().GetLoadAddress(target_sp.get());
// Only annotate if the current disassembly line is short enough
// to keep annotations aligned past the desired annotation_column.
@@ -763,22 +765,26 @@ void Instruction::Dump(lldb_private::Stream *s, uint32_t max_opcode_byte_size,
continue;
// Handle std::optional<DWARFExpressionEntry>.
- if (auto entryOrErr = expr_list.GetExpressionEntryAtAddress(func_load_addr, current_pc)) {
+ if (auto entryOrErr = expr_list.GetExpressionEntryAtAddress(
+ func_load_addr, current_pc)) {
auto entry = *entryOrErr;
// Check if entry has a file_range, and filter on address if so.
if (!entry.file_range || entry.file_range->ContainsFileAddress(
- (current_pc - func_load_addr) + expr_list.GetFuncFileAddress())) {
+ (current_pc - func_load_addr) +
+ expr_list.GetFuncFileAddress())) {
StreamString loc_str;
ABI *abi = exe_ctx->GetProcessPtr()->GetABI().get();
llvm::DIDumpOptions opts;
opts.ShowAddresses = false;
- opts.PrintRegisterOnly = true; // <-- important: suppress DW_OP_... annotations, etc.
+ opts.PrintRegisterOnly =
+ true; // <-- important: suppress DW_OP_... annotations, etc.
entry.expr->DumpLocation(&loc_str, eDescriptionLevelBrief, abi, opts);
-
+
// Only include if not empty.
- llvm::StringRef loc_clean = llvm::StringRef(loc_str.GetString()).trim();
+ llvm::StringRef loc_clean =
+ llvm::StringRef(loc_str.GetString()).trim();
if (!loc_clean.empty()) {
annotations.push_back(llvm::formatv("{0} = {1}", name, loc_clean));
}
@@ -808,7 +814,6 @@ void Instruction::Dump(lldb_private::Stream *s, uint32_t max_opcode_byte_size,
s->PutCString(ss.GetString());
}
-
bool Instruction::DumpEmulation(const ArchSpec &arch) {
std::unique_ptr<EmulateInstruction> insn_emulator_up(
EmulateInstruction::FindPlugin(arch, eInstructionTypeAny, nullptr));
@@ -820,9 +825,7 @@ bool Instruction::DumpEmulation(const ArchSpec &arch) {
return false;
}
-bool Instruction::CanSetBreakpoint () {
- return !HasDelaySlot();
-}
+bool Instruction::CanSetBreakpoint() { return !HasDelaySlot(); }
bool Instruction::HasDelaySlot() {
// Default is false.
@@ -1159,10 +1162,8 @@ void InstructionList::Append(lldb::InstructionSP &inst_sp) {
m_instructions.push_back(inst_sp);
}
-uint32_t
-InstructionList::GetIndexOfNextBranchInstruction(uint32_t start,
- bool ignore_calls,
- bool *found_calls) const {
+uint32_t InstructionList::GetIndexOfNextBranchInstruction(
+ uint32_t start, bool ignore_calls, bool *found_calls) const {
size_t num_instructions = m_instructions.size();
uint32_t next_branch = UINT32_MAX;
diff --git a/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py b/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py
index 7d29c3a9b6119..6b08eb3b43804 100644
--- a/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py
+++ b/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py
@@ -1,13 +1,14 @@
from lldbsuite.test.lldbtest import *
from lldbsuite.test.decorators import *
+
class TestRichDisassembler(TestBase):
def test_d_original_example_O1(self):
"""
Tests disassembler output for d_original_example.c built with -O1.
"""
self.build(
- dictionary={'C_SOURCES': 'd_original_example.c', 'CFLAGS_EXTRAS': '-g -O1'}
+ dictionary={"C_SOURCES": "d_original_example.c", "CFLAGS_EXTRAS": "-g -O1"}
)
exe = self.getBuildArtifact("a.out")
target = self.dbg.CreateTarget(exe)
diff --git a/lldb/test/API/functionalities/rich-disassembler/d_original_example.c b/lldb/test/API/functionalities/rich-disassembler/d_original_example.c
index 4f245f518a182..1c864753a0220 100644
--- a/lldb/test/API/functionalities/rich-disassembler/d_original_example.c
+++ b/lldb/test/API/functionalities/rich-disassembler/d_original_example.c
@@ -1,7 +1,7 @@
#include <stdio.h>
int main(int argc, char **argv) {
- for (int i = 1; i < argc; ++i)
- puts(argv[i]);
- return 0;
+ for (int i = 1; i < argc; ++i)
+ puts(argv[i]);
+ return 0;
}
>From c7f1b30ba586db5cd1b3ff19a9a8f713ffdf4d9d Mon Sep 17 00:00:00 2001
From: ultimateforce21 <abdullahmohammad155 at gmail.com>
Date: Wed, 6 Aug 2025 20:41:37 -0400
Subject: [PATCH 30/48] Ported annotations from Instruction::Dump to
Disassembler::PrintInstructions
---
lldb/source/Core/Disassembler.cpp | 195 ++++++++++++++----------------
1 file changed, 94 insertions(+), 101 deletions(-)
diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp
index 0f68d38e07bed..2edb2ee8fd9c4 100644
--- a/lldb/source/Core/Disassembler.cpp
+++ b/lldb/source/Core/Disassembler.cpp
@@ -379,6 +379,82 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
}
}
+ // Add rich variable location annotations to the disassembly output.
+ //
+ // For each instruction, this block attempts to resolve in-scope variables
+ // and determine if the current PC falls within their
+ // DWARF location entry. If so, it prints a simplified annotation using the
+ // variable name and its resolved location (e.g., "var = reg; " ).
+ //
+ // Annotations are only included if the variable has a valid DWARF location
+ // entry, and the location string is non-empty after filtering. Decoding
+ // errors and DWARF opcodes are intentionally omitted to keep the output
+ // concise and user-friendly.
+ //
+ // The goal is to give users helpful live variable hints alongside the
+ // disassembled instruction stream, similar to how debug information
+ // enhances source-level debugging.
+
+ auto annotate_variables = [&](Instruction &inst) -> std::vector<std::string> {
+ std::vector<std::string> annotations;
+
+ StackFrame *frame = exe_ctx.GetFramePtr();
+ TargetSP target_sp = exe_ctx.GetTargetSP();
+ if (!frame || !target_sp)
+ return annotations;
+
+ addr_t current_pc = inst.GetAddress().GetLoadAddress(target_sp.get());
+ addr_t original_pc = frame->GetFrameCodeAddress().GetLoadAddress(target_sp.get());
+
+ if (!frame->ChangePC(current_pc))
+ return annotations;
+
+ VariableListSP var_list_sp = frame->GetInScopeVariableList(true);
+ if (!var_list_sp)
+ return annotations;
+
+ SymbolContext sc = frame->GetSymbolContext(eSymbolContextFunction);
+ addr_t func_load_addr = sc.function
+ ? sc.function->GetAddress().GetLoadAddress(target_sp.get())
+ : LLDB_INVALID_ADDRESS;
+
+ for (const VariableSP &var_sp : *var_list_sp) {
+ if (!var_sp)
+ continue;
+
+ const char *name = var_sp->GetName().AsCString();
+ auto &expr_list = var_sp->LocationExpressionList();
+ if (!expr_list.IsValid())
+ continue;
+
+ if (auto entryOrErr = expr_list.GetExpressionEntryAtAddress(func_load_addr, current_pc)) {
+ auto entry = *entryOrErr;
+
+ if (!entry.file_range ||
+ entry.file_range->ContainsFileAddress(
+ (current_pc - func_load_addr) + expr_list.GetFuncFileAddress())) {
+
+ StreamString loc_str;
+ ABI *abi = exe_ctx.GetProcessPtr()->GetABI().get();
+ llvm::DIDumpOptions opts;
+ opts.ShowAddresses = false;
+ opts.PrintRegisterOnly = true;
+
+ entry.expr->DumpLocation(&loc_str, eDescriptionLevelBrief, abi, opts);
+
+ llvm::StringRef loc_clean = llvm::StringRef(loc_str.GetString()).trim();
+ if (!loc_clean.empty()) {
+ annotations.push_back(llvm::formatv("{0} = {1}", name, loc_clean));
+ }
+ }
+ }
+ }
+
+ frame->ChangePC(original_pc);
+ return annotations;
+ };
+
+
previous_symbol = nullptr;
SourceLine previous_line;
for (size_t i = 0; i < num_instructions_found; ++i) {
@@ -543,10 +619,25 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
const bool show_bytes = (options & eOptionShowBytes) != 0;
const bool show_control_flow_kind =
(options & eOptionShowControlFlowKind) != 0;
- inst->Dump(&strm, max_opcode_byte_size, true, show_bytes,
- show_control_flow_kind, &exe_ctx, &sc, &prev_sc, nullptr,
- address_text_size);
+
+ StreamString inst_line;
+
+ inst->Dump(&inst_line, max_opcode_byte_size, true, show_bytes,
+ show_control_flow_kind, &exe_ctx, &sc, &prev_sc, nullptr,
+ address_text_size);
+
+ std::vector<std::string> annotations = annotate_variables(*inst);
+ if (!annotations.empty()) {
+ const size_t annotation_column = 100;
+ inst_line.FillLastLineToColumn(annotation_column, ' ');
+ inst_line.PutCString("; ");
+ inst_line.PutCString(llvm::join(annotations, ", "));
+ }
+
+ strm.PutCString(inst_line.GetString());
strm.EOL();
+
+
} else {
break;
}
@@ -707,104 +798,6 @@ void Instruction::Dump(lldb_private::Stream *s, uint32_t max_opcode_byte_size,
ss.FillLastLineToColumn(opcode_pos + opcode_column_width, ' ');
ss.PutCString(mnemonics);
- // Add rich variable location annotations to the disassembly output.
- //
- // For each instruction, this block attempts to resolve in-scope variables
- // and determine if the current PC falls within their
- // DWARF location entry. If so, it prints a simplified annotation using the
- // variable name and its resolved location (e.g., "var = reg; " ).
- //
- // Annotations are only included if the variable has a valid DWARF location
- // entry, and the location string is non-empty after filtering. Decoding
- // errors and DWARF opcodes are intentionally omitted to keep the output
- // concise and user-friendly.
- //
- // The goal is to give users helpful live variable hints alongside the
- // disassembled instruction stream, similar to how debug information
- // enhances source-level debugging.
-
- const size_t annotation_column = 150;
-
- auto annotate_variables = [&]() {
- StackFrame *frame = exe_ctx->GetFramePtr();
- TargetSP target_sp = exe_ctx->GetTargetSP();
- if (!frame || !target_sp)
- return;
-
- addr_t current_pc = m_address.GetLoadAddress(target_sp.get());
- addr_t original_pc =
- frame->GetFrameCodeAddress().GetLoadAddress(target_sp.get());
-
- if (!frame->ChangePC(current_pc))
- return;
-
- VariableListSP var_list_sp = frame->GetInScopeVariableList(true);
- if (!var_list_sp)
- return;
-
- SymbolContext sc = frame->GetSymbolContext(eSymbolContextFunction);
- addr_t func_load_addr = LLDB_INVALID_ADDRESS;
- if (sc.function)
- func_load_addr =
- sc.function->GetAddress().GetLoadAddress(target_sp.get());
-
- // Only annotate if the current disassembly line is short enough
- // to keep annotations aligned past the desired annotation_column.
- if (ss.GetSizeOfLastLine() >= annotation_column)
- return;
-
- std::vector<std::string> annotations;
-
- for (const VariableSP &var_sp : *var_list_sp) {
- if (!var_sp)
- continue;
-
- const char *name = var_sp->GetName().AsCString();
- auto &expr_list = var_sp->LocationExpressionList();
- if (!expr_list.IsValid())
- continue;
-
- // Handle std::optional<DWARFExpressionEntry>.
- if (auto entryOrErr = expr_list.GetExpressionEntryAtAddress(
- func_load_addr, current_pc)) {
- auto entry = *entryOrErr;
- // Check if entry has a file_range, and filter on address if so.
- if (!entry.file_range || entry.file_range->ContainsFileAddress(
- (current_pc - func_load_addr) +
- expr_list.GetFuncFileAddress())) {
-
- StreamString loc_str;
- ABI *abi = exe_ctx->GetProcessPtr()->GetABI().get();
- llvm::DIDumpOptions opts;
- opts.ShowAddresses = false;
- opts.PrintRegisterOnly =
- true; // <-- important: suppress DW_OP_... annotations, etc.
-
- entry.expr->DumpLocation(&loc_str, eDescriptionLevelBrief, abi, opts);
-
- // Only include if not empty.
- llvm::StringRef loc_clean =
- llvm::StringRef(loc_str.GetString()).trim();
- if (!loc_clean.empty()) {
- annotations.push_back(llvm::formatv("{0} = {1}", name, loc_clean));
- }
- }
- }
- }
-
- if (!annotations.empty()) {
- ss.FillLastLineToColumn(annotation_column, ' ');
- ss.PutCString(" ; ");
- ss.PutCString(llvm::join(annotations, ", "));
- }
-
- frame->ChangePC(original_pc);
- };
-
- if (exe_ctx && exe_ctx->GetFramePtr()) {
- annotate_variables();
- }
-
if (!m_comment.empty()) {
ss.FillLastLineToColumn(
opcode_pos + opcode_column_width + operand_column_width, ' ');
>From 3d19b0294eafb14a550acb99b9b72c37895016fa Mon Sep 17 00:00:00 2001
From: ultimateforce21 <abdullahmohammad155 at gmail.com>
Date: Fri, 8 Aug 2025 11:13:21 -0400
Subject: [PATCH 31/48] Added `--rich` option for disassembler annotations and
updated SBFrame path**
* Added a new `--rich` (`-R`) command-line option to `CommandObjectDisassemble` to enable rich disassembly annotations for the current invocation.
* Plumbed a new `enable_rich_annotations` flag through:
* `Disassembler::Disassemble` overloads
* `Disassembler::PrintInstructions`
* `Instruction::Dump`
* `StackFrame::Disassemble`
* Updated `StackFrame::Disassemble` to take an optional `bool enable_rich_annotations` (default `false`) so the SB API can request annotated output without CLI involvement.
* Ensured annotations are only added when `enable_rich_annotations` is `true`; preserved caching for the non-rich path.
* Modified `Options.td` to define the new `--rich` option.
* Added/updated API test `TestRichDisassembler.py` to run `disassemble --rich -f` and check annotated output.
* Kept default behavior unchanged so existing scripts and IDE integrations are unaffected.
---
lldb/include/lldb/Core/Disassembler.h | 9 ++++---
lldb/include/lldb/Target/StackFrame.h | 3 ++-
.../Commands/CommandObjectDisassemble.cpp | 7 ++++-
.../Commands/CommandObjectDisassemble.h | 1 +
lldb/source/Commands/Options.td | 2 ++
lldb/source/Core/Disassembler.cpp | 22 ++++++++++------
lldb/source/Target/StackFrame.cpp | 17 ++++++++----
.../rich-disassembler/TestRichDisassembler.py | 26 +++++++++----------
8 files changed, 55 insertions(+), 32 deletions(-)
diff --git a/lldb/include/lldb/Core/Disassembler.h b/lldb/include/lldb/Core/Disassembler.h
index 21bacb14f9b25..94550bf9c9556 100644
--- a/lldb/include/lldb/Core/Disassembler.h
+++ b/lldb/include/lldb/Core/Disassembler.h
@@ -159,7 +159,8 @@ class Instruction {
const SymbolContext *sym_ctx,
const SymbolContext *prev_sym_ctx,
const FormatEntity::Entry *disassembly_addr_format,
- size_t max_address_text_size);
+ size_t max_address_text_size,
+ bool enable_rich_annotations = false);
virtual bool DoesBranch() = 0;
@@ -443,10 +444,10 @@ class Disassembler : public std::enable_shared_from_this<Disassembler>,
const ExecutionContext &exe_ctx, const Address &start,
Limit limit, bool mixed_source_and_assembly,
uint32_t num_mixed_context_lines, uint32_t options,
- Stream &strm);
+ Stream &strm, bool enable_rich_annotations = false);
static bool Disassemble(Debugger &debugger, const ArchSpec &arch,
- StackFrame &frame, Stream &strm);
+ StackFrame &frame, Stream &strm, bool enable_rich_annotations = false);
// Constructors and Destructors
Disassembler(const ArchSpec &arch, const char *flavor);
@@ -456,7 +457,7 @@ class Disassembler : public std::enable_shared_from_this<Disassembler>,
const ExecutionContext &exe_ctx,
bool mixed_source_and_assembly,
uint32_t num_mixed_context_lines, uint32_t options,
- Stream &strm);
+ Stream &strm, bool enable_rich_annotations = false);
size_t ParseInstructions(Target &target, Address address, Limit limit,
Stream *error_strm_ptr,
diff --git a/lldb/include/lldb/Target/StackFrame.h b/lldb/include/lldb/Target/StackFrame.h
index 3f51c9a7f22f0..3a1490b5c575c 100644
--- a/lldb/include/lldb/Target/StackFrame.h
+++ b/lldb/include/lldb/Target/StackFrame.h
@@ -321,7 +321,8 @@ class StackFrame : public ExecutionContextScope,
///
/// \return
/// C string with the assembly instructions for this function.
- const char *Disassemble();
+ const char *Disassemble(bool enable_rich_annotations = false);
+
/// Print a description of this frame using the provided frame format.
///
diff --git a/lldb/source/Commands/CommandObjectDisassemble.cpp b/lldb/source/Commands/CommandObjectDisassemble.cpp
index 70e687e19ac6d..7b7ab3314f5d6 100644
--- a/lldb/source/Commands/CommandObjectDisassemble.cpp
+++ b/lldb/source/Commands/CommandObjectDisassemble.cpp
@@ -154,6 +154,10 @@ Status CommandObjectDisassemble::CommandOptions::SetOptionValue(
}
} break;
+ case 'R': // --rich
+ enable_rich_annotations = true;
+ break;
+
case '\x01':
force = true;
break;
@@ -180,6 +184,7 @@ void CommandObjectDisassemble::CommandOptions::OptionParsingStarting(
end_addr = LLDB_INVALID_ADDRESS;
symbol_containing_addr = LLDB_INVALID_ADDRESS;
raw = false;
+ enable_rich_annotations = false;
plugin_name.clear();
Target *target =
@@ -550,7 +555,7 @@ void CommandObjectDisassemble::DoExecute(Args &command,
cpu_string, features_string, m_exe_ctx, cur_range.GetBaseAddress(),
limit, m_options.show_mixed,
m_options.show_mixed ? m_options.num_lines_context : 0, options,
- result.GetOutputStream())) {
+ result.GetOutputStream(), /*enable_rich_annotations=*/m_options.enable_rich_annotations)) {
result.SetStatus(eReturnStatusSuccessFinishResult);
} else {
if (m_options.symbol_containing_addr != LLDB_INVALID_ADDRESS) {
diff --git a/lldb/source/Commands/CommandObjectDisassemble.h b/lldb/source/Commands/CommandObjectDisassemble.h
index 4fbcd72d1c042..caaeabc15593d 100644
--- a/lldb/source/Commands/CommandObjectDisassemble.h
+++ b/lldb/source/Commands/CommandObjectDisassemble.h
@@ -78,6 +78,7 @@ class CommandObjectDisassemble : public CommandObjectParsed {
// in SetOptionValue if anything the selects a location is set.
lldb::addr_t symbol_containing_addr = 0;
bool force = false;
+ bool enable_rich_annotations = false;
};
CommandObjectDisassemble(CommandInterpreter &interpreter);
diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td
index acb741081cac3..15e03a2a3056b 100644
--- a/lldb/source/Commands/Options.td
+++ b/lldb/source/Commands/Options.td
@@ -361,6 +361,8 @@ let Command = "disassemble" in {
Desc<"Disassemble function containing this address.">;
def disassemble_options_force : Option<"force", "\\x01">, Groups<[2,3,4,5,7]>,
Desc<"Force disassembly of large functions.">;
+ def disassemble_options_rich : Option<"rich", "R">,
+ Desc<"Enable rich disassembly annotations for this invocation.">;
}
let Command = "diagnostics dump" in {
diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp
index 0f68d38e07bed..956db5eb8604c 100644
--- a/lldb/source/Core/Disassembler.cpp
+++ b/lldb/source/Core/Disassembler.cpp
@@ -175,7 +175,8 @@ bool Disassembler::Disassemble(Debugger &debugger, const ArchSpec &arch,
const Address &address, Limit limit,
bool mixed_source_and_assembly,
uint32_t num_mixed_context_lines,
- uint32_t options, Stream &strm) {
+ uint32_t options, Stream &strm,
+ bool enable_rich_annotations) {
if (!exe_ctx.GetTargetPtr())
return false;
@@ -191,8 +192,9 @@ bool Disassembler::Disassemble(Debugger &debugger, const ArchSpec &arch,
return false;
disasm_sp->PrintInstructions(debugger, arch, exe_ctx,
- mixed_source_and_assembly,
- num_mixed_context_lines, options, strm);
+ mixed_source_and_assembly,
+ num_mixed_context_lines, options, strm,
+ /*enable_rich_annotations=*/enable_rich_annotations);
return true;
}
@@ -287,7 +289,8 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
const ExecutionContext &exe_ctx,
bool mixed_source_and_assembly,
uint32_t num_mixed_context_lines,
- uint32_t options, Stream &strm) {
+ uint32_t options, Stream &strm,
+ bool enable_rich_annotations) {
// We got some things disassembled...
size_t num_instructions_found = GetInstructionList().GetSize();
@@ -545,7 +548,8 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
(options & eOptionShowControlFlowKind) != 0;
inst->Dump(&strm, max_opcode_byte_size, true, show_bytes,
show_control_flow_kind, &exe_ctx, &sc, &prev_sc, nullptr,
- address_text_size);
+ address_text_size,
+ enable_rich_annotations);
strm.EOL();
} else {
break;
@@ -554,7 +558,8 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
}
bool Disassembler::Disassemble(Debugger &debugger, const ArchSpec &arch,
- StackFrame &frame, Stream &strm) {
+ StackFrame &frame, Stream &strm,
+ bool enable_rich_annotations) {
constexpr const char *plugin_name = nullptr;
constexpr const char *flavor = nullptr;
constexpr const char *cpu = nullptr;
@@ -641,7 +646,8 @@ void Instruction::Dump(lldb_private::Stream *s, uint32_t max_opcode_byte_size,
const SymbolContext *sym_ctx,
const SymbolContext *prev_sym_ctx,
const FormatEntity::Entry *disassembly_addr_format,
- size_t max_address_text_size) {
+ size_t max_address_text_size,
+ bool enable_rich_annotations) {
size_t opcode_column_width = 7;
const size_t operand_column_width = 25;
@@ -801,7 +807,7 @@ void Instruction::Dump(lldb_private::Stream *s, uint32_t max_opcode_byte_size,
frame->ChangePC(original_pc);
};
- if (exe_ctx && exe_ctx->GetFramePtr()) {
+ if (enable_rich_annotations && exe_ctx && exe_ctx->GetFramePtr()) {
annotate_variables();
}
diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp
index d97a814952186..67a38c2e405a4 100644
--- a/lldb/source/Target/StackFrame.cpp
+++ b/lldb/source/Target/StackFrame.cpp
@@ -262,15 +262,22 @@ bool StackFrame::ChangePC(addr_t pc) {
return true;
}
-const char *StackFrame::Disassemble() {
+const char *StackFrame::Disassemble(bool enable_rich_annotations) {
std::lock_guard<std::recursive_mutex> guard(m_mutex);
- if (!m_disassembly.Empty())
- return m_disassembly.GetData();
+
+ // Keep the existing cache only for the plain (non-rich) path.
+ if (!enable_rich_annotations) {
+ if (!m_disassembly.Empty())
+ return m_disassembly.GetData();
+ }
ExecutionContext exe_ctx(shared_from_this());
if (Target *target = exe_ctx.GetTargetPtr()) {
- Disassembler::Disassemble(target->GetDebugger(), target->GetArchitecture(),
- *this, m_disassembly);
+ Disassembler::Disassemble(target->GetDebugger(),
+ target->GetArchitecture(),
+ *this,
+ m_disassembly,
+ /*enable_rich_annotations=*/enable_rich_annotations);
}
return m_disassembly.Empty() ? nullptr : m_disassembly.GetData();
diff --git a/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py b/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py
index 6b08eb3b43804..86fc46b41c6e5 100644
--- a/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py
+++ b/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py
@@ -1,11 +1,12 @@
from lldbsuite.test.lldbtest import *
from lldbsuite.test.decorators import *
-
+import lldb
class TestRichDisassembler(TestBase):
def test_d_original_example_O1(self):
"""
- Tests disassembler output for d_original_example.c built with -O1.
+ Tests disassembler output for d_original_example.c built with -O1,
+ using the CLI with --rich to enable annotations.
"""
self.build(
dictionary={"C_SOURCES": "d_original_example.c", "CFLAGS_EXTRAS": "-g -O1"}
@@ -14,20 +15,19 @@ def test_d_original_example_O1(self):
target = self.dbg.CreateTarget(exe)
self.assertTrue(target)
- breakpoint = target.BreakpointCreateByName("main")
- self.assertGreater(breakpoint.GetNumLocations(), 0)
+ bp = target.BreakpointCreateByName("main")
+ self.assertGreater(bp.GetNumLocations(), 0)
process = target.LaunchSimple(None, None, self.get_process_working_directory())
self.assertTrue(process, "Failed to launch process")
self.assertEqual(process.GetState(), lldb.eStateStopped)
- frame = process.GetSelectedThread().GetSelectedFrame()
- disasm = frame.Disassemble()
- print(disasm)
+ # Run the CLI command and read output from self.res
+ self.runCmd("disassemble --rich -f", check=True)
+ out = self.res.GetOutput()
+ print(out)
- self.assertIn("argc = ", disasm)
- self.assertIn("argv = ", disasm)
- self.assertIn("i = ", disasm)
- # self.assertIn("DW_OP_reg", disasm)
- # self.assertIn("DW_OP_stack_value", disasm)
- self.assertNotIn("<decoding error>", disasm)
\ No newline at end of file
+ self.assertIn("argc = ", out)
+ self.assertIn("argv = ", out)
+ self.assertIn("i = ", out)
+ self.assertNotIn("<decoding error>", out)
>From 6ca4bb602ca76011af7da9ffd77f977f9314d60c Mon Sep 17 00:00:00 2001
From: ultimateforce21 <abdullahmohammad155 at gmail.com>
Date: Fri, 8 Aug 2025 11:33:18 -0400
Subject: [PATCH 32/48] Formatting changes.
---
lldb/include/lldb/Core/Disassembler.h | 20 +++++------
lldb/include/lldb/Target/StackFrame.h | 1 -
.../Commands/CommandObjectDisassemble.cpp | 3 +-
lldb/source/Core/Disassembler.cpp | 26 +++++++--------
lldb/source/Target/StackFrame.cpp | 33 +++++++++----------
.../rich-disassembler/TestRichDisassembler.py | 1 +
6 files changed, 39 insertions(+), 45 deletions(-)
diff --git a/lldb/include/lldb/Core/Disassembler.h b/lldb/include/lldb/Core/Disassembler.h
index 94550bf9c9556..a35c04bd3c574 100644
--- a/lldb/include/lldb/Core/Disassembler.h
+++ b/lldb/include/lldb/Core/Disassembler.h
@@ -170,7 +170,7 @@ class Instruction {
virtual bool IsAuthenticated() = 0;
- bool CanSetBreakpoint ();
+ bool CanSetBreakpoint();
virtual size_t Decode(const Disassembler &disassembler,
const DataExtractor &data,
@@ -283,7 +283,7 @@ std::function<bool(const Instruction::Operand &)> FetchImmOp(int64_t &imm);
std::function<bool(const Instruction::Operand &)>
MatchOpType(Instruction::Operand::Type type);
-}
+} // namespace OperandMatchers
class InstructionList {
public:
@@ -315,20 +315,19 @@ class InstructionList {
/// @param[in] ignore_calls
/// It true, then fine the first branch instruction that isn't
/// a function call (a branch that calls and returns to the next
- /// instruction). If false, find the instruction index of any
+ /// instruction). If false, find the instruction index of any
/// branch in the list.
- ///
+ ///
/// @param[out] found_calls
- /// If non-null, this will be set to true if any calls were found in
+ /// If non-null, this will be set to true if any calls were found in
/// extending the range.
- ///
+ ///
/// @return
/// The instruction index of the first branch that is at or past
- /// \a start. Returns UINT32_MAX if no matching branches are
+ /// \a start. Returns UINT32_MAX if no matching branches are
/// found.
//------------------------------------------------------------------
- uint32_t GetIndexOfNextBranchInstruction(uint32_t start,
- bool ignore_calls,
+ uint32_t GetIndexOfNextBranchInstruction(uint32_t start, bool ignore_calls,
bool *found_calls) const;
uint32_t GetIndexOfInstructionAtLoadAddress(lldb::addr_t load_addr,
@@ -447,7 +446,8 @@ class Disassembler : public std::enable_shared_from_this<Disassembler>,
Stream &strm, bool enable_rich_annotations = false);
static bool Disassemble(Debugger &debugger, const ArchSpec &arch,
- StackFrame &frame, Stream &strm, bool enable_rich_annotations = false);
+ StackFrame &frame, Stream &strm,
+ bool enable_rich_annotations = false);
// Constructors and Destructors
Disassembler(const ArchSpec &arch, const char *flavor);
diff --git a/lldb/include/lldb/Target/StackFrame.h b/lldb/include/lldb/Target/StackFrame.h
index 3a1490b5c575c..a970743f0fa5b 100644
--- a/lldb/include/lldb/Target/StackFrame.h
+++ b/lldb/include/lldb/Target/StackFrame.h
@@ -323,7 +323,6 @@ class StackFrame : public ExecutionContextScope,
/// C string with the assembly instructions for this function.
const char *Disassemble(bool enable_rich_annotations = false);
-
/// Print a description of this frame using the provided frame format.
///
/// \param[out] strm
diff --git a/lldb/source/Commands/CommandObjectDisassemble.cpp b/lldb/source/Commands/CommandObjectDisassemble.cpp
index 7b7ab3314f5d6..5b7249be80a3e 100644
--- a/lldb/source/Commands/CommandObjectDisassemble.cpp
+++ b/lldb/source/Commands/CommandObjectDisassemble.cpp
@@ -555,7 +555,8 @@ void CommandObjectDisassemble::DoExecute(Args &command,
cpu_string, features_string, m_exe_ctx, cur_range.GetBaseAddress(),
limit, m_options.show_mixed,
m_options.show_mixed ? m_options.num_lines_context : 0, options,
- result.GetOutputStream(), /*enable_rich_annotations=*/m_options.enable_rich_annotations)) {
+ result.GetOutputStream(),
+ /*enable_rich_annotations=*/m_options.enable_rich_annotations)) {
result.SetStatus(eReturnStatusSuccessFinishResult);
} else {
if (m_options.symbol_containing_addr != LLDB_INVALID_ADDRESS) {
diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp
index 956db5eb8604c..3e94fa11c4ad3 100644
--- a/lldb/source/Core/Disassembler.cpp
+++ b/lldb/source/Core/Disassembler.cpp
@@ -168,15 +168,12 @@ Disassembler::DisassembleBytes(const ArchSpec &arch, const char *plugin_name,
return disasm_sp;
}
-bool Disassembler::Disassemble(Debugger &debugger, const ArchSpec &arch,
- const char *plugin_name, const char *flavor,
- const char *cpu, const char *features,
- const ExecutionContext &exe_ctx,
- const Address &address, Limit limit,
- bool mixed_source_and_assembly,
- uint32_t num_mixed_context_lines,
- uint32_t options, Stream &strm,
- bool enable_rich_annotations) {
+bool Disassembler::Disassemble(
+ Debugger &debugger, const ArchSpec &arch, const char *plugin_name,
+ const char *flavor, const char *cpu, const char *features,
+ const ExecutionContext &exe_ctx, const Address &address, Limit limit,
+ bool mixed_source_and_assembly, uint32_t num_mixed_context_lines,
+ uint32_t options, Stream &strm, bool enable_rich_annotations) {
if (!exe_ctx.GetTargetPtr())
return false;
@@ -191,10 +188,10 @@ bool Disassembler::Disassemble(Debugger &debugger, const ArchSpec &arch,
if (bytes_disassembled == 0)
return false;
- disasm_sp->PrintInstructions(debugger, arch, exe_ctx,
- mixed_source_and_assembly,
- num_mixed_context_lines, options, strm,
- /*enable_rich_annotations=*/enable_rich_annotations);
+ disasm_sp->PrintInstructions(
+ debugger, arch, exe_ctx, mixed_source_and_assembly,
+ num_mixed_context_lines, options, strm,
+ /*enable_rich_annotations=*/enable_rich_annotations);
return true;
}
@@ -548,8 +545,7 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
(options & eOptionShowControlFlowKind) != 0;
inst->Dump(&strm, max_opcode_byte_size, true, show_bytes,
show_control_flow_kind, &exe_ctx, &sc, &prev_sc, nullptr,
- address_text_size,
- enable_rich_annotations);
+ address_text_size, enable_rich_annotations);
strm.EOL();
} else {
break;
diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp
index 67a38c2e405a4..0aa93e635b866 100644
--- a/lldb/source/Target/StackFrame.cpp
+++ b/lldb/source/Target/StackFrame.cpp
@@ -273,11 +273,9 @@ const char *StackFrame::Disassemble(bool enable_rich_annotations) {
ExecutionContext exe_ctx(shared_from_this());
if (Target *target = exe_ctx.GetTargetPtr()) {
- Disassembler::Disassemble(target->GetDebugger(),
- target->GetArchitecture(),
- *this,
- m_disassembly,
- /*enable_rich_annotations=*/enable_rich_annotations);
+ Disassembler::Disassemble(
+ target->GetDebugger(), target->GetArchitecture(), *this, m_disassembly,
+ /*enable_rich_annotations=*/enable_rich_annotations);
}
return m_disassembly.Empty() ? nullptr : m_disassembly.GetData();
@@ -445,10 +443,10 @@ VariableList *StackFrame::GetVariableList(bool get_file_globals,
const bool get_child_variables = true;
const bool can_create = true;
const bool stop_if_child_block_is_inlined_function = true;
- frame_block->AppendBlockVariables(can_create, get_child_variables,
- stop_if_child_block_is_inlined_function,
- [](Variable *v) { return true; },
- m_variable_list_sp.get());
+ frame_block->AppendBlockVariables(
+ can_create, get_child_variables,
+ stop_if_child_block_is_inlined_function,
+ [](Variable *v) { return true; }, m_variable_list_sp.get());
}
}
@@ -1232,10 +1230,12 @@ StackFrame::GetValueObjectForFrameVariable(const VariableSP &variable_sp,
VariableList *var_list = GetVariableList(true, nullptr);
if (var_list) {
// Make sure the variable is a frame variable
- const uint32_t var_idx = var_list->FindIndexForVariable(variable_sp.get());
+ const uint32_t var_idx =
+ var_list->FindIndexForVariable(variable_sp.get());
const uint32_t num_variables = var_list->GetSize();
if (var_idx < num_variables) {
- valobj_sp = m_variable_list_value_objects.GetValueObjectAtIndex(var_idx);
+ valobj_sp =
+ m_variable_list_value_objects.GetValueObjectAtIndex(var_idx);
if (!valobj_sp) {
if (m_variable_list_value_objects.GetSize() < num_variables)
m_variable_list_value_objects.Resize(num_variables);
@@ -1769,11 +1769,9 @@ lldb::ValueObjectSP DoGuessValueAt(StackFrame &frame, ConstString reg,
if (clobbered_reg_matcher(operands[0])) {
origin_operand = &operands[1];
- }
- else if (clobbered_reg_matcher(operands[1])) {
+ } else if (clobbered_reg_matcher(operands[1])) {
origin_operand = &operands[0];
- }
- else {
+ } else {
continue;
}
@@ -1799,8 +1797,7 @@ lldb::ValueObjectSP DoGuessValueAt(StackFrame &frame, ConstString reg,
if (!source_path) {
continue;
}
- source_path =
- GetValueForDereferincingOffset(frame, source_path, offset);
+ source_path = GetValueForDereferincingOffset(frame, source_path, offset);
}
if (source_path) {
@@ -1810,7 +1807,7 @@ lldb::ValueObjectSP DoGuessValueAt(StackFrame &frame, ConstString reg,
return ValueObjectSP();
}
-}
+} // namespace
lldb::ValueObjectSP StackFrame::GuessValueForRegisterAndOffset(ConstString reg,
int64_t offset) {
diff --git a/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py b/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py
index 86fc46b41c6e5..d54c305f30761 100644
--- a/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py
+++ b/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py
@@ -2,6 +2,7 @@
from lldbsuite.test.decorators import *
import lldb
+
class TestRichDisassembler(TestBase):
def test_d_original_example_O1(self):
"""
>From b1f13e7078e805c9807f83ebed2e8b9e208df9d4 Mon Sep 17 00:00:00 2001
From: ultimateforce21 <abdullahmohammad155 at gmail.com>
Date: Sat, 9 Aug 2025 01:37:15 -0400
Subject: [PATCH 33/48] Redo Workflow tests
---
.../functionalities/rich-disassembler/TestRichDisassembler.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py b/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py
index d54c305f30761..4bad4a4f4986b 100644
--- a/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py
+++ b/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py
@@ -7,7 +7,7 @@ class TestRichDisassembler(TestBase):
def test_d_original_example_O1(self):
"""
Tests disassembler output for d_original_example.c built with -O1,
- using the CLI with --rich to enable annotations.
+ using the CLI with --rich for enabled annotations.
"""
self.build(
dictionary={"C_SOURCES": "d_original_example.c", "CFLAGS_EXTRAS": "-g -O1"}
>From 10fddc43855bdf154cc2cf24ced0452b03debced Mon Sep 17 00:00:00 2001
From: ultimateforce21 <abdullahmohammad155 at gmail.com>
Date: Sat, 9 Aug 2025 15:39:07 -0400
Subject: [PATCH 34/48] Added basic stateful variable location annotations to
disassembly output
This change introduces a simple live-variable tracking system for annotated
disassembly. While iterating over instructions, we now maintain an
unordered_map keyed by `lldb::user_id_t` to remember each in-scope variable's
last known location string.
For each instruction:
* If a variable is new, print `name = location` and add it to the map.
* If a variable's location has changed, print the updated mapping.
* If a previously tracked variable is no longer found, print
`name = <undef>` and remove it.
This produces concise, stateful annotations that only update when needed,
reducing noise in the disassembly while still showing variable lifetimes.
---
lldb/source/Core/Disassembler.cpp | 111 ++++++++++++++++++++++--------
1 file changed, 83 insertions(+), 28 deletions(-)
diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp
index f90c2f3b21115..315cd54c70e1d 100644
--- a/lldb/source/Core/Disassembler.cpp
+++ b/lldb/source/Core/Disassembler.cpp
@@ -49,6 +49,7 @@
#include <cstdint>
#include <cstring>
+#include <unordered_map>
#include <utility>
#include <cassert>
@@ -394,67 +395,121 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
// The goal is to give users helpful live variable hints alongside the
// disassembled instruction stream, similar to how debug information
// enhances source-level debugging.
-
+
+ struct VarState {
+ std::string name; // display name
+ std::string last_loc; // last printed location (empty means <undef>)
+ bool seen_this_inst = false;
+ };
+
+ // Track live variables across instructions (keyed by stable LLDB user_id_t)
+ std::unordered_map<lldb::user_id_t, VarState> live_vars;
+
+ // Stateful annotator: updates live_vars and returns only what should be printed for THIS instruction.
auto annotate_variables = [&](Instruction &inst) -> std::vector<std::string> {
- std::vector<std::string> annotations;
+ std::vector<std::string> events;
StackFrame *frame = exe_ctx.GetFramePtr();
TargetSP target_sp = exe_ctx.GetTargetSP();
- if (!frame || !target_sp)
- return annotations;
+ ProcessSP process_sp = exe_ctx.GetProcessSP();
+ if (!frame || !target_sp || !process_sp)
+ return events;
+
+ // Reset "seen" flags for this instruction
+ for (auto &kv : live_vars)
+ kv.second.seen_this_inst = false;
addr_t current_pc = inst.GetAddress().GetLoadAddress(target_sp.get());
addr_t original_pc = frame->GetFrameCodeAddress().GetLoadAddress(target_sp.get());
+ // We temporarily move the frame PC so variable locations resolve at this inst
if (!frame->ChangePC(current_pc))
- return annotations;
+ return events;
VariableListSP var_list_sp = frame->GetInScopeVariableList(true);
- if (!var_list_sp)
- return annotations;
+ if (!var_list_sp) {
+ // No variables in scope: everything previously live becomes <undef>
+ for (auto it = live_vars.begin(); it != live_vars.end(); ) {
+ events.push_back(llvm::formatv("{0} = <undef>", it->second.name).str());
+ it = live_vars.erase(it);
+ }
+ frame->ChangePC(original_pc);
+ return events;
+ }
SymbolContext sc = frame->GetSymbolContext(eSymbolContextFunction);
addr_t func_load_addr = sc.function
- ? sc.function->GetAddress().GetLoadAddress(target_sp.get())
- : LLDB_INVALID_ADDRESS;
+ ? sc.function->GetAddress().GetLoadAddress(target_sp.get())
+ : LLDB_INVALID_ADDRESS;
+ // Walk all in-scope variables and try to resolve a location
for (const VariableSP &var_sp : *var_list_sp) {
if (!var_sp)
continue;
- const char *name = var_sp->GetName().AsCString();
+ const auto var_id = var_sp->GetID(); // lldb::user_id_t – stable key
+ const char *name_cstr = var_sp->GetName().AsCString();
+ llvm::StringRef name = name_cstr ? name_cstr : "<anon>";
+
auto &expr_list = var_sp->LocationExpressionList();
if (!expr_list.IsValid())
continue;
- if (auto entryOrErr = expr_list.GetExpressionEntryAtAddress(func_load_addr, current_pc)) {
- auto entry = *entryOrErr;
+ // Try to get the expression entry for this PC
+ auto entry_or_err = expr_list.GetExpressionEntryAtAddress(func_load_addr, current_pc);
+ if (!entry_or_err)
+ continue;
- if (!entry.file_range ||
- entry.file_range->ContainsFileAddress(
- (current_pc - func_load_addr) + expr_list.GetFuncFileAddress())) {
+ auto entry = *entry_or_err;
- StreamString loc_str;
- ABI *abi = exe_ctx.GetProcessPtr()->GetABI().get();
- llvm::DIDumpOptions opts;
- opts.ShowAddresses = false;
- opts.PrintRegisterOnly = true;
+ // Check range if present
+ if (entry.file_range &&
+ !entry.file_range->ContainsFileAddress(
+ (current_pc - func_load_addr) + expr_list.GetFuncFileAddress()))
+ continue;
- entry.expr->DumpLocation(&loc_str, eDescriptionLevelBrief, abi, opts);
+ // Render a compact location string
+ ABI *abi = process_sp->GetABI().get();
+ llvm::DIDumpOptions opts;
+ opts.ShowAddresses = false;
+ opts.PrintRegisterOnly = true;
- llvm::StringRef loc_clean = llvm::StringRef(loc_str.GetString()).trim();
- if (!loc_clean.empty()) {
- annotations.push_back(llvm::formatv("{0} = {1}", name, loc_clean));
- }
+ StreamString loc_str;
+ entry.expr->DumpLocation(&loc_str, eDescriptionLevelBrief, abi, opts);
+ llvm::StringRef loc_clean = llvm::StringRef(loc_str.GetString()).trim();
+ if (loc_clean.empty())
+ continue;
+
+ // Update map + decide if we print
+ auto it = live_vars.find(var_id);
+ if (it == live_vars.end()) {
+ // New var → print
+ live_vars.emplace(var_id, VarState{std::string(name), loc_clean.str(), true});
+ events.push_back(llvm::formatv("{0} = {1}", name, loc_clean).str());
+ } else {
+ it->second.seen_this_inst = true;
+ if (it->second.last_loc != loc_clean) {
+ it->second.last_loc = loc_clean.str();
+ events.push_back(llvm::formatv("{0} = {1}", it->second.name, loc_clean).str());
}
}
}
+ // Anything previously live that we didn't see a location for at this inst is now <undef>
+ for (auto it = live_vars.begin(); it != live_vars.end(); ) {
+ if (!it->second.seen_this_inst) {
+ events.push_back(llvm::formatv("{0} = <undef>", it->second.name).str());
+ it = live_vars.erase(it);
+ } else {
+ ++it;
+ }
+ }
+
+ // Restore PC
frame->ChangePC(original_pc);
- return annotations;
+ return events;
};
-
previous_symbol = nullptr;
SourceLine previous_line;
for (size_t i = 0; i < num_instructions_found; ++i) {
@@ -626,7 +681,7 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
show_control_flow_kind, &exe_ctx, &sc, &prev_sc, nullptr,
address_text_size);
- if(enable_rich_annotations){
+ if (enable_rich_annotations){
std::vector<std::string> annotations = annotate_variables(*inst);
if (!annotations.empty()) {
const size_t annotation_column = 100;
>From b7848681c1d827b5068e002c95697116a140dd8e Mon Sep 17 00:00:00 2001
From: ultimateforce21 <abdullahmohammad155 at gmail.com>
Date: Sun, 10 Aug 2025 16:25:11 -0400
Subject: [PATCH 35/48] Formatting changes.
---
lldb/source/Core/Disassembler.cpp | 44 ++++++++++++++++++-------------
1 file changed, 25 insertions(+), 19 deletions(-)
diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp
index 315cd54c70e1d..8894ebe94264d 100644
--- a/lldb/source/Core/Disassembler.cpp
+++ b/lldb/source/Core/Disassembler.cpp
@@ -397,15 +397,16 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
// enhances source-level debugging.
struct VarState {
- std::string name; // display name
- std::string last_loc; // last printed location (empty means <undef>)
+ std::string name; // display name
+ std::string last_loc; // last printed location (empty means <undef>)
bool seen_this_inst = false;
};
// Track live variables across instructions (keyed by stable LLDB user_id_t)
std::unordered_map<lldb::user_id_t, VarState> live_vars;
- // Stateful annotator: updates live_vars and returns only what should be printed for THIS instruction.
+ // Stateful annotator: updates live_vars and returns only what should be
+ // printed for THIS instruction.
auto annotate_variables = [&](Instruction &inst) -> std::vector<std::string> {
std::vector<std::string> events;
@@ -420,16 +421,18 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
kv.second.seen_this_inst = false;
addr_t current_pc = inst.GetAddress().GetLoadAddress(target_sp.get());
- addr_t original_pc = frame->GetFrameCodeAddress().GetLoadAddress(target_sp.get());
+ addr_t original_pc =
+ frame->GetFrameCodeAddress().GetLoadAddress(target_sp.get());
- // We temporarily move the frame PC so variable locations resolve at this inst
+ // We temporarily move the frame PC so variable locations resolve at this
+ // inst
if (!frame->ChangePC(current_pc))
return events;
VariableListSP var_list_sp = frame->GetInScopeVariableList(true);
if (!var_list_sp) {
// No variables in scope: everything previously live becomes <undef>
- for (auto it = live_vars.begin(); it != live_vars.end(); ) {
+ for (auto it = live_vars.begin(); it != live_vars.end();) {
events.push_back(llvm::formatv("{0} = <undef>", it->second.name).str());
it = live_vars.erase(it);
}
@@ -438,16 +441,16 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
}
SymbolContext sc = frame->GetSymbolContext(eSymbolContextFunction);
- addr_t func_load_addr = sc.function
- ? sc.function->GetAddress().GetLoadAddress(target_sp.get())
- : LLDB_INVALID_ADDRESS;
+ addr_t func_load_addr =
+ sc.function ? sc.function->GetAddress().GetLoadAddress(target_sp.get())
+ : LLDB_INVALID_ADDRESS;
// Walk all in-scope variables and try to resolve a location
for (const VariableSP &var_sp : *var_list_sp) {
if (!var_sp)
continue;
- const auto var_id = var_sp->GetID(); // lldb::user_id_t – stable key
+ const auto var_id = var_sp->GetID(); // lldb::user_id_t – stable key
const char *name_cstr = var_sp->GetName().AsCString();
llvm::StringRef name = name_cstr ? name_cstr : "<anon>";
@@ -456,7 +459,8 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
continue;
// Try to get the expression entry for this PC
- auto entry_or_err = expr_list.GetExpressionEntryAtAddress(func_load_addr, current_pc);
+ auto entry_or_err =
+ expr_list.GetExpressionEntryAtAddress(func_load_addr, current_pc);
if (!entry_or_err)
continue;
@@ -484,19 +488,22 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
auto it = live_vars.find(var_id);
if (it == live_vars.end()) {
// New var → print
- live_vars.emplace(var_id, VarState{std::string(name), loc_clean.str(), true});
+ live_vars.emplace(var_id,
+ VarState{std::string(name), loc_clean.str(), true});
events.push_back(llvm::formatv("{0} = {1}", name, loc_clean).str());
} else {
it->second.seen_this_inst = true;
if (it->second.last_loc != loc_clean) {
it->second.last_loc = loc_clean.str();
- events.push_back(llvm::formatv("{0} = {1}", it->second.name, loc_clean).str());
+ events.push_back(
+ llvm::formatv("{0} = {1}", it->second.name, loc_clean).str());
}
}
}
- // Anything previously live that we didn't see a location for at this inst is now <undef>
- for (auto it = live_vars.begin(); it != live_vars.end(); ) {
+ // Anything previously live that we didn't see a location for at this inst
+ // is now <undef>
+ for (auto it = live_vars.begin(); it != live_vars.end();) {
if (!it->second.seen_this_inst) {
events.push_back(llvm::formatv("{0} = <undef>", it->second.name).str());
it = live_vars.erase(it);
@@ -678,10 +685,10 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
StreamString inst_line;
inst->Dump(&inst_line, max_opcode_byte_size, true, show_bytes,
- show_control_flow_kind, &exe_ctx, &sc, &prev_sc, nullptr,
- address_text_size);
+ show_control_flow_kind, &exe_ctx, &sc, &prev_sc, nullptr,
+ address_text_size);
- if (enable_rich_annotations){
+ if (enable_rich_annotations) {
std::vector<std::string> annotations = annotate_variables(*inst);
if (!annotations.empty()) {
const size_t annotation_column = 100;
@@ -694,7 +701,6 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
strm.PutCString(inst_line.GetString());
strm.EOL();
-
} else {
break;
}
>From cb0cd3abd5aa591d1062e865e6136a6ad23a7810 Mon Sep 17 00:00:00 2001
From: ultimateforce21 <abdullahmohammad155 at gmail.com>
Date: Mon, 11 Aug 2025 19:28:16 -0400
Subject: [PATCH 36/48] Moved rich annotations flag into Disassembler options
This change refactors how the `--rich` flag is handled, based on code review feedback.
- Removed the `enable_rich_annotations` boolean from the API signatures of:
- Disassembler::Disassemble(...)
- Disassembler::PrintInstructions(...)
- StackFrame::Disassemble(...)
- Added a new Disassembler::Option enum value: eOptionRichAnnotations.
- The `--rich` CLI flag now sets the new option bit in CommandObjectDisassemble::DoExecute:
options |= Disassembler::eOptionRichAnnotations;
- Disassembler::PrintInstructions checks the bit to determine whether to enable rich annotations:
const bool enable_rich = (options & eOptionRichAnnotations) != 0;
The SB API remains unchanged and defaults to non-rich output.
Tested via the existing test using `disassemble --rich -f`.
---
lldb/include/lldb/Core/Disassembler.h | 8 ++---
lldb/include/lldb/Target/StackFrame.h | 2 +-
.../Commands/CommandObjectDisassemble.cpp | 6 ++--
lldb/source/Core/Disassembler.cpp | 29 +++++++++----------
lldb/source/Target/StackFrame.cpp | 14 ++++-----
5 files changed, 28 insertions(+), 31 deletions(-)
diff --git a/lldb/include/lldb/Core/Disassembler.h b/lldb/include/lldb/Core/Disassembler.h
index fc83e747bb2b7..00a04678e3202 100644
--- a/lldb/include/lldb/Core/Disassembler.h
+++ b/lldb/include/lldb/Core/Disassembler.h
@@ -398,6 +398,7 @@ class Disassembler : public std::enable_shared_from_this<Disassembler>,
eOptionMarkPCAddress =
(1u << 3), // Mark the disassembly line the contains the PC
eOptionShowControlFlowKind = (1u << 4),
+ eOptionRichAnnotations = (1u << 5),
};
enum HexImmediateStyle {
@@ -444,11 +445,10 @@ class Disassembler : public std::enable_shared_from_this<Disassembler>,
const ExecutionContext &exe_ctx, const Address &start,
Limit limit, bool mixed_source_and_assembly,
uint32_t num_mixed_context_lines, uint32_t options,
- Stream &strm, bool enable_rich_annotations = false);
+ Stream &strm);
static bool Disassemble(Debugger &debugger, const ArchSpec &arch,
- StackFrame &frame, Stream &strm,
- bool enable_rich_annotations = false);
+ StackFrame &frame, Stream &strm);
// Constructors and Destructors
Disassembler(const ArchSpec &arch, const char *flavor);
@@ -458,7 +458,7 @@ class Disassembler : public std::enable_shared_from_this<Disassembler>,
const ExecutionContext &exe_ctx,
bool mixed_source_and_assembly,
uint32_t num_mixed_context_lines, uint32_t options,
- Stream &strm, bool enable_rich_annotations = false);
+ Stream &strm);
size_t ParseInstructions(Target &target, Address address, Limit limit,
Stream *error_strm_ptr,
diff --git a/lldb/include/lldb/Target/StackFrame.h b/lldb/include/lldb/Target/StackFrame.h
index a970743f0fa5b..3f51c9a7f22f0 100644
--- a/lldb/include/lldb/Target/StackFrame.h
+++ b/lldb/include/lldb/Target/StackFrame.h
@@ -321,7 +321,7 @@ class StackFrame : public ExecutionContextScope,
///
/// \return
/// C string with the assembly instructions for this function.
- const char *Disassemble(bool enable_rich_annotations = false);
+ const char *Disassemble();
/// Print a description of this frame using the provided frame format.
///
diff --git a/lldb/source/Commands/CommandObjectDisassemble.cpp b/lldb/source/Commands/CommandObjectDisassemble.cpp
index 5b7249be80a3e..ebd77123f60f9 100644
--- a/lldb/source/Commands/CommandObjectDisassemble.cpp
+++ b/lldb/source/Commands/CommandObjectDisassemble.cpp
@@ -533,6 +533,9 @@ void CommandObjectDisassemble::DoExecute(Args &command,
if (m_options.raw)
options |= Disassembler::eOptionRawOuput;
+ if (m_options.enable_rich_annotations)
+ options |= Disassembler::eOptionRichAnnotations;
+
llvm::Expected<std::vector<AddressRange>> ranges =
GetRangesForSelectedMode(result);
if (!ranges) {
@@ -555,8 +558,7 @@ void CommandObjectDisassemble::DoExecute(Args &command,
cpu_string, features_string, m_exe_ctx, cur_range.GetBaseAddress(),
limit, m_options.show_mixed,
m_options.show_mixed ? m_options.num_lines_context : 0, options,
- result.GetOutputStream(),
- /*enable_rich_annotations=*/m_options.enable_rich_annotations)) {
+ result.GetOutputStream())) {
result.SetStatus(eReturnStatusSuccessFinishResult);
} else {
if (m_options.symbol_containing_addr != LLDB_INVALID_ADDRESS) {
diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp
index 8894ebe94264d..70b0ccc2ffc7c 100644
--- a/lldb/source/Core/Disassembler.cpp
+++ b/lldb/source/Core/Disassembler.cpp
@@ -169,12 +169,14 @@ Disassembler::DisassembleBytes(const ArchSpec &arch, const char *plugin_name,
return disasm_sp;
}
-bool Disassembler::Disassemble(
- Debugger &debugger, const ArchSpec &arch, const char *plugin_name,
- const char *flavor, const char *cpu, const char *features,
- const ExecutionContext &exe_ctx, const Address &address, Limit limit,
- bool mixed_source_and_assembly, uint32_t num_mixed_context_lines,
- uint32_t options, Stream &strm, bool enable_rich_annotations) {
+bool Disassembler::Disassemble(Debugger &debugger, const ArchSpec &arch,
+ const char *plugin_name, const char *flavor,
+ const char *cpu, const char *features,
+ const ExecutionContext &exe_ctx,
+ const Address &address, Limit limit,
+ bool mixed_source_and_assembly,
+ uint32_t num_mixed_context_lines,
+ uint32_t options, Stream &strm) {
if (!exe_ctx.GetTargetPtr())
return false;
@@ -189,10 +191,9 @@ bool Disassembler::Disassemble(
if (bytes_disassembled == 0)
return false;
- disasm_sp->PrintInstructions(
- debugger, arch, exe_ctx, mixed_source_and_assembly,
- num_mixed_context_lines, options, strm,
- /*enable_rich_annotations=*/enable_rich_annotations);
+ disasm_sp->PrintInstructions(debugger, arch, exe_ctx,
+ mixed_source_and_assembly,
+ num_mixed_context_lines, options, strm);
return true;
}
@@ -287,8 +288,7 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
const ExecutionContext &exe_ctx,
bool mixed_source_and_assembly,
uint32_t num_mixed_context_lines,
- uint32_t options, Stream &strm,
- bool enable_rich_annotations) {
+ uint32_t options, Stream &strm) {
// We got some things disassembled...
size_t num_instructions_found = GetInstructionList().GetSize();
@@ -688,7 +688,7 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
show_control_flow_kind, &exe_ctx, &sc, &prev_sc, nullptr,
address_text_size);
- if (enable_rich_annotations) {
+ if (options & eOptionRichAnnotations) {
std::vector<std::string> annotations = annotate_variables(*inst);
if (!annotations.empty()) {
const size_t annotation_column = 100;
@@ -708,8 +708,7 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
}
bool Disassembler::Disassemble(Debugger &debugger, const ArchSpec &arch,
- StackFrame &frame, Stream &strm,
- bool enable_rich_annotations) {
+ StackFrame &frame, Stream &strm) {
constexpr const char *plugin_name = nullptr;
constexpr const char *flavor = nullptr;
constexpr const char *cpu = nullptr;
diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp
index 0aa93e635b866..c84e99c17c72e 100644
--- a/lldb/source/Target/StackFrame.cpp
+++ b/lldb/source/Target/StackFrame.cpp
@@ -262,20 +262,16 @@ bool StackFrame::ChangePC(addr_t pc) {
return true;
}
-const char *StackFrame::Disassemble(bool enable_rich_annotations) {
+const char *StackFrame::Disassemble() {
std::lock_guard<std::recursive_mutex> guard(m_mutex);
- // Keep the existing cache only for the plain (non-rich) path.
- if (!enable_rich_annotations) {
- if (!m_disassembly.Empty())
- return m_disassembly.GetData();
- }
+ if (!m_disassembly.Empty())
+ return m_disassembly.GetData();
ExecutionContext exe_ctx(shared_from_this());
if (Target *target = exe_ctx.GetTargetPtr()) {
- Disassembler::Disassemble(
- target->GetDebugger(), target->GetArchitecture(), *this, m_disassembly,
- /*enable_rich_annotations=*/enable_rich_annotations);
+ Disassembler::Disassemble(target->GetDebugger(), target->GetArchitecture(),
+ *this, m_disassembly);
}
return m_disassembly.Empty() ? nullptr : m_disassembly.GetData();
>From fbd4e65fad4f40733ba43a461f7f1f7af9c0ffe9 Mon Sep 17 00:00:00 2001
From: ultimateforce21 <abdullahmohammad155 at gmail.com>
Date: Mon, 11 Aug 2025 19:53:47 -0400
Subject: [PATCH 37/48] Switched to llvm::SmallDenseMap for live_vars in
PrintInstructions
Address code review feedback suggesting the use of LLVM's DenseMap family
over std::unordered_map for consistency and potential performance benefits
within LLDB.
Replaced:
std::unordered_map<lldb::user_id_t, VarState>
with:
llvm::SmallDenseMap<lldb::user_id_t, VarState, 8>
The small buffer size of 8 is a heuristic for typical numbers of live
variables in scope, reducing allocations for common cases.
---
lldb/source/Core/Disassembler.cpp | 46 ++++++++++++++++---------------
1 file changed, 24 insertions(+), 22 deletions(-)
diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp
index 70b0ccc2ffc7c..4e3cd0668efaf 100644
--- a/lldb/source/Core/Disassembler.cpp
+++ b/lldb/source/Core/Disassembler.cpp
@@ -44,12 +44,13 @@
#include "lldb/lldb-private-enumerations.h"
#include "lldb/lldb-private-interfaces.h"
#include "lldb/lldb-private-types.h"
+#include "llvm/ADT/DenseMap.h"
#include "llvm/Support/Compiler.h"
#include "llvm/TargetParser/Triple.h"
+
#include <cstdint>
#include <cstring>
-#include <unordered_map>
#include <utility>
#include <cassert>
@@ -403,7 +404,7 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
};
// Track live variables across instructions (keyed by stable LLDB user_id_t)
- std::unordered_map<lldb::user_id_t, VarState> live_vars;
+ llvm::SmallDenseMap<lldb::user_id_t, VarState, 8> live_vars; // 8 is a good small-buffer guess
// Stateful annotator: updates live_vars and returns only what should be
// printed for THIS instruction.
@@ -432,9 +433,10 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
VariableListSP var_list_sp = frame->GetInScopeVariableList(true);
if (!var_list_sp) {
// No variables in scope: everything previously live becomes <undef>
- for (auto it = live_vars.begin(); it != live_vars.end();) {
- events.push_back(llvm::formatv("{0} = <undef>", it->second.name).str());
- it = live_vars.erase(it);
+ for (auto I = live_vars.begin(), E = live_vars.end(); I != E; ) {
+ auto Cur = I++;
+ events.push_back(llvm::formatv("{0} = <undef>", Cur->second.name).str());
+ live_vars.erase(Cur);
}
frame->ChangePC(original_pc);
return events;
@@ -484,31 +486,31 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
if (loc_clean.empty())
continue;
- // Update map + decide if we print
- auto it = live_vars.find(var_id);
- if (it == live_vars.end()) {
- // New var → print
- live_vars.emplace(var_id,
- VarState{std::string(name), loc_clean.str(), true});
+ auto insert_res = live_vars.insert({var_id,
+ VarState{std::string(name),
+ loc_clean.str(),
+ /*seen_this_inst*/ true}});
+ if (insert_res.second) {
+ // Newly inserted → print
events.push_back(llvm::formatv("{0} = {1}", name, loc_clean).str());
} else {
- it->second.seen_this_inst = true;
- if (it->second.last_loc != loc_clean) {
- it->second.last_loc = loc_clean.str();
- events.push_back(
- llvm::formatv("{0} = {1}", it->second.name, loc_clean).str());
+ // Already present
+ VarState &vs = insert_res.first->second;
+ vs.seen_this_inst = true;
+ if (vs.last_loc != loc_clean) {
+ vs.last_loc = loc_clean.str();
+ events.push_back(llvm::formatv("{0} = {1}", vs.name, loc_clean).str());
}
}
}
// Anything previously live that we didn't see a location for at this inst
// is now <undef>
- for (auto it = live_vars.begin(); it != live_vars.end();) {
- if (!it->second.seen_this_inst) {
- events.push_back(llvm::formatv("{0} = <undef>", it->second.name).str());
- it = live_vars.erase(it);
- } else {
- ++it;
+ for (auto I = live_vars.begin(), E = live_vars.end(); I != E; ) {
+ auto Cur = I++;
+ if (!Cur->second.seen_this_inst) {
+ events.push_back(llvm::formatv("{0} = <undef>", Cur->second.name).str());
+ live_vars.erase(Cur);
}
}
>From 77fa1ed32148ff474719af9dd37109e4df23b115 Mon Sep 17 00:00:00 2001
From: ultimateforce21 <abdullahmohammad155 at gmail.com>
Date: Mon, 11 Aug 2025 20:11:49 -0400
Subject: [PATCH 38/48] Fixed code style to match LLVM convention
---
.../Commands/CommandObjectDisassemble.cpp | 2 +-
lldb/source/Core/Disassembler.cpp | 32 +++++++++----------
.../rich-disassembler/Makefile | 7 +---
3 files changed, 18 insertions(+), 23 deletions(-)
diff --git a/lldb/source/Commands/CommandObjectDisassemble.cpp b/lldb/source/Commands/CommandObjectDisassemble.cpp
index ebd77123f60f9..35315dc144669 100644
--- a/lldb/source/Commands/CommandObjectDisassemble.cpp
+++ b/lldb/source/Commands/CommandObjectDisassemble.cpp
@@ -154,7 +154,7 @@ Status CommandObjectDisassemble::CommandOptions::SetOptionValue(
}
} break;
- case 'R': // --rich
+ case 'R': //< --rich
enable_rich_annotations = true;
break;
diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp
index 4e3cd0668efaf..64c52913574a3 100644
--- a/lldb/source/Core/Disassembler.cpp
+++ b/lldb/source/Core/Disassembler.cpp
@@ -398,13 +398,13 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
// enhances source-level debugging.
struct VarState {
- std::string name; // display name
- std::string last_loc; // last printed location (empty means <undef>)
+ std::string name; //< Display name.
+ std::string last_loc; //< Last printed location (empty means <undef>).
bool seen_this_inst = false;
};
- // Track live variables across instructions (keyed by stable LLDB user_id_t)
- llvm::SmallDenseMap<lldb::user_id_t, VarState, 8> live_vars; // 8 is a good small-buffer guess
+ // Track live variables across instructions (keyed by stable LLDB user_id_t. 8 is a good small-buffer guess
+ llvm::SmallDenseMap<lldb::user_id_t, VarState, 8> live_vars;
// Stateful annotator: updates live_vars and returns only what should be
// printed for THIS instruction.
@@ -417,7 +417,7 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
if (!frame || !target_sp || !process_sp)
return events;
- // Reset "seen" flags for this instruction
+ // Reset "seen" flags for this instruction.
for (auto &kv : live_vars)
kv.second.seen_this_inst = false;
@@ -426,13 +426,13 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
frame->GetFrameCodeAddress().GetLoadAddress(target_sp.get());
// We temporarily move the frame PC so variable locations resolve at this
- // inst
+ // instruction.
if (!frame->ChangePC(current_pc))
return events;
VariableListSP var_list_sp = frame->GetInScopeVariableList(true);
if (!var_list_sp) {
- // No variables in scope: everything previously live becomes <undef>
+ // No variables in scope: everything previously live becomes <undef>.
for (auto I = live_vars.begin(), E = live_vars.end(); I != E; ) {
auto Cur = I++;
events.push_back(llvm::formatv("{0} = <undef>", Cur->second.name).str());
@@ -447,12 +447,13 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
sc.function ? sc.function->GetAddress().GetLoadAddress(target_sp.get())
: LLDB_INVALID_ADDRESS;
- // Walk all in-scope variables and try to resolve a location
+ // Walk all in-scope variables and try to resolve a location.
for (const VariableSP &var_sp : *var_list_sp) {
if (!var_sp)
continue;
- const auto var_id = var_sp->GetID(); // lldb::user_id_t – stable key
+ // The var_id is a lldb::user_id_t – stable key.
+ const auto var_id = var_sp->GetID();
const char *name_cstr = var_sp->GetName().AsCString();
llvm::StringRef name = name_cstr ? name_cstr : "<anon>";
@@ -460,7 +461,6 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
if (!expr_list.IsValid())
continue;
- // Try to get the expression entry for this PC
auto entry_or_err =
expr_list.GetExpressionEntryAtAddress(func_load_addr, current_pc);
if (!entry_or_err)
@@ -468,13 +468,13 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
auto entry = *entry_or_err;
- // Check range if present
+ // Check range if present.
if (entry.file_range &&
!entry.file_range->ContainsFileAddress(
(current_pc - func_load_addr) + expr_list.GetFuncFileAddress()))
continue;
- // Render a compact location string
+ // Render a compact location string.
ABI *abi = process_sp->GetABI().get();
llvm::DIDumpOptions opts;
opts.ShowAddresses = false;
@@ -491,10 +491,10 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
loc_clean.str(),
/*seen_this_inst*/ true}});
if (insert_res.second) {
- // Newly inserted → print
+ // Newly inserted → print.
events.push_back(llvm::formatv("{0} = {1}", name, loc_clean).str());
} else {
- // Already present
+ // Already present.
VarState &vs = insert_res.first->second;
vs.seen_this_inst = true;
if (vs.last_loc != loc_clean) {
@@ -505,7 +505,7 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
}
// Anything previously live that we didn't see a location for at this inst
- // is now <undef>
+ // is now <undef>.
for (auto I = live_vars.begin(), E = live_vars.end(); I != E; ) {
auto Cur = I++;
if (!Cur->second.seen_this_inst) {
@@ -514,7 +514,7 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
}
}
- // Restore PC
+ // Restore PC.
frame->ChangePC(original_pc);
return events;
};
diff --git a/lldb/test/API/functionalities/rich-disassembler/Makefile b/lldb/test/API/functionalities/rich-disassembler/Makefile
index ae3330e632a0e..be53a34f7e265 100644
--- a/lldb/test/API/functionalities/rich-disassembler/Makefile
+++ b/lldb/test/API/functionalities/rich-disassembler/Makefile
@@ -1,6 +1 @@
-
-# CXX_SOURCES := a_loop_with_local_variable.c b_multiple_stack_variables.c c_variable_passed_to_another_function.c d_original_example.c e_control_flow_edge.c
-C_SOURCES := a_loop_with_local_variable.c b_multiple_stack_variables.c c_variable_passed_to_another_function.c d_original_example.c e_control_flow_edge.c
-
-
-include Makefile.rules
+include Makefile.rules
\ No newline at end of file
>From 7069b6a8ae67aa0e22d10c1a2433269e3a399dc1 Mon Sep 17 00:00:00 2001
From: ultimateforce21 <abdullahmohammad155 at gmail.com>
Date: Mon, 11 Aug 2025 20:23:34 -0400
Subject: [PATCH 39/48] Formatting changes.
---
lldb/source/Core/Disassembler.cpp | 22 ++++++++++++----------
1 file changed, 12 insertions(+), 10 deletions(-)
diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp
index 64c52913574a3..08190d05db3c6 100644
--- a/lldb/source/Core/Disassembler.cpp
+++ b/lldb/source/Core/Disassembler.cpp
@@ -48,7 +48,6 @@
#include "llvm/Support/Compiler.h"
#include "llvm/TargetParser/Triple.h"
-
#include <cstdint>
#include <cstring>
#include <utility>
@@ -403,7 +402,8 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
bool seen_this_inst = false;
};
- // Track live variables across instructions (keyed by stable LLDB user_id_t. 8 is a good small-buffer guess
+ // Track live variables across instructions (keyed by stable LLDB user_id_t. 8
+ // is a good small-buffer guess.
llvm::SmallDenseMap<lldb::user_id_t, VarState, 8> live_vars;
// Stateful annotator: updates live_vars and returns only what should be
@@ -433,9 +433,10 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
VariableListSP var_list_sp = frame->GetInScopeVariableList(true);
if (!var_list_sp) {
// No variables in scope: everything previously live becomes <undef>.
- for (auto I = live_vars.begin(), E = live_vars.end(); I != E; ) {
+ for (auto I = live_vars.begin(), E = live_vars.end(); I != E;) {
auto Cur = I++;
- events.push_back(llvm::formatv("{0} = <undef>", Cur->second.name).str());
+ events.push_back(
+ llvm::formatv("{0} = <undef>", Cur->second.name).str());
live_vars.erase(Cur);
}
frame->ChangePC(original_pc);
@@ -486,9 +487,8 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
if (loc_clean.empty())
continue;
- auto insert_res = live_vars.insert({var_id,
- VarState{std::string(name),
- loc_clean.str(),
+ auto insert_res =
+ live_vars.insert({var_id, VarState{std::string(name), loc_clean.str(),
/*seen_this_inst*/ true}});
if (insert_res.second) {
// Newly inserted → print.
@@ -499,17 +499,19 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
vs.seen_this_inst = true;
if (vs.last_loc != loc_clean) {
vs.last_loc = loc_clean.str();
- events.push_back(llvm::formatv("{0} = {1}", vs.name, loc_clean).str());
+ events.push_back(
+ llvm::formatv("{0} = {1}", vs.name, loc_clean).str());
}
}
}
// Anything previously live that we didn't see a location for at this inst
// is now <undef>.
- for (auto I = live_vars.begin(), E = live_vars.end(); I != E; ) {
+ for (auto I = live_vars.begin(), E = live_vars.end(); I != E;) {
auto Cur = I++;
if (!Cur->second.seen_this_inst) {
- events.push_back(llvm::formatv("{0} = <undef>", Cur->second.name).str());
+ events.push_back(
+ llvm::formatv("{0} = <undef>", Cur->second.name).str());
live_vars.erase(Cur);
}
}
>From 212a401ebe3f5310a8f90d04613480b14b5c3e4c Mon Sep 17 00:00:00 2001
From: ultimateforce21 <abdullahmohammad155 at gmail.com>
Date: Fri, 15 Aug 2025 09:05:40 -0400
Subject: [PATCH 40/48] Switched rich annotations CLI flag from -R (--rich) to
-v (--variable)
---
lldb/source/Commands/CommandObjectDisassemble.cpp | 2 +-
lldb/source/Commands/Options.td | 2 +-
.../functionalities/rich-disassembler/TestRichDisassembler.py | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/lldb/source/Commands/CommandObjectDisassemble.cpp b/lldb/source/Commands/CommandObjectDisassemble.cpp
index 35315dc144669..b067525274804 100644
--- a/lldb/source/Commands/CommandObjectDisassemble.cpp
+++ b/lldb/source/Commands/CommandObjectDisassemble.cpp
@@ -154,7 +154,7 @@ Status CommandObjectDisassemble::CommandOptions::SetOptionValue(
}
} break;
- case 'R': //< --rich
+ case 'v': //< --rich variable annotations
enable_rich_annotations = true;
break;
diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td
index 15e03a2a3056b..c0718daab7d57 100644
--- a/lldb/source/Commands/Options.td
+++ b/lldb/source/Commands/Options.td
@@ -361,7 +361,7 @@ let Command = "disassemble" in {
Desc<"Disassemble function containing this address.">;
def disassemble_options_force : Option<"force", "\\x01">, Groups<[2,3,4,5,7]>,
Desc<"Force disassembly of large functions.">;
- def disassemble_options_rich : Option<"rich", "R">,
+ def disassemble_options_rich : Option<"variable", "v">,
Desc<"Enable rich disassembly annotations for this invocation.">;
}
diff --git a/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py b/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py
index 4bad4a4f4986b..975261877e96c 100644
--- a/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py
+++ b/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py
@@ -24,7 +24,7 @@ def test_d_original_example_O1(self):
self.assertEqual(process.GetState(), lldb.eStateStopped)
# Run the CLI command and read output from self.res
- self.runCmd("disassemble --rich -f", check=True)
+ self.runCmd("disassemble --variable -f", check=True)
out = self.res.GetOutput()
print(out)
>From d234d3e9e1f3cca5f4c0b7a655b2fc7d4d8b8e60 Mon Sep 17 00:00:00 2001
From: Abdullah Mohammad Amin
<67847674+UltimateForce21 at users.noreply.github.com>
Date: Sat, 16 Aug 2025 18:17:16 -0400
Subject: [PATCH 41/48] Update lldb/source/Core/Disassembler.cpp
Co-authored-by: Adrian Prantl <adrian.prantl at gmail.com>
---
lldb/source/Core/Disassembler.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp
index 08190d05db3c6..de0acba5de7c1 100644
--- a/lldb/source/Core/Disassembler.cpp
+++ b/lldb/source/Core/Disassembler.cpp
@@ -397,7 +397,7 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
// enhances source-level debugging.
struct VarState {
- std::string name; //< Display name.
+ std::string name; ///< Display name.
std::string last_loc; //< Last printed location (empty means <undef>).
bool seen_this_inst = false;
};
>From e28278228aa0dcc1f45b6b3e18402e09ef325f70 Mon Sep 17 00:00:00 2001
From: ultimateforce21 <abdullahmohammad155 at gmail.com>
Date: Sun, 17 Aug 2025 23:47:11 -0400
Subject: [PATCH 42/48] Make variable annotations work offline
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Rework the variable-location annotation logic to operate purely on
file addresses and DWARF data so it doesn’t require a Process/StackFrame.
This enables annotations when disassembling static objects (or in crash/core
scenarios) and sets the stage for portable tests built from yaml2obj.
Key changes:
- Resolve context at the instruction’s *file* address and use
eSymbolContextFunction | eSymbolContextBlock. Use sc.block (innermost)
and Block::AppendVariables(can_create=true, get_parent_variables=true,
stop_if_block_is_inlined_function=false, filter, &list) to collect
in-scope, non-artificial variables (via a filter callback).
-- Pretty-print register names through an ABI derived from the Target only:
ABI::FindPlugin(ProcessSP(), ArchSpec). Handle absence gracefully; fall
back to DWARF reg ops.
- Keep a small live map keyed by Variable user_id_t and emit only
transitions:
- “name = REG” on start/change
- “name = <undef>” when a location disappears
---
lldb/source/Core/Disassembler.cpp | 150 +++++++++++++++---------------
1 file changed, 76 insertions(+), 74 deletions(-)
diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp
index de0acba5de7c1..3be2379919e3a 100644
--- a/lldb/source/Core/Disassembler.cpp
+++ b/lldb/source/Core/Disassembler.cpp
@@ -28,6 +28,7 @@
#include "lldb/Symbol/SymbolContext.h"
#include "lldb/Symbol/Variable.h"
#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/ABI.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/SectionLoadList.h"
@@ -45,6 +46,7 @@
#include "lldb/lldb-private-interfaces.h"
#include "lldb/lldb-private-types.h"
#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Compiler.h"
#include "llvm/TargetParser/Triple.h"
@@ -408,116 +410,116 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
// Stateful annotator: updates live_vars and returns only what should be
// printed for THIS instruction.
- auto annotate_variables = [&](Instruction &inst) -> std::vector<std::string> {
- std::vector<std::string> events;
+ auto annotate_static =
+ [&](Instruction &inst, Target &target, ModuleSP module_sp)
+ -> std::vector<std::string> {
- StackFrame *frame = exe_ctx.GetFramePtr();
- TargetSP target_sp = exe_ctx.GetTargetSP();
- ProcessSP process_sp = exe_ctx.GetProcessSP();
- if (!frame || !target_sp || !process_sp)
- return events;
-
- // Reset "seen" flags for this instruction.
- for (auto &kv : live_vars)
- kv.second.seen_this_inst = false;
+ std::vector<std::string> events;
- addr_t current_pc = inst.GetAddress().GetLoadAddress(target_sp.get());
- addr_t original_pc =
- frame->GetFrameCodeAddress().GetLoadAddress(target_sp.get());
+ // Reset per-instruction seen flags.
+ for (auto &kv : live_vars) kv.second.seen_this_inst = false;
- // We temporarily move the frame PC so variable locations resolve at this
- // instruction.
- if (!frame->ChangePC(current_pc))
+ const Address &iaddr = inst.GetAddress();
+ if (!module_sp) {
+ // Everything previously live becomes <undef>.
+ for (auto I = live_vars.begin(), E = live_vars.end(); I != E; ) {
+ auto Cur = I++;
+ events.push_back(llvm::formatv("{0} = <undef>", Cur->second.name).str());
+ live_vars.erase(Cur);
+ }
return events;
+ }
- VariableListSP var_list_sp = frame->GetInScopeVariableList(true);
- if (!var_list_sp) {
- // No variables in scope: everything previously live becomes <undef>.
- for (auto I = live_vars.begin(), E = live_vars.end(); I != E;) {
+ // Resolve innermost block at this *file* address.
+ SymbolContext sc;
+ const lldb::SymbolContextItem mask =
+ eSymbolContextFunction | eSymbolContextBlock;
+ if (!module_sp->ResolveSymbolContextForAddress(iaddr, mask, sc) || !sc.function) {
+ // No function context: everything dies here.
+ for (auto I = live_vars.begin(), E = live_vars.end(); I != E; ) {
auto Cur = I++;
- events.push_back(
- llvm::formatv("{0} = <undef>", Cur->second.name).str());
+ events.push_back(llvm::formatv("{0} = <undef>", Cur->second.name).str());
live_vars.erase(Cur);
}
- frame->ChangePC(original_pc);
return events;
}
- SymbolContext sc = frame->GetSymbolContext(eSymbolContextFunction);
- addr_t func_load_addr =
- sc.function ? sc.function->GetAddress().GetLoadAddress(target_sp.get())
- : LLDB_INVALID_ADDRESS;
+ Block *B = sc.block; ///< Innermost block containing iaddr.
+ VariableList var_list;
+ if (B) {
+ auto filter = [](Variable *v) -> bool {
+ return v && !v->IsArtificial();
+ };
+
+ B->AppendVariables(/*can_create*/ true,
+ /*get_parent_variables*/ true,
+ /*stop_if_block_is_inlined_function*/ false,
+ /*filter*/ filter,
+ /*variable_list*/ &var_list);
+ }
+
+ const lldb::addr_t pc_file = iaddr.GetFileAddress();
+ const lldb::addr_t func_file = sc.function->GetAddress().GetFileAddress();
+
+ // ABI from Target (pretty reg names if plugin exists). Safe to be null.
+ lldb::ProcessSP no_process;
+ lldb::ABISP abi_sp = ABI::FindPlugin(no_process, target.GetArchitecture());
+ ABI *abi = abi_sp.get();
+
+ llvm::DIDumpOptions opts;
+ opts.ShowAddresses = false;
+ if (abi)
+ opts.PrintRegisterOnly = true;
- // Walk all in-scope variables and try to resolve a location.
- for (const VariableSP &var_sp : *var_list_sp) {
- if (!var_sp)
+ for (size_t i = 0, e = var_list.GetSize(); i != e; ++i) {
+ lldb::VariableSP v = var_list.GetVariableAtIndex(i);
+ if (!v || v->IsArtificial())
continue;
- // The var_id is a lldb::user_id_t – stable key.
- const auto var_id = var_sp->GetID();
- const char *name_cstr = var_sp->GetName().AsCString();
- llvm::StringRef name = name_cstr ? name_cstr : "<anon>";
+ const char *nm = v->GetName().AsCString();
+ llvm::StringRef name = nm ? nm : "<anon>";
- auto &expr_list = var_sp->LocationExpressionList();
- if (!expr_list.IsValid())
+ lldb_private::DWARFExpressionList &exprs = v->LocationExpressionList();
+ if (!exprs.IsValid())
continue;
- auto entry_or_err =
- expr_list.GetExpressionEntryAtAddress(func_load_addr, current_pc);
+ auto entry_or_err = exprs.GetExpressionEntryAtAddress(func_file, pc_file);
if (!entry_or_err)
continue;
auto entry = *entry_or_err;
- // Check range if present.
- if (entry.file_range &&
- !entry.file_range->ContainsFileAddress(
- (current_pc - func_load_addr) + expr_list.GetFuncFileAddress()))
- continue;
-
- // Render a compact location string.
- ABI *abi = process_sp->GetABI().get();
- llvm::DIDumpOptions opts;
- opts.ShowAddresses = false;
- opts.PrintRegisterOnly = true;
-
- StreamString loc_str;
- entry.expr->DumpLocation(&loc_str, eDescriptionLevelBrief, abi, opts);
- llvm::StringRef loc_clean = llvm::StringRef(loc_str.GetString()).trim();
- if (loc_clean.empty())
+ StreamString loc_ss;
+ entry.expr->DumpLocation(&loc_ss, eDescriptionLevelBrief, abi, opts);
+ llvm::StringRef loc = llvm::StringRef(loc_ss.GetString()).trim();
+ if (loc.empty())
continue;
- auto insert_res =
- live_vars.insert({var_id, VarState{std::string(name), loc_clean.str(),
- /*seen_this_inst*/ true}});
- if (insert_res.second) {
- // Newly inserted → print.
- events.push_back(llvm::formatv("{0} = {1}", name, loc_clean).str());
+ auto ins = live_vars.insert({v->GetID(),
+ VarState{name.str(), loc.str(), /*seen*/ true}});
+ if (ins.second) {
+ // Newly live.
+ events.push_back(llvm::formatv("{0} = {1}", name, loc).str());
} else {
- // Already present.
- VarState &vs = insert_res.first->second;
+ VarState &vs = ins.first->second;
vs.seen_this_inst = true;
- if (vs.last_loc != loc_clean) {
- vs.last_loc = loc_clean.str();
- events.push_back(
- llvm::formatv("{0} = {1}", vs.name, loc_clean).str());
+ if (vs.last_loc != loc) {
+ vs.last_loc = loc.str();
+ events.push_back(llvm::formatv("{0} = {1}", vs.name, loc).str());
}
}
}
// Anything previously live that we didn't see a location for at this inst
// is now <undef>.
- for (auto I = live_vars.begin(), E = live_vars.end(); I != E;) {
+ for (auto I = live_vars.begin(), E = live_vars.end(); I != E; ) {
auto Cur = I++;
if (!Cur->second.seen_this_inst) {
- events.push_back(
- llvm::formatv("{0} = <undef>", Cur->second.name).str());
+ events.push_back(llvm::formatv("{0} = <undef>", Cur->second.name).str());
live_vars.erase(Cur);
}
}
- // Restore PC.
- frame->ChangePC(original_pc);
return events;
};
@@ -692,8 +694,8 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
show_control_flow_kind, &exe_ctx, &sc, &prev_sc, nullptr,
address_text_size);
- if (options & eOptionRichAnnotations) {
- std::vector<std::string> annotations = annotate_variables(*inst);
+ if ((options & eOptionRichAnnotations) && target_sp) {
+ auto annotations = annotate_static(*inst, *target_sp, module_sp);
if (!annotations.empty()) {
const size_t annotation_column = 100;
inst_line.FillLastLineToColumn(annotation_column, ' ');
>From 0b71f82dd09763d1d6df3e765afd1e333be69b45 Mon Sep 17 00:00:00 2001
From: ultimateforce21 <abdullahmohammad155 at gmail.com>
Date: Tue, 19 Aug 2025 23:48:54 -0400
Subject: [PATCH 43/48] Added portable disassembler variable tests across
register cases
This patch extends `TestRichDisassembler.py` (in lldb/test/API/functionalities/disassembler-variables) with a suite of new
object-level test cases, complementing the existing
`d_original_example.c` test. Each case exercises different calling
conventions and register lifetimes, ensuring disassembler annotations
are validated in a portable way.
- Keep original style test for `d_original_example.c` (full exe, stop in main).
---
.../Makefile | 0
.../TestRichDisassembler.py | 156 ++++++++++++++++++
.../disassembler-variables/d_original_example | Bin 0 -> 17264 bytes
.../d_original_example.c | 0
.../disassembler-variables/live_across_call.c | 11 ++
.../disassembler-variables/live_across_call.o | Bin 0 -> 3488 bytes
.../disassembler-variables/loop_reg_rotate.c | 18 ++
.../disassembler-variables/loop_reg_rotate.o | Bin 0 -> 3680 bytes
.../disassembler-variables/regs_fp_params.c | 5 +
.../disassembler-variables/regs_fp_params.o | Bin 0 -> 3304 bytes
.../disassembler-variables/regs_int_params.c | 7 +
.../disassembler-variables/regs_int_params.o | Bin 0 -> 3296 bytes
.../regs_mixed_params.c | 9 +
.../regs_mixed_params.o | Bin 0 -> 3440 bytes
.../seed_reg_const_undef.c | 9 +
.../rich-disassembler/TestRichDisassembler.py | 34 ----
16 files changed, 215 insertions(+), 34 deletions(-)
rename lldb/test/API/functionalities/{rich-disassembler => disassembler-variables}/Makefile (100%)
create mode 100644 lldb/test/API/functionalities/disassembler-variables/TestRichDisassembler.py
create mode 100755 lldb/test/API/functionalities/disassembler-variables/d_original_example
rename lldb/test/API/functionalities/{rich-disassembler => disassembler-variables}/d_original_example.c (100%)
create mode 100644 lldb/test/API/functionalities/disassembler-variables/live_across_call.c
create mode 100644 lldb/test/API/functionalities/disassembler-variables/live_across_call.o
create mode 100644 lldb/test/API/functionalities/disassembler-variables/loop_reg_rotate.c
create mode 100644 lldb/test/API/functionalities/disassembler-variables/loop_reg_rotate.o
create mode 100644 lldb/test/API/functionalities/disassembler-variables/regs_fp_params.c
create mode 100644 lldb/test/API/functionalities/disassembler-variables/regs_fp_params.o
create mode 100644 lldb/test/API/functionalities/disassembler-variables/regs_int_params.c
create mode 100644 lldb/test/API/functionalities/disassembler-variables/regs_int_params.o
create mode 100644 lldb/test/API/functionalities/disassembler-variables/regs_mixed_params.c
create mode 100644 lldb/test/API/functionalities/disassembler-variables/regs_mixed_params.o
create mode 100644 lldb/test/API/functionalities/disassembler-variables/seed_reg_const_undef.c
delete mode 100644 lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py
diff --git a/lldb/test/API/functionalities/rich-disassembler/Makefile b/lldb/test/API/functionalities/disassembler-variables/Makefile
similarity index 100%
rename from lldb/test/API/functionalities/rich-disassembler/Makefile
rename to lldb/test/API/functionalities/disassembler-variables/Makefile
diff --git a/lldb/test/API/functionalities/disassembler-variables/TestRichDisassembler.py b/lldb/test/API/functionalities/disassembler-variables/TestRichDisassembler.py
new file mode 100644
index 0000000000000..05aba06149030
--- /dev/null
+++ b/lldb/test/API/functionalities/disassembler-variables/TestRichDisassembler.py
@@ -0,0 +1,156 @@
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+import lldb
+import os
+import re
+
+class TestRichDisassembler(TestBase):
+ def _compile_object(self, src_name, func_cflags="-g -gdwarf-5 -O2 -fno-inline"):
+ """
+ Compile a single C source to an object file using the host toolchain.
+ We intentionally use `platform shell` to keep this as close to your
+ existing style as possible (and avoid depending on the Makefile for .o).
+ """
+ src = self.getSourcePath(src_name)
+ obj = self.getBuildArtifact(os.path.splitext(src_name)[0] + ".o")
+ cmd = f"cc {func_cflags} -c '{src}' -o '{obj}'"
+ self.runCmd(f"platform shell {cmd}", check=True)
+ self.assertTrue(os.path.exists(obj), f"missing object: {obj}")
+ return obj
+
+ def _create_target(self, path):
+ target = self.dbg.CreateTarget(path)
+ self.assertTrue(target, f"failed to create target for {path}")
+ return target
+
+ def _disassemble_verbose_frame(self):
+ # Same as your original: current frame (-f), with --variable enabled.
+ self.runCmd("disassemble --variable -f", check=True)
+ return self.res.GetOutput()
+
+ def _disassemble_verbose_symbol(self, symname):
+ # For object-only tests, disassemble a named symbol from the .o
+ self.runCmd(f"disassemble -n {symname} -v", check=True)
+ return self.res.GetOutput()
+
+ def test_d_original_example_O1(self):
+ """
+ Tests disassembler output for d_original_example.c built with -O1,
+ using the CLI with --rich for enabled annotations.
+ """
+ self.build(
+ dictionary={"C_SOURCES": "d_original_example.c", "CFLAGS_EXTRAS": "-g -O1"}
+ )
+ exe = self.getBuildArtifact("a.out")
+ target = self.dbg.CreateTarget(exe)
+ self.assertTrue(target)
+
+ bp = target.BreakpointCreateByName("main")
+ self.assertGreater(bp.GetNumLocations(), 0)
+
+ process = target.LaunchSimple(None, None, self.get_process_working_directory())
+ self.assertTrue(process, "Failed to launch process")
+ self.assertEqual(process.GetState(), lldb.eStateStopped)
+
+ # Run the CLI command and read output from self.res
+ self.runCmd("disassemble --variable -f", check=True)
+ out = self.res.GetOutput()
+ print(out)
+
+ self.assertIn("argc = ", out)
+ self.assertIn("argv = ", out)
+ self.assertIn("i = ", out)
+ self.assertNotIn("<decoding error>", out)
+
+ @no_debug_info_test # we explicitly request -g in _compile_object
+ def test_regs_int_params(self):
+ obj = self._compile_object("regs_int_params.c")
+ target = self._create_target(obj)
+ out = self._disassemble_verbose_symbol("regs_int_params")
+ print(out)
+
+ # assertions (tweak as desired)
+ self.assertRegex(out, r"\ba\s*=\s*(DW_OP_reg5\b|RDI\b)")
+ self.assertRegex(out, r"\bb\s*=\s*(DW_OP_reg4\b|RSI\b)")
+ self.assertRegex(out, r"\bc\s*=\s*(DW_OP_reg1\b|RDX\b)")
+ self.assertRegex(out, r"\bd\s*=\s*(DW_OP_reg2\b|RCX\b)")
+ self.assertRegex(out, r"\be\s*=\s*(DW_OP_reg8\b|R8\b)")
+ self.assertRegex(out, r"\bf\s*=\s*(DW_OP_reg9\b|R9\b)")
+ self.assertNotIn("<decoding error>", out)
+
+
+ @no_debug_info_test
+ def test_regs_fp_params(self):
+ obj = self._compile_object("regs_fp_params.c")
+ target = self._create_target(obj)
+ out = self._disassemble_verbose_symbol("regs_fp_params")
+ print(out)
+
+ # XMM0..5 typically map to DW_OP_reg17..22
+ self.assertRegex(out, r"\ba\s*=\s*(DW_OP_reg17\b|XMM0\b)")
+ self.assertRegex(out, r"\bb\s*=\s*(DW_OP_reg18\b|XMM1\b)")
+ self.assertRegex(out, r"\bc\s*=\s*(DW_OP_reg19\b|XMM2\b)")
+ self.assertRegex(out, r"\bd\s*=\s*(DW_OP_reg20\b|XMM3\b)")
+ self.assertRegex(out, r"\be\s*=\s*(DW_OP_reg21\b|XMM4\b)")
+ self.assertRegex(out, r"\bf\s*=\s*(DW_OP_reg22\b|XMM5\b)")
+ self.assertNotIn("<decoding error>", out)
+
+ @no_debug_info_test
+ def test_regs_mixed_params(self):
+ obj = self._compile_object("regs_mixed_params.c")
+ target = self._create_target(obj)
+ out = self._disassemble_verbose_symbol("regs_mixed_params")
+ print(out)
+
+ self.assertRegex(out, r"\ba\s*=\s*(DW_OP_reg5\b|RDI\b)")
+ self.assertRegex(out, r"\bb\s*=\s*(DW_OP_reg4\b|RSI\b)")
+ self.assertRegex(out, r"\bx\s*=\s*(DW_OP_reg17\b|XMM0\b|DW_OP_reg\d+\b)")
+ self.assertRegex(out, r"\by\s*=\s*(DW_OP_reg18\b|XMM1\b|DW_OP_reg\d+\b)")
+ self.assertRegex(out, r"\bc\s*=\s*(DW_OP_reg1\b|RDX\b)")
+ self.assertRegex(out, r"\bz\s*=\s*(DW_OP_reg19\b|XMM2\b|DW_OP_reg\d+\b)")
+ self.assertNotIn("<decoding error>", out)
+
+ @no_debug_info_test
+ def test_live_across_call(self):
+ obj = self._compile_object("live_across_call.c")
+ target = self._create_target(obj)
+ out = self._disassemble_verbose_symbol("live_across_call")
+ print(out)
+
+ # We just assert 'a' is in a register, then there's a call, then 'a' again.
+ self.assertRegex(out, r"\bx\s*=\s*(DW_OP_reg5\b|RDI\b)")
+ self.assertIn("call", out)
+ self.assertRegex(out, r"\br\s*=\s*(DW_OP_reg4\b|RAX\b)")
+ self.assertNotIn("<decoding error>", out)
+
+ @no_debug_info_test
+ def test_loop_reg_rotate(self):
+ obj = self._compile_object("loop_reg_rotate.c")
+ target = self._create_target(obj)
+ out = self._disassemble_verbose_symbol("loop_reg_rotate")
+ print(out)
+
+ self.assertRegex(out, r"\bn\s*=\s*()")
+ self.assertRegex(out, r"\bt\s*=\s*()")
+ self.assertRegex(out, r"\bk\s*=\s*()")
+ self.assertRegex(out, r"\bj\s*=\s*()")
+ self.assertRegex(out, r"\bi\s*=\s*()")
+ self.assertNotIn("<decoding error>", out)
+
+ @no_debug_info_test
+ def test_seed_reg_const_undef(self):
+ """
+ For now, you mentioned constants aren’t printed; we still check that the
+ register part shows up (first range). When you add CONST support, you
+ can add an assertion for ' = 0' or similar.
+ """
+ # Use O1 to help keep a first reg range; still object-only
+ obj = self._compile_object("seed_reg_const_undef.c",
+ func_cflags="-g -gdwarf-5 -O1 -fno-inline")
+ target = self._create_target(obj)
+ out = self._disassemble_verbose_symbol("main")
+ print(out)
+
+ # check that at least one var (i or argc) is shown as a register at start
+ self.assertRegex(out, r"\b(i|argc)\s*=\s*(DW_OP_reg\d+\b|R[A-Z0-9]+)")
+ self.assertNotIn("<decoding error>", out)
diff --git a/lldb/test/API/functionalities/disassembler-variables/d_original_example b/lldb/test/API/functionalities/disassembler-variables/d_original_example
new file mode 100755
index 0000000000000000000000000000000000000000..e02bf2d1646e47583075418c39e7c4c387866336
GIT binary patch
literal 17264
zcmeHOYiu0V6~43UT{}3jofmN)!Xp}o(AI0Oane8%vQDzj8WKqJfTS%Q#=B#C(LUVW
z*<e#1Y139%QPdz+Ek8g4q>7 at d5~UTYs{Sxa3ba2;145!we?$l>BL!+&sN6!c{m#re
z<Mpr$RbM}<xz^0N=ey^-k8|(bnd`mxf#m+7kftdKVRfS-?O=zCBsgL2dX7LO)HbyY
z_f4u>MWHu2n46w(1ybGcT4=FsVLS{;yiSVLksDlCiIf_WBwnM?Nfkm6skj})i%<gV
ze)%XDAX3u3@^m62=+51f=UEk5%cH8y_P7SKI~U6o`{f`@;gL$bdFIVCPvk|er%3T9
z`Gl@*>`%Yckw!sx?n?Q2$C&4rd)Zzjl|kX~n|9`3DeoZjnsg@{<(HpzVI at +^`#gBu
zwQTY6Lb~}7m)EEtI@$js*}bYammS$U(3{Kj<g$gzWY6Tbtvy=@V&!72-zk{tMP<;K
z+IP!Q6*{SA2opZVSK%VJQu+q9Y2X3v!K1CetW0nE{xhrIf9%IEOCH1_8wzAELxOY}
z4`k!E|7!$uT&WAzZ}JY%;6`;^Q70`0Os|7q4XmDhrh$FA0Z#R$ygm{PBRy#vquGL)
z%id!t!+ at jHM%gw?wvjip1vOEz%Zfm9_ExoT|M2cTMt`h7w#AEIH=vB+BL|F(RkFsi
zW!owpIj|>JELca(ksReYmM<1KPs25>YlzB)&|_g-bf=&Jxdu|kPi0+p3H3^xv7bKl
zU35c8 at nfmvxt8atZ#m_n)EpZKKF1kuPB_Wl^C$XiyIh?5n0zPUVhvt9E at o?R8b=c5
zYH(-1<AnJd+#N at 5LTAF2%@8~VxIl0ZJP>#w at Ic^!zypB?0uKZp_`mePpWCncTk6!M
zmelFU?|PL=eRIYRRezg0b-v}C)86Wq*8x|%- at v`SD}fm4<CON+#VSJgGtfuRigPHx
z_}jzN7rvaDem!;Sjk$wIlJS}NuTp1rUVu$?bsFaHjmFx$zTr at SKLsRpCh`P{>*wv&
zsLBw<QI#dti|t(}$?zOY*iSk3TS_FZy_}k!OFj3_uGDk$;gt4#>cVAv6*5 at O8MIU{
zj<$F0UEovwPVU at -WL5dh(bTD(e~KZRntsz>nmWDnO5n5CqE)jQ+ at 6mtgRb3yvc2+&
z|JL0|<oX;175>v-NPbAwoW5^9HC>r|A$f^D;!h_pZBNd(pXs6^PABJ5=>wNir+2(K
z`|Rav6^W;2wCUufw_Zhd at frABzvG<OA6^eowm}Fy5O^T)K;VJE1Azwu4+I_vJP>#w
z at Ic^!zytrD2k7 at MZGCwAW@JOZBW;+gR=)(f1Cmzg`ysJ>Ry*FURv&?+-!v&`_Z(8%
zWQVqXX>-e2taS- at eyfN5CNkLD?EK=j5zzr$Kl@{~sza)_j-j^Bo7!9NZaJxTt=s<T
z>oz(+6B3`weHwQ1n;Dr?o;I$Bk?%nQ2HQHm722~b8oC)J6A*;J1Azwu4+I_vJP>#w
z at Ic^!zypB?0uKZpsP%xiFOl~l${s`7>qud#E4+=5%UD0l`x)1=F8dwVvo1 at pb*#(2
zM`tYvOw!-KTrHBxW4y^w_9jlyq7y;(B)-V$SGce`!?KlSC(G3=MR<D`6D}fqCB>fB
zmlSB9BZW1t at Cp_@!4;Ew&#)affjU9*`=IK!2cTn$A9S7ebub at 4={maj8Ov8kzs&9Z
zE9+jpIsgB)kbUL;a<}q05nT4E?%T8H27U9<kxId?=<#@LAlBCtuQ+P_{{H at 0-@vsD
zD>h2!%)*#{!YY-s#eyCOp at tHRtGiG{L}`u}HH<?y1~8;(!B4mw({2l^@E4(mLk~?x
zyy!$!g$<xh8tu%EgdWPZs3tN)-!?^>nlp$rXGv?0VxRAFt-E_=B-~wE)3IiK_ww~C
zqGgPQP)ws}qBGomL+kd|Yt|00+=U?@j;Ihr!z)olt7C;y1{UGbjVj!IV{0o0X>+?G
zOGW#lBdGl6AT>?<plulm8V$G*>2d}}SnCS4N3PNi16TLG`u0n&eD$uMycvDxrh(#y
zt1(iRgcAJxt%|f=(Q-VpI%-GO?1)5mMB1WbktNZeN4lc7M<UU)(JzErknca~^Ndj}
zWyfd_wqZ at 0`H7qrORL`TV&3Yl<m_zTw5`!%DQ)$~dvm!H`JRbV at h&TEL&=Qv+E&@_
z9XvSPJ6b8EZFDMja@$#}+*`_~$9po_vRN)$d2GZkDQ81>wqPr>G?u2 at 36&l<ODgO9
zzli)J?YoWJ`Cq6+-NtT)!(|SlIdBKAZ{zw7u7`0wit7nnPvZKxK;77+-mUs=Xui;p
zaxXvNIvdc~sAqo_ at Q`X(Cw<Qy at d$s~hhN9I>??=rgkjt at wNCXVT-_OFh|=i-O|AC!
zrMsz0-HQn2FYgDzA9U<jdHdT1Cx6tdf1`H&!pUc)n$abP1z9hjUpw|!D*wFnibGqW
zn)C%e#r}XxQ<#+#q%DLG5v_NQVJ#e at FPM3;qYl1S=?M>VB3pph%V!99hg$3TSy%_i
zKGdP)y(#!%w)f|MCvaViYGe`k^3Yl(^R6V_OE~_7#IF-|8h8i#eOp2-U3nb%3iPj>
z=W_lxizND=E!oRB5!NGYFV7`S!4w_`PWAEI{|LD5SS-H10Q~C3kfXg0d_ at Rf8@}<Z
z4yQ|YJXXZv4Ra)G*yfl5P_`?hqnMf&4tf}N-bm9?k1{MXMPn>i9KkG>v5Td$VOAzp
zx=7QRZDnG8^)2Y^N!Bn+C3DKK3U+Bqjh4*3Wn?P({1hlPngL6Dk!jtz#4v^q4IW4u
z$y at dsIBv4{_$`A6hW8*V9O^NWDNar8J*13%`)}PnxZk*SXy|bAh;d|a_x>ap{xdKM
zIrQ=g2VLl>jAu&+WDF}~+NN at vNUewB9)p3h_)LtEDHn}#vyh?lGQ+pRDw8c3m9mvV
zCI#G#k#d>2&Ma;iIAbGCq{BCLPt+_v-LtrE<V>Yv<*B at FjzHQaSB^^zFAA$Pp<;!i
zZN<h4l^9+-6IRKds)0r-S)9qrW*8dWJ={ZM#W5c@%i}7RnJS<NSK1{vg}*))DF)I?
zR?Z{>$0l;NiaFJcAsQPiLba_)+-c0fsOY#iW{vYG8P6aS(OjxKTwI34QG%Jzrcr#+
zrkqhZ>ZTZ6tYR3jd5l9~)<|UxLvFMvadTv(WStNV2hl8v!(|dRGgJweOeW|wPe;@!
zj*gZs9I5)x1p4mBBaC;38#?*@CUOJgBFMHjd<qJEYYTrH^F`{l#)vzgg*Dh##B2G-
zm at jf;jV(oGeiWGAkF|VxKa14Gkfoo0JMh2Y+g<7}>lu+!zN}~b^*6z%wT$p(y(97m
zTrSm-d?$QacVvN4lZ7wqCy}yVf{7FS{{IS4TAK)8)*~X at m?!lYJCXMzoz^;{%X&v-
zAM^e7XV_WKkxzx6;EynoGi)g3``iCLq!WKP+j$E|CWxO%rMRE}I56^w<nO(Q_<bbn
zT9`P&-~W#Qt>x>?^Ca^`$ZJmIlRkce6GTdVg)h3ur+s{RuZv9hI$X+?{GRpk<vT^B
ztoy~@-+uRduI8IaXG at lTd(lOnhdEVI`d_~1M9R5e&h;bD at 5?@ZpYB?Ur0 at 6IAbu0<
z_lVFK=V`<HzLoRAetXeH&Vt)s!<YYe%=G|75Ia{}&;@@F2{iVkeEH6m^WGP^|LFOl
zAUx^E9|9v2;mf(v*LulXsm-qN?!p)Occj$vRf0|XT&c4vLU<yvgkHqg6RxGse5t3O
zFZxpODKDwNd{6c<U&;}Cs%vdn&gHYYmn8f{9^yuXFLE98eP-(GoMFM2AUuILHQ>)N
ze_RY%`uPJ5_{ZWd;qFi1Cz$VV|4Yn&ssUf>adU(6C;MHZ%%75t at I=48LHTEy->ChP
z&sc-<=b7(+{)H#ylP+_IU?exwf*9dW9#|U at Ux7=;leB9IFd2J at df`F(ut1pg73foG
S<yDtIvCW~Ydwc|+s{R21WT689
literal 0
HcmV?d00001
diff --git a/lldb/test/API/functionalities/rich-disassembler/d_original_example.c b/lldb/test/API/functionalities/disassembler-variables/d_original_example.c
similarity index 100%
rename from lldb/test/API/functionalities/rich-disassembler/d_original_example.c
rename to lldb/test/API/functionalities/disassembler-variables/d_original_example.c
diff --git a/lldb/test/API/functionalities/disassembler-variables/live_across_call.c b/lldb/test/API/functionalities/disassembler-variables/live_across_call.c
new file mode 100644
index 0000000000000..53de9247c7f10
--- /dev/null
+++ b/lldb/test/API/functionalities/disassembler-variables/live_across_call.c
@@ -0,0 +1,11 @@
+// Declare a real external call so the compiler must respect ABI clobbers.
+extern int leaf(int) __attribute__((noinline));
+
+__attribute__((noinline))
+int live_across_call(int x) {
+ volatile int a = x; // a starts in a GPR (from arg)
+ asm volatile("" :: "r"(a)); // keep 'a' live in a register
+ int r = leaf(a); // 'a' is live across the call
+ asm volatile("" :: "r"(a), "r"(r));// still live afterwards
+ return a + r;
+}
diff --git a/lldb/test/API/functionalities/disassembler-variables/live_across_call.o b/lldb/test/API/functionalities/disassembler-variables/live_across_call.o
new file mode 100644
index 0000000000000000000000000000000000000000..643b21b30b08ef9a3b6aee5cfaf411d4c822d386
GIT binary patch
literal 3488
zcmc&#&2Jl35TE^WoH$9H1o90rC8nuF*G_2aB%op>C`lVogcMLKgtcDp#-?jK+TElf
zDwG2UN_!|5Bu)r%>X8dnhy#cl2q6%M9{2;eAV5Om08*IQeG_ljTTzJvBfXtBznOV6
zvpe(l$}{U{WJv-h2~I;tViX{k?(ECKwhW_axpDRKta|NfT*!YW#kKWWRYBnVSF<Q-
zkOmn!xsT#fVL;kc2d34Jq|FaN9iEgnKpK+r`4LUYyJOk0sr=B?aLOCIE{y at mx<pcW
ze1Ag8uVfz29Gh4hd2#|k(Lg>LLTe*<>Pe8tJEDeSM4MWLD<iK at 0rcon=a?rZ=%BO)
zbU^PcDEZTwOa`EDF+iXdWG at AEOYlJFt;jHqDyC49UeZ9TBc?!4xKZVI)fA*0#56o4
zP0G{-d`QrvfJ|b5L9eQ*eI&vhE{!(#K&E4~wsO0c>owY~+`@uh)QejUKQ~wR{cUfh
zP(Y!+Q`OCOv#{y-ji%vSXWFi5EzB1jXSaD`+ikyUnZ8a=rDd~dEL-zdsb&=I(n85R
zIbXx)vU$q3myCs_YPD1=p0baF)7Z5thUvCFuVNaGqnl8ux0_aBN5rrXW7Mh at -}3yz
zT5Eg9_rS3X8yYRY>j*Gl4_vqpi>7AjqW`H<kq4zkB<;Td(q2}Y>hDS4zyTqg+aTai
z4Q4M4rePOxDx*X`lJwW$05O^<T)3GDhD(uV<RjXH!5$W;OUE}J`Q^7C>zAHe{Ns&R
zRBT5YnSe&MY~MrL<T*`Aoz=3b5iNs!GIc(X`%zX>nX|HtN8jsr=>CEnD~bPvpbEbq
zB<Zk?lVF?yEFpoEyL7jb^x4s0fxd)O2*)u^AO(gJP_oAfkI~yG4 at dInZ}}<W*_bVf
zg94+<DEi+AA#uY}rqQZ76T)ex+3tSa6A{iB$usmoBD at C|>mDW$iEss1KM6>zCp;A(
z;e+*zUlKa5iSc&?&i&2!#{wT0obgYD%<qZu&jg-}*?%MC*%<z<kmq9fcS7d6Xf&eT
z#rZQnEbMcFlWo-gsK6=DUa=>pi#_AVguE&^pD#pos9DvWt%^~ty4Eh}uH_iIZ|(U&
zS8LP43r$!!ZPRIZc+-V8p_}Ksm9}kr7U72wZ=FWVs-VdoE=H|(C(vlw?e1GsZ?%0(
zfA;y!6P|CF7eS|6y+Y1jV=KIEqlr)`5yq-lY}aU7U1#EDu46X7QAPe*llXt8U-1O$
z+c at Q@$&un0OL!%QI(kxn5*F=o!prnP;(iRJ?%6$p3YyC at 8wfrpeze@@G?y|i?mxz9
z{^B{&5iipNiTgN|x^2lf$^R|k at E$t~9=E-G7yn6N=>Cg8&3{t;Wl{g0 at TdCe9mnfm
zLM)yWxm*|Eqa;KqZu=POlH%VI at ox)XichiQ at y{VfXBKfq>6QreeH6q!$7g;qf<;ZQ
z(#J;P?;J`Uf0k|BB;|iYG$iLm`O|+Pp8q6bN%?;$;`94Qvw%2n=I<lQdtnl=iKL-Q
zu>B##wnezZBXYu@^JfL;&obTDN%emr>gV at I_0z1eKjU8_mX!a7h`*2eD8+3*M`cp{
z+ams|aHjb5&yB}_5ivS5Ugy^${tD`%#PONmM5VHR&_?!V)Q;~j#wb1!`-Kv~pSX#?
Td(x$;D5RWlAV&YBc>ezYqMSxX
literal 0
HcmV?d00001
diff --git a/lldb/test/API/functionalities/disassembler-variables/loop_reg_rotate.c b/lldb/test/API/functionalities/disassembler-variables/loop_reg_rotate.c
new file mode 100644
index 0000000000000..84c1a18e11697
--- /dev/null
+++ b/lldb/test/API/functionalities/disassembler-variables/loop_reg_rotate.c
@@ -0,0 +1,18 @@
+__attribute__((noinline))
+int loop_reg_rotate(int n, int seed) {
+ volatile int acc = seed; // keep as a named local
+ int i = 0, j = 1, k = 2; // extra pressure but not enough to spill
+
+ for (int t = 0; t < n; ++t) {
+ // Mix uses so the allocator may reshuffle regs for 'acc'
+ acc = acc + i;
+ asm volatile("" :: "r"(acc)); // pin 'acc' live here
+ acc = acc ^ j;
+ asm volatile("" :: "r"(acc)); // and here
+ acc = acc + k;
+ i ^= acc; j += acc; k ^= j;
+ }
+
+ asm volatile("" :: "r"(acc));
+ return acc + i + j + k;
+}
diff --git a/lldb/test/API/functionalities/disassembler-variables/loop_reg_rotate.o b/lldb/test/API/functionalities/disassembler-variables/loop_reg_rotate.o
new file mode 100644
index 0000000000000000000000000000000000000000..041781731d1d00a022f8eee3a05294cf4e4cdeea
GIT binary patch
literal 3680
zcmc&$U1%It6h3!;Hk)ouH-DxzwOMLHE7;lHBsNJabsu82X{~8U at Tn}byEDnQyE|cb
z64O$wQ2S67RD2Odi}c9{K|vG?LQuitOD!V4)rufUA4Gf+T8QV&+_QU~ZXOiD19#3j
z-}%lxckbMKcF#X??64*Tun0H=Ess%vRA<Z11!fNVk$K4<zI$=w`Ny*BS1Ii?4Ii%S
zTHA(R3|L}%`0mTd$<Bvb92>qnhM10i_QuBbm7BM>21!ABcAdE5TX9WC&X;fD6eh$W
zhI%`WF&xKn6mYEKcnOCAePY4rn>Mb9h0DhDEIP9%11AX0hoSi(G#`fMFN0z9>D_P{
zgJKqw$PO_PU?&1hNK6Z`xd79exgfg5$VjiLkJJZ}1A`;ogFUgvz$;<^K(hpSx$S3n
z=pzRb2NQdC9O-?02Y_yZwmXE5^y1pbLEGMPO>~uLED_8C&@l}9$e~0c0T6!_C=o8=
zFhRso;YWC|^~p3iNtl$-6VK|PokvK=oeT}p5gLZ}`Z*Ky3m7!DI2gn-^-gIRTS6ly
za)g at +h!Fu-WSB-gFvlqnVE^n<tk|{1)T&c&l)YMNbkxdNnZ>f7+PmcY%Z&r+G&)Nw
z1*_;)(+d^9T(y1YuvafSquF$&vRd7 at T=z~nMc<;NiK$}7o^rCzM9I#$6QdKw at oWj7
zQ^ozRJ86$j777!k%zpP_sCeFTzV0mM>z<D at T17}Nc~vL9B1^c<5~V`kcN%{BNNst=
zZ$uNJT=T&$7NG_W$0<P>PQhvLp)c5u;IsUFKmMop4HiTz5ohN>tR=<Z){gjVcvESi
zJLMA96Z1Y|bmb-}M~FLJ_mQHjjRTD#GC>zZ+BS1nV8i6Ab2nFio!*`K<<gzEfBN`0
z1I*+W=%h{%NT1pNw5i7|{j6nn#^UG9okvYGHiLGrYIYnoyAd6nG5cfs87s_~F&~KO
z$*bthm|e*4iCO0T7 at N@quIAr;JN)RI%MA<}@cTwP2Hv7}FkUNt6WrJY at 7M%anykXm
zVT<CmOdXOfN3nG1QM}N64^@w{e*WfsRH=fx+HN at m;dH;5*RSA=5j3RW^mZJ3NWp2B
zwo0GlG(WX>7LGIioDA at rGJZ+obdE_F|1flep9>XVmpGq!=6 at yel+=u0lm2i7|3><I
zBlvacKN7*|JdtEV)m-Oy(x?7O!gaq7-RAz$6;n?g?*rrf%~CRtxGI0Q#A$x at bXgI6
zkMxg9&GSnaz$!U~mBqYWDAb)*uzY9D2kOpM2Roq*R=i at R+`w*W)t!nh!G>SYd#>AX
z2oEPgh|Nm5=HvrkNwG^MBq%_+=6Yb&JnYy{9$(nk at a^Jhu;_OnPuxa%@ty{)rc)6)
zpR<&A>vq+N^06scxD4MepgvP2{h!FsI7_?kuvcDyKO$9V-&NU$4q at sYp?HoSD147Y
z5ANv*yD$))leq85gm<N873Gp2#fW=Qg6B9<>L{1)ZxkcyrxN6QEYt7d)-gb5JjVpq
zazoanAO7&R`VReFBpJieu6|An!Y~=C<}=Erd(y7{nyjCa2~=P0dtCo8;zZ&5k7gii
zvi=VYNX_+GyNI)`QlF8BitBJJ%C#}jZvAh{pCDcrt)F at X_hp`!`aR9bJTwDYl=asb
zkechWzJNH}D)q;9m!b{#>kwO(+tr-nmduZ?i`UP at FVJZB{@3LD^Lx_!cj4fC#y>%<
z-TIR>Dar?-YCfafk1*1%{w-PmDVa(2lQ^RFPasBfM&I*uS$_&+s&IYQZ(vYAw&9_A
gLFIA(VT|gNa9-#E+(sk1_Z0h6){n~sQti&qUz*2sSO5S3
literal 0
HcmV?d00001
diff --git a/lldb/test/API/functionalities/disassembler-variables/regs_fp_params.c b/lldb/test/API/functionalities/disassembler-variables/regs_fp_params.c
new file mode 100644
index 0000000000000..0f94076eb3299
--- /dev/null
+++ b/lldb/test/API/functionalities/disassembler-variables/regs_fp_params.c
@@ -0,0 +1,5 @@
+__attribute__((noinline))
+double regs_fp_params(double a, double b, double c, double d, double e, double f) {
+ asm volatile("" :: "x"(a), "x"(b), "x"(c), "x"(d), "x"(e), "x"(f));
+ return a + b + c + d + e + f;
+}
\ No newline at end of file
diff --git a/lldb/test/API/functionalities/disassembler-variables/regs_fp_params.o b/lldb/test/API/functionalities/disassembler-variables/regs_fp_params.o
new file mode 100644
index 0000000000000000000000000000000000000000..2bec9fd94d98abc0ccc0315666b125ab0493bd74
GIT binary patch
literal 3304
zcmc&$&2Jl35TEt#Ce8=>E=fpfqEahGqH8Bqby84?gqk*Pgb-2$2awrazl{yH*Yd7I
zXb->z^}qq~4<JB7+{guq6H+NHLa0Ec9=LGe$gx~Glo{_E at 5_rp2q7`j?#%q=>+SQ*
zByU~Z-pm*VFd1+GQjJl7&jwR at qbqK}G4%X>{G}WCe2vdde7?cw*3C!I!G>%%k%h7a
zmf>2~G3yh<{Sb^vBbS at D&0KqSVs;@nxiB>l&Aw;M0%RP6w2ir|^Jeb+$WtTB^IOx;
z%mbJ<WKJf~)->irbuLxyQ()#UjEsz6si<4p$Rfk?kEY*}!Q-ZsXvT{sWZH<Dc-*dF
z8QuU({vb=0EK6dDrC>Obz_zR`6flXMrvFEhUw50kOM9Xn)xze|>Z((43cIy<>2x)Y
zThaM^9+hgxbNsN8->Jv7h8v5`u<eW0m3+Ov*ErK^hp!1gb||R0?ibv3u_B5Uw-6Lp
zi~iY_3clC<b3w4?uC94ru~Ik}JPvKK8<m4rx#hOqM&$UAuZ9hg at 5l;4uR_Hu$0CaJ
zTg_G{j<h(agdMLgzy%L{sDOX~M!T2Lz53(={--ab;xow!z_|JX82b~(!suZ3eQa#P
zk4u}{5LLveWsArS!}w$DA!0NM_ at J=Z9SYM}%#7P7x-$H7eSP`jm9dZR{BZ5*hi`xS
ztOd3?3_}^29cJv&X?tRzWY3iBxsrXnWSgaofyw^Wh2szPck~k)Ex7-npAR;9KN!2-
z{0p3h_9&U4ECI!A!jPh+q6x#PqDUr8C|W9-FqJA&+C)C3oS%O)?lP*No_12k5l#ci
zejG<QW8}I>FC at b0>)7 at ffk=eYEm3b1v7PXNE|R?1&iEVB#$U?#b%}F-GXA;5=OkzR
zE2(qOGJZ?qOPc?;Qa`2P-%0(nhJP>hCp7$y)M>F1aoryiwRaCR3Tk&PNjqUGep2F;
zN9`6z!<VIgMRGkqI2GY_cFV5kwZ$GdvDlA+R%AmoQAmvS(684b+*wXr)LjWial0G_
zK_rguvecuOq1F^-j5w6RtyB&JwPp~4(+p$bTzYQjOccBR>)_CCE|YIm+dULt$D|Sv
z)pCIAS7=)mhup!cvFjm!vqAhnqF*sjziB^^O?+RgldQND97mBbwdcvYK`$hpuSA0*
z6Mg~>dQS4)kpW~^N%tjP$H<@l-V?o&u-;queHSe{M$WW1iFjTU?WpfNXrMi_!48st
zDVNW_)M>8Oy~FeTB;x(*Uy|Ti8P81jr8((WzaanX$jSh!ukJCfPxnkNJkKb0>6(nY
z%YfuupLqvymQ~m7Orr6gW1H^VLPNjvugQkubJF=~NAR9yocfD$>gRu1*55~)N?f1$
z2WVtj^}8qIRg7fa_jZD{boZYnpr4D+&v|d4(C_=7 at ALg*=*#i^{a+;YIbDxu<xtmq
zNFwOxKR~!&{i>{gS;o`-PvE21e-$yxseflI>#w0rC9co>V>FuE4?<*btA4zH7^C_`
Y9G7SSzoDStJ=qRqeeQpX>(@Vj0-oqJ5&!@I
literal 0
HcmV?d00001
diff --git a/lldb/test/API/functionalities/disassembler-variables/regs_int_params.c b/lldb/test/API/functionalities/disassembler-variables/regs_int_params.c
new file mode 100644
index 0000000000000..3b65a9e421f93
--- /dev/null
+++ b/lldb/test/API/functionalities/disassembler-variables/regs_int_params.c
@@ -0,0 +1,7 @@
+__attribute__((noinline))
+int regs_int_params(int a, int b, int c, int d, int e, int f) {
+ // Keep all params in regs; avoid spilling to the stack.
+ // The compiler will usually keep a..f in the 6 integer-arg regs.
+ asm volatile("" :: "r"(a), "r"(b), "r"(c), "r"(d), "r"(e), "r"(f));
+ return a + b + c + d + e + f;
+}
diff --git a/lldb/test/API/functionalities/disassembler-variables/regs_int_params.o b/lldb/test/API/functionalities/disassembler-variables/regs_int_params.o
new file mode 100644
index 0000000000000000000000000000000000000000..3f7d635f6cd560187493164b40aa8edbd1326f12
GIT binary patch
literal 3296
zcmc&$&2Jl35TCc}%{Og6T9S~`Sfy5jMAuFzbyHBW)DRlAszjCWaX at Ch-c1bt2;NNy
zy at 0r&3XqVva098AN?gi?b5#%&6hYx%;K&8U5s5P6oyoqut`I^<Occ+|Z@%7po*DVA
zXD%&UN`a7qbC7C+0({z^%9Wm2fy3yj{`?>}q<&pex0ckMFCRjS0|UJbIh1o?snwiy
zAa`A at egbMt6$%rMUFc4ZPR<m at X2$!X$@kSHfaNOEDpOad?ZVmNCx=f=uS`5W4PZN9
zO(oFE1m;8aSgJb5!7iK|9v;S0QLnTbK!)WXPA_4>0m7IPO}$`))kVlwYnYOx9P|nF
zT)#+H21I&sP^6cJ5(%7~lY<h5I-L8TGQa7y*Yn#!H)?d+`Gp0y<d)VOaei(ijyI#T
z#Ud&jTQ%43w2EuZxY6?BV7b%vgN6BGv$@?mx!LW!8u+nGLFGli<ShpCLAmag!tz4d
zKQ&*+&qe=q7 at qMK&eUq<dg*ldD0GAMsM=`9)lIMKwIbJt;zp+x6t}oUxL2ZHtHwbT
z7gySwTXAFt0_Fe at YT!d10tjKG_XfR}PoCg^x*!!ll$-$6)hnQOM%Bzn|G<aX(1agh
zo8AurV$`u^<VvYO4m?1NCIJ6YnC%UNt!Ax*&e5I>KP;ZlfBf0C()r!@miwN#_)`uX
zdk6+C&JGVcBNNVOpX(fY$~odXc4*<4sDJm?_$B>4{fOoq{B|$P3wt~Z1{?pf$i_ at r
zWP>*Rnu864nwE+-45f-D*)XbUsc6G^s<5<-d|W%fd}q>SR6#%O7{?J#^C*6YHC!-)
zW;L8{C$>j4oL-4~n at H@GzpsZRA7U5$4YtWm1;56)^rzrA8J}V<_$}6 at X9fR?@jP?E
zzh?cof!|?$&cMH8{V at aIWu5jKk>vkAQTN_~MnUhcJlhG=@neir9=%&!13$s~MdoIH
zaO**BYrX2#YTaNP+&I{Yfi`3-z-`3FX2)+fBHUPRH)whcMsc^=3BxGJ*kq~8UW!IL
zsA9;z6kff)A85404!G at 39JtS3UOO4Zp8p!Sw41Bs8#UJV#Wyi21Hnc$#O)iHOBDy*
z#j>$iL;hxq_<uq_W0tIGzrjuXyHO=saVdBZMZ)x+Cu at a1kYp|s4ep!pqi8U5lJ8{>
zAiGYcFX<*m{!I>gnYl32_gyrY82Nt70Wz<w-sk%c8fec{#DV!uUOqdl(_HI&N9Or)
z#Ix#G7(B)C!c1S9ldSr4{G(%l1E{{f$E5!Ah?9%VGm1TmIqt3i%%#5YZNx>^U6-sx
z8`Z%K-`5e#I)8~9O3q2=ryU`CR&d(iRNp-RD%amZn@)08;UA$<WZiF<<8_Q=)AySM
z+ce$pGGLxd&M$dyqmXs~3mNyPS(13U{})MpNtbdS>PCw6B=h{ABbrtJ6|R4Q<LUkB
zyEN;2h*3`So(-<Qh&G+1zVPd4v at hN3AbVT)ll>zY)hCj;L<9H*1 at rC6c7y9n|5IF6
G|NI3cpe+Od
literal 0
HcmV?d00001
diff --git a/lldb/test/API/functionalities/disassembler-variables/regs_mixed_params.c b/lldb/test/API/functionalities/disassembler-variables/regs_mixed_params.c
new file mode 100644
index 0000000000000..6bcb418f8ba17
--- /dev/null
+++ b/lldb/test/API/functionalities/disassembler-variables/regs_mixed_params.c
@@ -0,0 +1,9 @@
+__attribute__((noinline))
+double regs_mixed_params(int a, int b, double x, double y, int c, double z) {
+ // Keep everything live; avoid spills.
+ asm volatile("" :: "r"(a), "r"(b), "x"(x), "x"(y), "r"(c), "x"(z));
+ // Some mixing so values stay in regs long enough to annotate.
+ double r = (double)(a + b + c) + x + y + z;
+ asm volatile("" :: "x"(r), "r"(a), "x"(x));
+ return r;
+}
diff --git a/lldb/test/API/functionalities/disassembler-variables/regs_mixed_params.o b/lldb/test/API/functionalities/disassembler-variables/regs_mixed_params.o
new file mode 100644
index 0000000000000000000000000000000000000000..26391797be2e29cf4bcc9004762955ba70d10e09
GIT binary patch
literal 3440
zcmc(gO>7%g5P;|HdYvDK{5DELk~Ru;6DqoPLKG({D!C9yTOolOkqffc>viJdpWxku
zGzW?RArKt6AdWo%*CN3M4wVq?rK%7 at 2x$4a!V&0&0}^FscgEgqHWG*fPkNa*-^`nt
zw|;MO`LXjWno=M`!5L_2LIJYvt at X0MT!umPRDY>&@AjPhxzsa#`)<$rk2vq({2uk|
z*XPmEfOgPPcHz7q=QPd)XzD^j8&ABaE?hxvOj(IB(k#@5$5jzjpGv0(3_aZ#>Kz(Q
z_l@=^-J!SC5P)_Z^z at ljDg~gMiVVxcdn0;!DRnwEHL^PJ=m^F)KpPIw>HwC2>SRkz
z&h1m-GFD`O@(-wBymfpHmDbSoM7!=2^$Z?hiq4_dF!YQ5{x)7<9JUoYWc5^MU`!}G
z6|kh%1xX44TGB|sf**p^e?RPsRoj@@aT;#9UYnSmHM3 at RqwGygZ+hOAyOhbGvbkL_
z?Rqt{R`JSJ%X3!h4cnQW$y6#k)l*xI`bEd~Oxl!Nw6oTtGvnlnR<@Lz&DnD^Mf|&H
z&zDLI*6c!|kSk{AOAkWB*>LmK@~%_NZ&?ki>Y6rWHtSU<v&~6L%_PM_-g8_pvs&BQ
z_FO2|w+j{LU<9zBT=Sp+yKo6?*n at _DYv=|w$NzL%IKmaEy{AF#_NvjYw)VI2p%5Qq
zTlltAkx}6}luD^TyYC}GFAFCwj`@#FSI4v-<EXy|_r|sNwS}uIomX!yT|A+GnE*rY
zgbt0P!=N#8#28PSMz?89o;6OqY at 9r6=*e>f8s1*@55Ilf&n5}t<ktuj at cX?un-0y|
z_L+76!S6%9qGP1Ep+j37#wZ<!@XkYc?;*VZ0AE4UAF5w|JGp9<AzW=AM<8zC6u-d`
z7e?q<h|}rB_CSbBTO=fQil1a364W+n2!A0^K~5_CP3Ce>g at 43+n5FPfS(m#j{1)?x
zi2oO?ACK^_Sf7q?x;Hc)3PiL1Z&=Sp_;;+&Mfe at osYB9`I)4b%=69nf6uylUY$q1x
zlguej_;#5Q?)PS7=a@(1gIRP6+Z%bSP-r+iV0zB32h?M$4z at 5hR_b=8>|)0=8&1Vy
z=z5KOy;O1?<Og1(>_jJ7R?0Oe at 9Uu;R<Vc<AyBTB>R{IDo?|}#<k~6Mv+U=<r2d{K
zU$?xm&z*uYa5nQL?B6l{7(2R&i9M@;@=}%Le;&VJ1iJUpvA`|hkBA{?jV;)NOJd<S
zLe^#agG1hNph5GUh<+3o(U at d=mK~N at iirA>9%bbFE^nx?6cP1(0}W9|zB<EKWFyO{
z at 2kkEFD<iOL at zZZ9J_(=|1X96l)V3k(H at OSTV7-M2=5mW^`&<bm;ZInzrY))4~6 at l
z<X=ZlF7p0y>5tEO-%kQqN`8 at aK3RuduV{fLeNk*tUl$E=^?$|}O6sKgsYgg(7Eb-1
zVn*x#kn>Z0;UW1&zJo?thyAAF9z~4r&46ujx at L%J-XE<?>KDfwxQILd?U?g-qp$3j
z^M8UYuKsn-|GXq(8TI`bd0hVAIR6v8kG?<smeKq+GKv{J=S|Lk8g1br`9*$!M*aM~
kI*OOVe$s!0QGOb-FVFyf#YObnldYW&MUpp=h`T?313)rGtpET3
literal 0
HcmV?d00001
diff --git a/lldb/test/API/functionalities/disassembler-variables/seed_reg_const_undef.c b/lldb/test/API/functionalities/disassembler-variables/seed_reg_const_undef.c
new file mode 100644
index 0000000000000..54546bc40d04f
--- /dev/null
+++ b/lldb/test/API/functionalities/disassembler-variables/seed_reg_const_undef.c
@@ -0,0 +1,9 @@
+// Inputs/seed_reg_const_undef.c
+__attribute__((noinline))
+int main(int argc, char **argv) {
+ int i = argc; // i in a reg (DW_OP_regN)
+ asm volatile("" :: "r"(i)); // keep i live here
+ i = 0; // i becomes const 0 (DW_OP_constu 0, stack_value)
+ asm volatile("" :: "r"(i)); // keep the const range materialized
+ return 0; // i ends -> <undef> after its range
+}
diff --git a/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py b/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py
deleted file mode 100644
index 975261877e96c..0000000000000
--- a/lldb/test/API/functionalities/rich-disassembler/TestRichDisassembler.py
+++ /dev/null
@@ -1,34 +0,0 @@
-from lldbsuite.test.lldbtest import *
-from lldbsuite.test.decorators import *
-import lldb
-
-
-class TestRichDisassembler(TestBase):
- def test_d_original_example_O1(self):
- """
- Tests disassembler output for d_original_example.c built with -O1,
- using the CLI with --rich for enabled annotations.
- """
- self.build(
- dictionary={"C_SOURCES": "d_original_example.c", "CFLAGS_EXTRAS": "-g -O1"}
- )
- exe = self.getBuildArtifact("a.out")
- target = self.dbg.CreateTarget(exe)
- self.assertTrue(target)
-
- bp = target.BreakpointCreateByName("main")
- self.assertGreater(bp.GetNumLocations(), 0)
-
- process = target.LaunchSimple(None, None, self.get_process_working_directory())
- self.assertTrue(process, "Failed to launch process")
- self.assertEqual(process.GetState(), lldb.eStateStopped)
-
- # Run the CLI command and read output from self.res
- self.runCmd("disassemble --variable -f", check=True)
- out = self.res.GetOutput()
- print(out)
-
- self.assertIn("argc = ", out)
- self.assertIn("argv = ", out)
- self.assertIn("i = ", out)
- self.assertNotIn("<decoding error>", out)
>From 9b3f552a1a904d5664fdac2cd42f847cca415654 Mon Sep 17 00:00:00 2001
From: ultimateforce21 <abdullahmohammad155 at gmail.com>
Date: Thu, 21 Aug 2025 22:29:25 -0400
Subject: [PATCH 44/48] Add stable assembler-based tests for rich disassembler
annotations
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Refactor `TestRichDisassembler.py` to use pre-generated `.s` files instead of
compiling C sources directly. This ensures stable, portable test inputs that
don’t depend on compiler optimizations or register allocation heuristics.
- Added `.s` assembler files alongside the original `.c` sources for reference.
- Updated test harness with `_compile_or_assemble_object()` helper:
* Detects `.s` inputs and assembles them with clang (`-x assembler`).
* Falls back to compiling `.c` if needed.
- Adjusted regex assertions to tolerate both DWARF register numbers and
architecture-specific register names.
- Fixed placeholder checks in `loop_reg_rotate` and relaxed return register
checks for `live_across_call`.
These changes make the disassembler-variables tests deterministic and portable
while still keeping the original `.c` sources around for documentation and
future regeneration of the `.s`.
---
.../TestRichDisassembler.py | 65 +-
.../d_original_example.c | 7 -
.../d_original_example.s | 461 +++++++++++++++
.../disassembler-variables/live_across_call.c | 11 -
.../disassembler-variables/live_across_call.s | 371 ++++++++++++
.../disassembler-variables/loop_reg_rotate.c | 18 -
.../disassembler-variables/loop_reg_rotate.s | 557 ++++++++++++++++++
.../disassembler-variables/regs_fp_params.c | 5 -
.../disassembler-variables/regs_fp_params.s | 304 ++++++++++
.../disassembler-variables/regs_int_params.c | 7 -
.../disassembler-variables/regs_int_params.s | 312 ++++++++++
.../regs_mixed_params.c | 9 -
.../regs_mixed_params.s | 375 ++++++++++++
.../seed_reg_const_undef.c | 9 -
.../seed_reg_const_undef.s | 289 +++++++++
15 files changed, 2704 insertions(+), 96 deletions(-)
delete mode 100644 lldb/test/API/functionalities/disassembler-variables/d_original_example.c
create mode 100644 lldb/test/API/functionalities/disassembler-variables/d_original_example.s
delete mode 100644 lldb/test/API/functionalities/disassembler-variables/live_across_call.c
create mode 100644 lldb/test/API/functionalities/disassembler-variables/live_across_call.s
delete mode 100644 lldb/test/API/functionalities/disassembler-variables/loop_reg_rotate.c
create mode 100644 lldb/test/API/functionalities/disassembler-variables/loop_reg_rotate.s
delete mode 100644 lldb/test/API/functionalities/disassembler-variables/regs_fp_params.c
create mode 100644 lldb/test/API/functionalities/disassembler-variables/regs_fp_params.s
delete mode 100644 lldb/test/API/functionalities/disassembler-variables/regs_int_params.c
create mode 100644 lldb/test/API/functionalities/disassembler-variables/regs_int_params.s
delete mode 100644 lldb/test/API/functionalities/disassembler-variables/regs_mixed_params.c
create mode 100644 lldb/test/API/functionalities/disassembler-variables/regs_mixed_params.s
delete mode 100644 lldb/test/API/functionalities/disassembler-variables/seed_reg_const_undef.c
create mode 100644 lldb/test/API/functionalities/disassembler-variables/seed_reg_const_undef.s
diff --git a/lldb/test/API/functionalities/disassembler-variables/TestRichDisassembler.py b/lldb/test/API/functionalities/disassembler-variables/TestRichDisassembler.py
index 05aba06149030..14d9ab5bf11ed 100644
--- a/lldb/test/API/functionalities/disassembler-variables/TestRichDisassembler.py
+++ b/lldb/test/API/functionalities/disassembler-variables/TestRichDisassembler.py
@@ -18,16 +18,34 @@ def _compile_object(self, src_name, func_cflags="-g -gdwarf-5 -O2 -fno-inline"):
self.assertTrue(os.path.exists(obj), f"missing object: {obj}")
return obj
+ def _compile_or_assemble_object(
+ self,
+ src_name,
+ cflags="-g -gdwarf-5 -O2 -fno-inline -fno-omit-frame-pointer",
+ ):
+ cc = self.getCompiler() # use the configured clang from dotest
+ src = self.getSourcePath(src_name)
+ stem, ext = os.path.splitext(src_name)
+ obj = self.getBuildArtifact(stem + ".o")
+
+ if ext == ".s":
+ # Assemble the already-generated DWARF-rich .s
+ cmd = f"'{cc}' -c -x assembler '{src}' -o '{obj}'"
+ else:
+ # Fallback for .c if needed
+ cmd = f"'{cc}' {cflags} -c '{src}' -o '{obj}'"
+
+ # run and validate
+ self.runCmd(f"platform shell {cmd}", check=True)
+ self.assertTrue(os.path.exists(obj), f"missing object: {obj}")
+ return obj
+
+
def _create_target(self, path):
target = self.dbg.CreateTarget(path)
self.assertTrue(target, f"failed to create target for {path}")
return target
- def _disassemble_verbose_frame(self):
- # Same as your original: current frame (-f), with --variable enabled.
- self.runCmd("disassemble --variable -f", check=True)
- return self.res.GetOutput()
-
def _disassemble_verbose_symbol(self, symname):
# For object-only tests, disassemble a named symbol from the .o
self.runCmd(f"disassemble -n {symname} -v", check=True)
@@ -38,23 +56,10 @@ def test_d_original_example_O1(self):
Tests disassembler output for d_original_example.c built with -O1,
using the CLI with --rich for enabled annotations.
"""
- self.build(
- dictionary={"C_SOURCES": "d_original_example.c", "CFLAGS_EXTRAS": "-g -O1"}
- )
- exe = self.getBuildArtifact("a.out")
- target = self.dbg.CreateTarget(exe)
- self.assertTrue(target)
-
- bp = target.BreakpointCreateByName("main")
- self.assertGreater(bp.GetNumLocations(), 0)
-
- process = target.LaunchSimple(None, None, self.get_process_working_directory())
- self.assertTrue(process, "Failed to launch process")
- self.assertEqual(process.GetState(), lldb.eStateStopped)
-
- # Run the CLI command and read output from self.res
- self.runCmd("disassemble --variable -f", check=True)
- out = self.res.GetOutput()
+ obj = self._compile_or_assemble_object("d_original_example.s")
+ target = self._create_target(obj)
+ out = self._disassemble_verbose_symbol("main")
+
print(out)
self.assertIn("argc = ", out)
@@ -64,7 +69,7 @@ def test_d_original_example_O1(self):
@no_debug_info_test # we explicitly request -g in _compile_object
def test_regs_int_params(self):
- obj = self._compile_object("regs_int_params.c")
+ obj = self._compile_or_assemble_object("regs_int_params.s")
target = self._create_target(obj)
out = self._disassemble_verbose_symbol("regs_int_params")
print(out)
@@ -81,7 +86,7 @@ def test_regs_int_params(self):
@no_debug_info_test
def test_regs_fp_params(self):
- obj = self._compile_object("regs_fp_params.c")
+ obj = self._compile_or_assemble_object("regs_fp_params.s")
target = self._create_target(obj)
out = self._disassemble_verbose_symbol("regs_fp_params")
print(out)
@@ -97,7 +102,7 @@ def test_regs_fp_params(self):
@no_debug_info_test
def test_regs_mixed_params(self):
- obj = self._compile_object("regs_mixed_params.c")
+ obj = self._compile_or_assemble_object("regs_mixed_params.s")
target = self._create_target(obj)
out = self._disassemble_verbose_symbol("regs_mixed_params")
print(out)
@@ -112,7 +117,7 @@ def test_regs_mixed_params(self):
@no_debug_info_test
def test_live_across_call(self):
- obj = self._compile_object("live_across_call.c")
+ obj = self._compile_or_assemble_object("live_across_call.s")
target = self._create_target(obj)
out = self._disassemble_verbose_symbol("live_across_call")
print(out)
@@ -125,13 +130,13 @@ def test_live_across_call(self):
@no_debug_info_test
def test_loop_reg_rotate(self):
- obj = self._compile_object("loop_reg_rotate.c")
+ obj = self._compile_or_assemble_object("loop_reg_rotate.s")
target = self._create_target(obj)
out = self._disassemble_verbose_symbol("loop_reg_rotate")
print(out)
self.assertRegex(out, r"\bn\s*=\s*()")
- self.assertRegex(out, r"\bt\s*=\s*()")
+ self.assertRegex(out, r"\bseed\s*=\s*()")
self.assertRegex(out, r"\bk\s*=\s*()")
self.assertRegex(out, r"\bj\s*=\s*()")
self.assertRegex(out, r"\bi\s*=\s*()")
@@ -145,8 +150,8 @@ def test_seed_reg_const_undef(self):
can add an assertion for ' = 0' or similar.
"""
# Use O1 to help keep a first reg range; still object-only
- obj = self._compile_object("seed_reg_const_undef.c",
- func_cflags="-g -gdwarf-5 -O1 -fno-inline")
+ obj = self._compile_or_assemble_object("seed_reg_const_undef.s",
+ cflags="-g -gdwarf-5 -O1 -fno-inline")
target = self._create_target(obj)
out = self._disassemble_verbose_symbol("main")
print(out)
diff --git a/lldb/test/API/functionalities/disassembler-variables/d_original_example.c b/lldb/test/API/functionalities/disassembler-variables/d_original_example.c
deleted file mode 100644
index 1c864753a0220..0000000000000
--- a/lldb/test/API/functionalities/disassembler-variables/d_original_example.c
+++ /dev/null
@@ -1,7 +0,0 @@
-#include <stdio.h>
-
-int main(int argc, char **argv) {
- for (int i = 1; i < argc; ++i)
- puts(argv[i]);
- return 0;
-}
diff --git a/lldb/test/API/functionalities/disassembler-variables/d_original_example.s b/lldb/test/API/functionalities/disassembler-variables/d_original_example.s
new file mode 100644
index 0000000000000..c38742cfc683e
--- /dev/null
+++ b/lldb/test/API/functionalities/disassembler-variables/d_original_example.s
@@ -0,0 +1,461 @@
+/* Original C (for context):
+* #include <stdio.h>
+*
+* int main(int argc, char **argv) {
+* for (int i = 1; i < argc; ++i)
+* puts(argv[i]);
+* return 0;
+* }
+*/
+ .file "d_original_example.c"
+ .text
+ .globl main # -- Begin function main
+ .p2align 4
+ .type main, at function
+main: # @main
+.Lfunc_begin0:
+ .file 0 "." "d_original_example.c" md5 0x25192a1d5a6018cf37d369f9004fab00
+ .cfi_startproc
+# %bb.0: # %entry
+ #DEBUG_VALUE: main:argc <- $edi
+ #DEBUG_VALUE: main:argv <- $rsi
+ #DEBUG_VALUE: i <- 1
+ .loc 0 4 21 prologue_end # d_original_example.c:4:21
+ cmpl $2, %edi
+.Ltmp0:
+ .loc 0 4 3 is_stmt 0 # d_original_example.c:4:3
+ jl .LBB0_4
+.Ltmp1:
+# %bb.1: # %for.body.preheader
+ #DEBUG_VALUE: main:argc <- $edi
+ #DEBUG_VALUE: main:argv <- $rsi
+ #DEBUG_VALUE: i <- 1
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ pushq %r15
+ pushq %r14
+ pushq %rbx
+ pushq %rax
+ .cfi_offset %rbx, -40
+ .cfi_offset %r14, -32
+ .cfi_offset %r15, -24
+ movq %rsi, %rbx
+.Ltmp2:
+ .loc 0 4 21 # d_original_example.c:4:21
+ movl %edi, %r14d
+.Ltmp3:
+ #DEBUG_VALUE: main:argc <- $r14d
+ .loc 0 0 21 # d_original_example.c:0:21
+ movl $1, %r15d
+.Ltmp4:
+ .p2align 4
+.LBB0_2: # %for.body
+ # =>This Inner Loop Header: Depth=1
+ #DEBUG_VALUE: main:argc <- $r14d
+ #DEBUG_VALUE: main:argv <- $rbx
+ #DEBUG_VALUE: i <- $r15
+ .loc 0 5 10 is_stmt 1 # d_original_example.c:5:10
+ movq (%rbx,%r15,8), %rdi
+ .loc 0 5 5 is_stmt 0 # d_original_example.c:5:5
+ callq puts at PLT
+.Ltmp5:
+ .loc 0 4 29 is_stmt 1 # d_original_example.c:4:29
+ incq %r15
+.Ltmp6:
+ #DEBUG_VALUE: i <- $r15
+ .loc 0 4 21 is_stmt 0 # d_original_example.c:4:21
+ cmpq %r15, %r14
+.Ltmp7:
+ .loc 0 4 3 # d_original_example.c:4:3
+ jne .LBB0_2
+.Ltmp8:
+# %bb.3:
+ #DEBUG_VALUE: main:argc <- $r14d
+ #DEBUG_VALUE: main:argv <- $rbx
+ #DEBUG_VALUE: i <- $r15
+ .loc 0 0 3 # d_original_example.c:0:3
+ addq $8, %rsp
+ popq %rbx
+.Ltmp9:
+ #DEBUG_VALUE: main:argv <- [DW_OP_LLVM_entry_value 1] $rsi
+ popq %r14
+.Ltmp10:
+ #DEBUG_VALUE: main:argc <- [DW_OP_LLVM_entry_value 1] $edi
+ popq %r15
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ .cfi_restore %rbx
+ .cfi_restore %r14
+ .cfi_restore %r15
+ .cfi_restore %rbp
+.Ltmp11:
+.LBB0_4: # %for.cond.cleanup
+ #DEBUG_VALUE: main:argc <- [DW_OP_LLVM_entry_value 1] $edi
+ #DEBUG_VALUE: main:argv <- [DW_OP_LLVM_entry_value 1] $rsi
+ .loc 0 6 3 is_stmt 1 # d_original_example.c:6:3
+ xorl %eax, %eax
+ retq
+.Ltmp12:
+.Lfunc_end0:
+ .size main, .Lfunc_end0-main
+ .cfi_endproc
+ .file 1 "/usr/include" "stdio.h" md5 0xf31eefcc3f15835fc5a4023a625cf609
+ # -- End function
+ .section .debug_loclists,"", at progbits
+ .long .Ldebug_list_header_end0-.Ldebug_list_header_start0 # Length
+.Ldebug_list_header_start0:
+ .short 5 # Version
+ .byte 8 # Address size
+ .byte 0 # Segment selector size
+ .long 3 # Offset entry count
+.Lloclists_table_base0:
+ .long .Ldebug_loc0-.Lloclists_table_base0
+ .long .Ldebug_loc1-.Lloclists_table_base0
+ .long .Ldebug_loc2-.Lloclists_table_base0
+.Ldebug_loc0:
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Lfunc_begin0-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp3-.Lfunc_begin0 # ending offset
+ .byte 1 # Loc expr size
+ .byte 85 # super-register DW_OP_reg5
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp3-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp10-.Lfunc_begin0 # ending offset
+ .byte 1 # Loc expr size
+ .byte 94 # super-register DW_OP_reg14
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp10-.Lfunc_begin0 # starting offset
+ .uleb128 .Lfunc_end0-.Lfunc_begin0 # ending offset
+ .byte 4 # Loc expr size
+ .byte 163 # DW_OP_entry_value
+ .byte 1 # 1
+ .byte 85 # super-register DW_OP_reg5
+ .byte 159 # DW_OP_stack_value
+ .byte 0 # DW_LLE_end_of_list
+.Ldebug_loc1:
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Lfunc_begin0-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp4-.Lfunc_begin0 # ending offset
+ .byte 1 # Loc expr size
+ .byte 84 # DW_OP_reg4
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp4-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp9-.Lfunc_begin0 # ending offset
+ .byte 1 # Loc expr size
+ .byte 83 # DW_OP_reg3
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp9-.Lfunc_begin0 # starting offset
+ .uleb128 .Lfunc_end0-.Lfunc_begin0 # ending offset
+ .byte 4 # Loc expr size
+ .byte 163 # DW_OP_entry_value
+ .byte 1 # 1
+ .byte 84 # DW_OP_reg4
+ .byte 159 # DW_OP_stack_value
+ .byte 0 # DW_LLE_end_of_list
+.Ldebug_loc2:
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Lfunc_begin0-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp4-.Lfunc_begin0 # ending offset
+ .byte 3 # Loc expr size
+ .byte 17 # DW_OP_consts
+ .byte 1 # 1
+ .byte 159 # DW_OP_stack_value
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp4-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp8-.Lfunc_begin0 # ending offset
+ .byte 1 # Loc expr size
+ .byte 95 # DW_OP_reg15
+ .byte 0 # DW_LLE_end_of_list
+.Ldebug_list_header_end0:
+ .section .debug_abbrev,"", at progbits
+ .byte 1 # Abbreviation Code
+ .byte 17 # DW_TAG_compile_unit
+ .byte 1 # DW_CHILDREN_yes
+ .byte 37 # DW_AT_producer
+ .byte 37 # DW_FORM_strx1
+ .byte 19 # DW_AT_language
+ .byte 5 # DW_FORM_data2
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 114 # DW_AT_str_offsets_base
+ .byte 23 # DW_FORM_sec_offset
+ .byte 16 # DW_AT_stmt_list
+ .byte 23 # DW_FORM_sec_offset
+ .byte 27 # DW_AT_comp_dir
+ .byte 37 # DW_FORM_strx1
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 115 # DW_AT_addr_base
+ .byte 23 # DW_FORM_sec_offset
+ .ascii "\214\001" # DW_AT_loclists_base
+ .byte 23 # DW_FORM_sec_offset
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 2 # Abbreviation Code
+ .byte 46 # DW_TAG_subprogram
+ .byte 1 # DW_CHILDREN_yes
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 64 # DW_AT_frame_base
+ .byte 24 # DW_FORM_exprloc
+ .byte 122 # DW_AT_call_all_calls
+ .byte 25 # DW_FORM_flag_present
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 39 # DW_AT_prototyped
+ .byte 25 # DW_FORM_flag_present
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 63 # DW_AT_external
+ .byte 25 # DW_FORM_flag_present
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 3 # Abbreviation Code
+ .byte 5 # DW_TAG_formal_parameter
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 34 # DW_FORM_loclistx
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 4 # Abbreviation Code
+ .byte 11 # DW_TAG_lexical_block
+ .byte 1 # DW_CHILDREN_yes
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 5 # Abbreviation Code
+ .byte 52 # DW_TAG_variable
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 34 # DW_FORM_loclistx
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 6 # Abbreviation Code
+ .byte 72 # DW_TAG_call_site
+ .byte 0 # DW_CHILDREN_no
+ .byte 127 # DW_AT_call_origin
+ .byte 19 # DW_FORM_ref4
+ .byte 125 # DW_AT_call_return_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 7 # Abbreviation Code
+ .byte 46 # DW_TAG_subprogram
+ .byte 1 # DW_CHILDREN_yes
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 5 # DW_FORM_data2
+ .byte 39 # DW_AT_prototyped
+ .byte 25 # DW_FORM_flag_present
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 60 # DW_AT_declaration
+ .byte 25 # DW_FORM_flag_present
+ .byte 63 # DW_AT_external
+ .byte 25 # DW_FORM_flag_present
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 8 # Abbreviation Code
+ .byte 5 # DW_TAG_formal_parameter
+ .byte 0 # DW_CHILDREN_no
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 9 # Abbreviation Code
+ .byte 36 # DW_TAG_base_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 62 # DW_AT_encoding
+ .byte 11 # DW_FORM_data1
+ .byte 11 # DW_AT_byte_size
+ .byte 11 # DW_FORM_data1
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 10 # Abbreviation Code
+ .byte 15 # DW_TAG_pointer_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 11 # Abbreviation Code
+ .byte 38 # DW_TAG_const_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 0 # EOM(3)
+ .section .debug_info,"", at progbits
+.Lcu_begin0:
+ .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit
+.Ldebug_info_start0:
+ .short 5 # DWARF version number
+ .byte 1 # DWARF Unit Type
+ .byte 8 # Address Size (in bytes)
+ .long .debug_abbrev # Offset Into Abbrev. Section
+ .byte 1 # Abbrev [1] 0xc:0x7f DW_TAG_compile_unit
+ .byte 0 # DW_AT_producer
+ .short 29 # DW_AT_language
+ .byte 1 # DW_AT_name
+ .long .Lstr_offsets_base0 # DW_AT_str_offsets_base
+ .long .Lline_table_start0 # DW_AT_stmt_list
+ .byte 2 # DW_AT_comp_dir
+ .byte 0 # DW_AT_low_pc
+ .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+ .long .Laddr_table_base0 # DW_AT_addr_base
+ .long .Lloclists_table_base0 # DW_AT_loclists_base
+ .byte 2 # Abbrev [2] 0x27:0x38 DW_TAG_subprogram
+ .byte 0 # DW_AT_low_pc
+ .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+ .byte 1 # DW_AT_frame_base
+ .byte 86
+ # DW_AT_call_all_calls
+ .byte 6 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 3 # DW_AT_decl_line
+ # DW_AT_prototyped
+ .long 110 # DW_AT_type
+ # DW_AT_external
+ .byte 3 # Abbrev [3] 0x36:0x9 DW_TAG_formal_parameter
+ .byte 0 # DW_AT_location
+ .byte 7 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 3 # DW_AT_decl_line
+ .long 110 # DW_AT_type
+ .byte 3 # Abbrev [3] 0x3f:0x9 DW_TAG_formal_parameter
+ .byte 1 # DW_AT_location
+ .byte 8 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 3 # DW_AT_decl_line
+ .long 128 # DW_AT_type
+ .byte 4 # Abbrev [4] 0x48:0x10 DW_TAG_lexical_block
+ .byte 0 # DW_AT_low_pc
+ .long .Ltmp8-.Lfunc_begin0 # DW_AT_high_pc
+ .byte 5 # Abbrev [5] 0x4e:0x9 DW_TAG_variable
+ .byte 2 # DW_AT_location
+ .byte 9 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 4 # DW_AT_decl_line
+ .long 110 # DW_AT_type
+ .byte 0 # End Of Children Mark
+ .byte 6 # Abbrev [6] 0x58:0x6 DW_TAG_call_site
+ .long 95 # DW_AT_call_origin
+ .byte 1 # DW_AT_call_return_pc
+ .byte 0 # End Of Children Mark
+ .byte 7 # Abbrev [7] 0x5f:0xf DW_TAG_subprogram
+ .byte 3 # DW_AT_name
+ .byte 1 # DW_AT_decl_file
+ .short 661 # DW_AT_decl_line
+ # DW_AT_prototyped
+ .long 110 # DW_AT_type
+ # DW_AT_declaration
+ # DW_AT_external
+ .byte 8 # Abbrev [8] 0x68:0x5 DW_TAG_formal_parameter
+ .long 114 # DW_AT_type
+ .byte 0 # End Of Children Mark
+ .byte 9 # Abbrev [9] 0x6e:0x4 DW_TAG_base_type
+ .byte 4 # DW_AT_name
+ .byte 5 # DW_AT_encoding
+ .byte 4 # DW_AT_byte_size
+ .byte 10 # Abbrev [10] 0x72:0x5 DW_TAG_pointer_type
+ .long 119 # DW_AT_type
+ .byte 11 # Abbrev [11] 0x77:0x5 DW_TAG_const_type
+ .long 124 # DW_AT_type
+ .byte 9 # Abbrev [9] 0x7c:0x4 DW_TAG_base_type
+ .byte 5 # DW_AT_name
+ .byte 6 # DW_AT_encoding
+ .byte 1 # DW_AT_byte_size
+ .byte 10 # Abbrev [10] 0x80:0x5 DW_TAG_pointer_type
+ .long 133 # DW_AT_type
+ .byte 10 # Abbrev [10] 0x85:0x5 DW_TAG_pointer_type
+ .long 124 # DW_AT_type
+ .byte 0 # End Of Children Mark
+.Ldebug_info_end0:
+ .section .debug_str_offsets,"", at progbits
+ .long 44 # Length of String Offsets Set
+ .short 5
+ .short 0
+.Lstr_offsets_base0:
+ .section .debug_str,"MS", at progbits,1
+.Linfo_string0:
+ .asciz "clang version 22.0.0git (https://github.com/UltimateForce21/llvm-project.git 79c0a9e1e7da0f727c41d27c9c6ff8a28bb7d06f)" # string offset=0
+.Linfo_string1:
+ .asciz "d_original_example.c" # string offset=119
+.Linfo_string2:
+ .asciz "." # string offset=140
+.Linfo_string3:
+ .asciz "puts" # string offset=142
+.Linfo_string4:
+ .asciz "int" # string offset=147
+.Linfo_string5:
+ .asciz "char" # string offset=151
+.Linfo_string6:
+ .asciz "main" # string offset=156
+.Linfo_string7:
+ .asciz "argc" # string offset=161
+.Linfo_string8:
+ .asciz "argv" # string offset=166
+.Linfo_string9:
+ .asciz "i" # string offset=171
+ .section .debug_str_offsets,"", at progbits
+ .long .Linfo_string0
+ .long .Linfo_string1
+ .long .Linfo_string2
+ .long .Linfo_string3
+ .long .Linfo_string4
+ .long .Linfo_string5
+ .long .Linfo_string6
+ .long .Linfo_string7
+ .long .Linfo_string8
+ .long .Linfo_string9
+ .section .debug_addr,"", at progbits
+ .long .Ldebug_addr_end0-.Ldebug_addr_start0 # Length of contribution
+.Ldebug_addr_start0:
+ .short 5 # DWARF version number
+ .byte 8 # Address size
+ .byte 0 # Segment selector size
+.Laddr_table_base0:
+ .quad .Lfunc_begin0
+ .quad .Ltmp5
+.Ldebug_addr_end0:
+ .ident "clang version 22.0.0git (https://github.com/UltimateForce21/llvm-project.git 79c0a9e1e7da0f727c41d27c9c6ff8a28bb7d06f)"
+ .section ".note.GNU-stack","", at progbits
+ .addrsig
+ .section .debug_line,"", at progbits
+.Lline_table_start0:
diff --git a/lldb/test/API/functionalities/disassembler-variables/live_across_call.c b/lldb/test/API/functionalities/disassembler-variables/live_across_call.c
deleted file mode 100644
index 53de9247c7f10..0000000000000
--- a/lldb/test/API/functionalities/disassembler-variables/live_across_call.c
+++ /dev/null
@@ -1,11 +0,0 @@
-// Declare a real external call so the compiler must respect ABI clobbers.
-extern int leaf(int) __attribute__((noinline));
-
-__attribute__((noinline))
-int live_across_call(int x) {
- volatile int a = x; // a starts in a GPR (from arg)
- asm volatile("" :: "r"(a)); // keep 'a' live in a register
- int r = leaf(a); // 'a' is live across the call
- asm volatile("" :: "r"(a), "r"(r));// still live afterwards
- return a + r;
-}
diff --git a/lldb/test/API/functionalities/disassembler-variables/live_across_call.s b/lldb/test/API/functionalities/disassembler-variables/live_across_call.s
new file mode 100644
index 0000000000000..cd9f08afe6fdc
--- /dev/null
+++ b/lldb/test/API/functionalities/disassembler-variables/live_across_call.s
@@ -0,0 +1,371 @@
+/* Original C (for context):
+* // Declare a real external call so the compiler must respect ABI clobbers.
+* extern int leaf(int) __attribute__((noinline));
+*
+* __attribute__((noinline))
+* int live_across_call(int x) {
+* volatile int a = x; // a starts in a GPR (from arg)
+* asm volatile("" :: "r"(a)); // keep 'a' live in a register
+* int r = leaf(a); // 'a' is live across the call
+* asm volatile("" :: "r"(a), "r"(r));// still live afterwards
+* return a + r;
+* }
+*/
+ .file "live_across_call.c"
+ .text
+ .globl live_across_call # -- Begin function live_across_call
+ .p2align 4
+ .type live_across_call, at function
+live_across_call: # @live_across_call
+.Lfunc_begin0:
+ .file 0 "." "live_across_call.c" md5 0x351c37295026edf0d468774d35f47e5e
+ .loc 0 5 0 # live_across_call.c:5:0
+ .cfi_startproc
+# %bb.0: # %entry
+ #DEBUG_VALUE: live_across_call:x <- $edi
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ subq $16, %rsp
+.Ltmp0:
+ .loc 0 6 16 prologue_end # live_across_call.c:6:16
+ movl %edi, -4(%rbp)
+ .loc 0 7 26 # live_across_call.c:7:26
+ movl -4(%rbp), %eax
+ .loc 0 7 3 is_stmt 0 # live_across_call.c:7:3
+ #APP
+ #NO_APP
+ .loc 0 8 16 is_stmt 1 # live_across_call.c:8:16
+ movl -4(%rbp), %edi
+.Ltmp1:
+ #DEBUG_VALUE: live_across_call:x <- [DW_OP_LLVM_entry_value 1] $edi
+ .loc 0 8 11 is_stmt 0 # live_across_call.c:8:11
+ callq leaf at PLT
+.Ltmp2:
+ #DEBUG_VALUE: live_across_call:r <- $eax
+ .loc 0 9 26 is_stmt 1 # live_across_call.c:9:26
+ movl -4(%rbp), %ecx
+ .loc 0 9 3 is_stmt 0 # live_across_call.c:9:3
+ #APP
+ #NO_APP
+ .loc 0 10 12 is_stmt 1 # live_across_call.c:10:12
+ addl -4(%rbp), %eax
+.Ltmp3:
+ .loc 0 10 3 epilogue_begin is_stmt 0 # live_across_call.c:10:3
+ addq $16, %rsp
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Ltmp4:
+.Lfunc_end0:
+ .size live_across_call, .Lfunc_end0-live_across_call
+ .cfi_endproc
+ # -- End function
+ .section .debug_loclists,"", at progbits
+ .long .Ldebug_list_header_end0-.Ldebug_list_header_start0 # Length
+.Ldebug_list_header_start0:
+ .short 5 # Version
+ .byte 8 # Address size
+ .byte 0 # Segment selector size
+ .long 2 # Offset entry count
+.Lloclists_table_base0:
+ .long .Ldebug_loc0-.Lloclists_table_base0
+ .long .Ldebug_loc1-.Lloclists_table_base0
+.Ldebug_loc0:
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Lfunc_begin0-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp1-.Lfunc_begin0 # ending offset
+ .byte 1 # Loc expr size
+ .byte 85 # super-register DW_OP_reg5
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp1-.Lfunc_begin0 # starting offset
+ .uleb128 .Lfunc_end0-.Lfunc_begin0 # ending offset
+ .byte 4 # Loc expr size
+ .byte 163 # DW_OP_entry_value
+ .byte 1 # 1
+ .byte 85 # super-register DW_OP_reg5
+ .byte 159 # DW_OP_stack_value
+ .byte 0 # DW_LLE_end_of_list
+.Ldebug_loc1:
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp2-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp3-.Lfunc_begin0 # ending offset
+ .byte 1 # Loc expr size
+ .byte 80 # super-register DW_OP_reg0
+ .byte 0 # DW_LLE_end_of_list
+.Ldebug_list_header_end0:
+ .section .debug_abbrev,"", at progbits
+ .byte 1 # Abbreviation Code
+ .byte 17 # DW_TAG_compile_unit
+ .byte 1 # DW_CHILDREN_yes
+ .byte 37 # DW_AT_producer
+ .byte 37 # DW_FORM_strx1
+ .byte 19 # DW_AT_language
+ .byte 5 # DW_FORM_data2
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 114 # DW_AT_str_offsets_base
+ .byte 23 # DW_FORM_sec_offset
+ .byte 16 # DW_AT_stmt_list
+ .byte 23 # DW_FORM_sec_offset
+ .byte 27 # DW_AT_comp_dir
+ .byte 37 # DW_FORM_strx1
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 115 # DW_AT_addr_base
+ .byte 23 # DW_FORM_sec_offset
+ .ascii "\214\001" # DW_AT_loclists_base
+ .byte 23 # DW_FORM_sec_offset
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 2 # Abbreviation Code
+ .byte 46 # DW_TAG_subprogram
+ .byte 1 # DW_CHILDREN_yes
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 64 # DW_AT_frame_base
+ .byte 24 # DW_FORM_exprloc
+ .byte 122 # DW_AT_call_all_calls
+ .byte 25 # DW_FORM_flag_present
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 39 # DW_AT_prototyped
+ .byte 25 # DW_FORM_flag_present
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 63 # DW_AT_external
+ .byte 25 # DW_FORM_flag_present
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 3 # Abbreviation Code
+ .byte 5 # DW_TAG_formal_parameter
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 34 # DW_FORM_loclistx
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 4 # Abbreviation Code
+ .byte 52 # DW_TAG_variable
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 24 # DW_FORM_exprloc
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 5 # Abbreviation Code
+ .byte 52 # DW_TAG_variable
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 34 # DW_FORM_loclistx
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 6 # Abbreviation Code
+ .byte 72 # DW_TAG_call_site
+ .byte 0 # DW_CHILDREN_no
+ .byte 127 # DW_AT_call_origin
+ .byte 19 # DW_FORM_ref4
+ .byte 125 # DW_AT_call_return_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 7 # Abbreviation Code
+ .byte 46 # DW_TAG_subprogram
+ .byte 1 # DW_CHILDREN_yes
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 39 # DW_AT_prototyped
+ .byte 25 # DW_FORM_flag_present
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 60 # DW_AT_declaration
+ .byte 25 # DW_FORM_flag_present
+ .byte 63 # DW_AT_external
+ .byte 25 # DW_FORM_flag_present
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 8 # Abbreviation Code
+ .byte 5 # DW_TAG_formal_parameter
+ .byte 0 # DW_CHILDREN_no
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 9 # Abbreviation Code
+ .byte 36 # DW_TAG_base_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 62 # DW_AT_encoding
+ .byte 11 # DW_FORM_data1
+ .byte 11 # DW_AT_byte_size
+ .byte 11 # DW_FORM_data1
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 10 # Abbreviation Code
+ .byte 53 # DW_TAG_volatile_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 0 # EOM(3)
+ .section .debug_info,"", at progbits
+.Lcu_begin0:
+ .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit
+.Ldebug_info_start0:
+ .short 5 # DWARF version number
+ .byte 1 # DWARF Unit Type
+ .byte 8 # Address Size (in bytes)
+ .long .debug_abbrev # Offset Into Abbrev. Section
+ .byte 1 # Abbrev [1] 0xc:0x66 DW_TAG_compile_unit
+ .byte 0 # DW_AT_producer
+ .short 29 # DW_AT_language
+ .byte 1 # DW_AT_name
+ .long .Lstr_offsets_base0 # DW_AT_str_offsets_base
+ .long .Lline_table_start0 # DW_AT_stmt_list
+ .byte 2 # DW_AT_comp_dir
+ .byte 0 # DW_AT_low_pc
+ .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+ .long .Laddr_table_base0 # DW_AT_addr_base
+ .long .Lloclists_table_base0 # DW_AT_loclists_base
+ .byte 2 # Abbrev [2] 0x27:0x33 DW_TAG_subprogram
+ .byte 0 # DW_AT_low_pc
+ .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+ .byte 1 # DW_AT_frame_base
+ .byte 86
+ # DW_AT_call_all_calls
+ .byte 5 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 5 # DW_AT_decl_line
+ # DW_AT_prototyped
+ .long 104 # DW_AT_type
+ # DW_AT_external
+ .byte 3 # Abbrev [3] 0x36:0x9 DW_TAG_formal_parameter
+ .byte 0 # DW_AT_location
+ .byte 7 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 5 # DW_AT_decl_line
+ .long 104 # DW_AT_type
+ .byte 4 # Abbrev [4] 0x3f:0xb DW_TAG_variable
+ .byte 2 # DW_AT_location
+ .byte 145
+ .byte 124
+ .byte 6 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 6 # DW_AT_decl_line
+ .long 108 # DW_AT_type
+ .byte 5 # Abbrev [5] 0x4a:0x9 DW_TAG_variable
+ .byte 1 # DW_AT_location
+ .byte 8 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 8 # DW_AT_decl_line
+ .long 104 # DW_AT_type
+ .byte 6 # Abbrev [6] 0x53:0x6 DW_TAG_call_site
+ .long 90 # DW_AT_call_origin
+ .byte 1 # DW_AT_call_return_pc
+ .byte 0 # End Of Children Mark
+ .byte 7 # Abbrev [7] 0x5a:0xe DW_TAG_subprogram
+ .byte 3 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ # DW_AT_prototyped
+ .long 104 # DW_AT_type
+ # DW_AT_declaration
+ # DW_AT_external
+ .byte 8 # Abbrev [8] 0x62:0x5 DW_TAG_formal_parameter
+ .long 104 # DW_AT_type
+ .byte 0 # End Of Children Mark
+ .byte 9 # Abbrev [9] 0x68:0x4 DW_TAG_base_type
+ .byte 4 # DW_AT_name
+ .byte 5 # DW_AT_encoding
+ .byte 4 # DW_AT_byte_size
+ .byte 10 # Abbrev [10] 0x6c:0x5 DW_TAG_volatile_type
+ .long 104 # DW_AT_type
+ .byte 0 # End Of Children Mark
+.Ldebug_info_end0:
+ .section .debug_str_offsets,"", at progbits
+ .long 40 # Length of String Offsets Set
+ .short 5
+ .short 0
+.Lstr_offsets_base0:
+ .section .debug_str,"MS", at progbits,1
+.Linfo_string0:
+ .asciz "clang version 22.0.0git (https://github.com/UltimateForce21/llvm-project.git 79c0a9e1e7da0f727c41d27c9c6ff8a28bb7d06f)" # string offset=0
+.Linfo_string1:
+ .asciz "live_across_call.c" # string offset=119
+.Linfo_string2:
+ .asciz "." # string offset=138
+.Linfo_string3:
+ .asciz "leaf" # string offset=140
+.Linfo_string4:
+ .asciz "int" # string offset=145
+.Linfo_string5:
+ .asciz "live_across_call" # string offset=149
+.Linfo_string6:
+ .asciz "a" # string offset=166
+.Linfo_string7:
+ .asciz "x" # string offset=168
+.Linfo_string8:
+ .asciz "r" # string offset=170
+ .section .debug_str_offsets,"", at progbits
+ .long .Linfo_string0
+ .long .Linfo_string1
+ .long .Linfo_string2
+ .long .Linfo_string3
+ .long .Linfo_string4
+ .long .Linfo_string5
+ .long .Linfo_string6
+ .long .Linfo_string7
+ .long .Linfo_string8
+ .section .debug_addr,"", at progbits
+ .long .Ldebug_addr_end0-.Ldebug_addr_start0 # Length of contribution
+.Ldebug_addr_start0:
+ .short 5 # DWARF version number
+ .byte 8 # Address size
+ .byte 0 # Segment selector size
+.Laddr_table_base0:
+ .quad .Lfunc_begin0
+ .quad .Ltmp2
+.Ldebug_addr_end0:
+ .ident "clang version 22.0.0git (https://github.com/UltimateForce21/llvm-project.git 79c0a9e1e7da0f727c41d27c9c6ff8a28bb7d06f)"
+ .section ".note.GNU-stack","", at progbits
+ .addrsig
+ .section .debug_line,"", at progbits
+.Lline_table_start0:
diff --git a/lldb/test/API/functionalities/disassembler-variables/loop_reg_rotate.c b/lldb/test/API/functionalities/disassembler-variables/loop_reg_rotate.c
deleted file mode 100644
index 84c1a18e11697..0000000000000
--- a/lldb/test/API/functionalities/disassembler-variables/loop_reg_rotate.c
+++ /dev/null
@@ -1,18 +0,0 @@
-__attribute__((noinline))
-int loop_reg_rotate(int n, int seed) {
- volatile int acc = seed; // keep as a named local
- int i = 0, j = 1, k = 2; // extra pressure but not enough to spill
-
- for (int t = 0; t < n; ++t) {
- // Mix uses so the allocator may reshuffle regs for 'acc'
- acc = acc + i;
- asm volatile("" :: "r"(acc)); // pin 'acc' live here
- acc = acc ^ j;
- asm volatile("" :: "r"(acc)); // and here
- acc = acc + k;
- i ^= acc; j += acc; k ^= j;
- }
-
- asm volatile("" :: "r"(acc));
- return acc + i + j + k;
-}
diff --git a/lldb/test/API/functionalities/disassembler-variables/loop_reg_rotate.s b/lldb/test/API/functionalities/disassembler-variables/loop_reg_rotate.s
new file mode 100644
index 0000000000000..c01e2b28fd2be
--- /dev/null
+++ b/lldb/test/API/functionalities/disassembler-variables/loop_reg_rotate.s
@@ -0,0 +1,557 @@
+/* Original C (for context):
+* __attribute__((noinline))
+* int loop_reg_rotate(int n, int seed) {
+* volatile int acc = seed; // keep as a named local
+* int i = 0, j = 1, k = 2; // extra pressure but not enough to spill
+*
+* for (int t = 0; t < n; ++t) {
+* // Mix uses so the allocator may reshuffle regs for 'acc'
+* acc = acc + i;
+* asm volatile("" :: "r"(acc)); // pin 'acc' live here
+* acc = acc ^ j;
+* asm volatile("" :: "r"(acc)); // and here
+* acc = acc + k;
+* i ^= acc; j += acc; k ^= j;
+* }
+*
+* asm volatile("" :: "r"(acc));
+* return acc + i + j + k;
+* }
+*/
+ .file "loop_reg_rotate.c"
+ .text
+ .globl loop_reg_rotate # -- Begin function loop_reg_rotate
+ .p2align 4
+ .type loop_reg_rotate, at function
+loop_reg_rotate: # @loop_reg_rotate
+.Lfunc_begin0:
+ .file 0 "." "loop_reg_rotate.c" md5 0x388f52de76e9442230e689fb9be1b4ef
+ .loc 0 2 0 # loop_reg_rotate.c:2:0
+ .cfi_startproc
+# %bb.0: # %entry
+ #DEBUG_VALUE: loop_reg_rotate:n <- $edi
+ #DEBUG_VALUE: loop_reg_rotate:seed <- $esi
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+.Ltmp0:
+ .loc 0 3 16 prologue_end # loop_reg_rotate.c:3:16
+ movl %esi, -4(%rbp)
+.Ltmp1:
+ #DEBUG_VALUE: loop_reg_rotate:i <- 0
+ #DEBUG_VALUE: loop_reg_rotate:j <- 1
+ #DEBUG_VALUE: loop_reg_rotate:k <- 2
+ #DEBUG_VALUE: t <- 0
+ .loc 0 6 21 # loop_reg_rotate.c:6:21
+ testl %edi, %edi
+.Ltmp2:
+ .loc 0 6 3 is_stmt 0 # loop_reg_rotate.c:6:3
+ jle .LBB0_1
+.Ltmp3:
+# %bb.3: # %for.body.preheader
+ #DEBUG_VALUE: loop_reg_rotate:n <- $edi
+ #DEBUG_VALUE: loop_reg_rotate:seed <- $esi
+ #DEBUG_VALUE: loop_reg_rotate:i <- 0
+ #DEBUG_VALUE: loop_reg_rotate:j <- 1
+ #DEBUG_VALUE: loop_reg_rotate:k <- 2
+ #DEBUG_VALUE: t <- 0
+ .loc 0 0 3 # loop_reg_rotate.c:0:3
+ xorl %eax, %eax
+ movl $1, %edx
+ movl $2, %ecx
+.Ltmp4:
+ .p2align 4
+.LBB0_4: # %for.body
+ # =>This Inner Loop Header: Depth=1
+ #DEBUG_VALUE: loop_reg_rotate:n <- [DW_OP_LLVM_entry_value 1] $edi
+ #DEBUG_VALUE: loop_reg_rotate:seed <- [DW_OP_LLVM_entry_value 1] $esi
+ #DEBUG_VALUE: t <- [DW_OP_LLVM_arg 0, DW_OP_LLVM_arg 1, DW_OP_minus, DW_OP_consts 18446744073709551615, DW_OP_div, DW_OP_stack_value] undef, undef
+ #DEBUG_VALUE: loop_reg_rotate:k <- $ecx
+ #DEBUG_VALUE: loop_reg_rotate:j <- $edx
+ #DEBUG_VALUE: loop_reg_rotate:i <- $eax
+ .loc 0 8 9 is_stmt 1 # loop_reg_rotate.c:8:9
+ addl %eax, -4(%rbp)
+ .loc 0 9 28 # loop_reg_rotate.c:9:28
+ movl -4(%rbp), %esi
+ .loc 0 9 5 is_stmt 0 # loop_reg_rotate.c:9:5
+ #APP
+ #NO_APP
+ .loc 0 10 9 is_stmt 1 # loop_reg_rotate.c:10:9
+ xorl %edx, -4(%rbp)
+ .loc 0 11 28 # loop_reg_rotate.c:11:28
+ movl -4(%rbp), %esi
+ .loc 0 11 5 is_stmt 0 # loop_reg_rotate.c:11:5
+ #APP
+ #NO_APP
+ .loc 0 12 9 is_stmt 1 # loop_reg_rotate.c:12:9
+ addl %ecx, -4(%rbp)
+ .loc 0 13 7 # loop_reg_rotate.c:13:7
+ xorl -4(%rbp), %eax
+.Ltmp5:
+ #DEBUG_VALUE: loop_reg_rotate:i <- $eax
+ .loc 0 13 17 is_stmt 0 # loop_reg_rotate.c:13:17
+ addl -4(%rbp), %edx
+.Ltmp6:
+ #DEBUG_VALUE: loop_reg_rotate:j <- $edx
+ .loc 0 13 27 # loop_reg_rotate.c:13:27
+ xorl %edx, %ecx
+.Ltmp7:
+ #DEBUG_VALUE: loop_reg_rotate:k <- $ecx
+ #DEBUG_VALUE: t <- [DW_OP_LLVM_arg 0, DW_OP_LLVM_arg 1, DW_OP_minus, DW_OP_consts 18446744073709551615, DW_OP_div, DW_OP_consts 1, DW_OP_plus, DW_OP_stack_value] undef, undef
+ .loc 0 6 21 is_stmt 1 # loop_reg_rotate.c:6:21
+ decl %edi
+.Ltmp8:
+ .loc 0 6 3 is_stmt 0 # loop_reg_rotate.c:6:3
+ jne .LBB0_4
+ jmp .LBB0_2
+.Ltmp9:
+.LBB0_1:
+ #DEBUG_VALUE: loop_reg_rotate:n <- $edi
+ #DEBUG_VALUE: loop_reg_rotate:seed <- $esi
+ #DEBUG_VALUE: loop_reg_rotate:i <- 0
+ #DEBUG_VALUE: loop_reg_rotate:j <- 1
+ #DEBUG_VALUE: loop_reg_rotate:k <- 2
+ #DEBUG_VALUE: t <- 0
+ .loc 0 0 3 # loop_reg_rotate.c:0:3
+ movl $2, %ecx
+ movl $1, %edx
+ xorl %eax, %eax
+.Ltmp10:
+.LBB0_2: # %for.cond.cleanup
+ #DEBUG_VALUE: loop_reg_rotate:n <- [DW_OP_LLVM_entry_value 1] $edi
+ #DEBUG_VALUE: loop_reg_rotate:seed <- [DW_OP_LLVM_entry_value 1] $esi
+ .loc 0 16 26 is_stmt 1 # loop_reg_rotate.c:16:26
+ movl -4(%rbp), %esi
+ .loc 0 16 3 is_stmt 0 # loop_reg_rotate.c:16:3
+ #APP
+ #NO_APP
+ .loc 0 17 14 is_stmt 1 # loop_reg_rotate.c:17:14
+ addl %edx, %eax
+ .loc 0 17 18 is_stmt 0 # loop_reg_rotate.c:17:18
+ addl %ecx, %eax
+ .loc 0 17 22 # loop_reg_rotate.c:17:22
+ addl -4(%rbp), %eax
+ .loc 0 17 3 epilogue_begin # loop_reg_rotate.c:17:3
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Ltmp11:
+.Lfunc_end0:
+ .size loop_reg_rotate, .Lfunc_end0-loop_reg_rotate
+ .cfi_endproc
+ # -- End function
+ .section .debug_loclists,"", at progbits
+ .long .Ldebug_list_header_end0-.Ldebug_list_header_start0 # Length
+.Ldebug_list_header_start0:
+ .short 5 # Version
+ .byte 8 # Address size
+ .byte 0 # Segment selector size
+ .long 6 # Offset entry count
+.Lloclists_table_base0:
+ .long .Ldebug_loc0-.Lloclists_table_base0
+ .long .Ldebug_loc1-.Lloclists_table_base0
+ .long .Ldebug_loc2-.Lloclists_table_base0
+ .long .Ldebug_loc3-.Lloclists_table_base0
+ .long .Ldebug_loc4-.Lloclists_table_base0
+ .long .Ldebug_loc5-.Lloclists_table_base0
+.Ldebug_loc0:
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Lfunc_begin0-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp4-.Lfunc_begin0 # ending offset
+ .byte 1 # Loc expr size
+ .byte 85 # super-register DW_OP_reg5
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp4-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp9-.Lfunc_begin0 # ending offset
+ .byte 4 # Loc expr size
+ .byte 163 # DW_OP_entry_value
+ .byte 1 # 1
+ .byte 85 # super-register DW_OP_reg5
+ .byte 159 # DW_OP_stack_value
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp9-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp10-.Lfunc_begin0 # ending offset
+ .byte 1 # Loc expr size
+ .byte 85 # super-register DW_OP_reg5
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp10-.Lfunc_begin0 # starting offset
+ .uleb128 .Lfunc_end0-.Lfunc_begin0 # ending offset
+ .byte 4 # Loc expr size
+ .byte 163 # DW_OP_entry_value
+ .byte 1 # 1
+ .byte 85 # super-register DW_OP_reg5
+ .byte 159 # DW_OP_stack_value
+ .byte 0 # DW_LLE_end_of_list
+.Ldebug_loc1:
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Lfunc_begin0-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp4-.Lfunc_begin0 # ending offset
+ .byte 1 # Loc expr size
+ .byte 84 # super-register DW_OP_reg4
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp4-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp9-.Lfunc_begin0 # ending offset
+ .byte 4 # Loc expr size
+ .byte 163 # DW_OP_entry_value
+ .byte 1 # 1
+ .byte 84 # super-register DW_OP_reg4
+ .byte 159 # DW_OP_stack_value
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp9-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp10-.Lfunc_begin0 # ending offset
+ .byte 1 # Loc expr size
+ .byte 84 # super-register DW_OP_reg4
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp10-.Lfunc_begin0 # starting offset
+ .uleb128 .Lfunc_end0-.Lfunc_begin0 # ending offset
+ .byte 4 # Loc expr size
+ .byte 163 # DW_OP_entry_value
+ .byte 1 # 1
+ .byte 84 # super-register DW_OP_reg4
+ .byte 159 # DW_OP_stack_value
+ .byte 0 # DW_LLE_end_of_list
+.Ldebug_loc2:
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp1-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp4-.Lfunc_begin0 # ending offset
+ .byte 3 # Loc expr size
+ .byte 17 # DW_OP_consts
+ .byte 0 # 0
+ .byte 159 # DW_OP_stack_value
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp4-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp9-.Lfunc_begin0 # ending offset
+ .byte 1 # Loc expr size
+ .byte 80 # super-register DW_OP_reg0
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp9-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp10-.Lfunc_begin0 # ending offset
+ .byte 3 # Loc expr size
+ .byte 17 # DW_OP_consts
+ .byte 0 # 0
+ .byte 159 # DW_OP_stack_value
+ .byte 0 # DW_LLE_end_of_list
+.Ldebug_loc3:
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp1-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp4-.Lfunc_begin0 # ending offset
+ .byte 3 # Loc expr size
+ .byte 17 # DW_OP_consts
+ .byte 1 # 1
+ .byte 159 # DW_OP_stack_value
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp4-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp9-.Lfunc_begin0 # ending offset
+ .byte 1 # Loc expr size
+ .byte 81 # super-register DW_OP_reg1
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp9-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp10-.Lfunc_begin0 # ending offset
+ .byte 3 # Loc expr size
+ .byte 17 # DW_OP_consts
+ .byte 1 # 1
+ .byte 159 # DW_OP_stack_value
+ .byte 0 # DW_LLE_end_of_list
+.Ldebug_loc4:
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp1-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp4-.Lfunc_begin0 # ending offset
+ .byte 3 # Loc expr size
+ .byte 17 # DW_OP_consts
+ .byte 2 # 2
+ .byte 159 # DW_OP_stack_value
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp4-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp9-.Lfunc_begin0 # ending offset
+ .byte 1 # Loc expr size
+ .byte 82 # super-register DW_OP_reg2
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp9-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp10-.Lfunc_begin0 # ending offset
+ .byte 3 # Loc expr size
+ .byte 17 # DW_OP_consts
+ .byte 2 # 2
+ .byte 159 # DW_OP_stack_value
+ .byte 0 # DW_LLE_end_of_list
+.Ldebug_loc5:
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp1-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp4-.Lfunc_begin0 # ending offset
+ .byte 3 # Loc expr size
+ .byte 17 # DW_OP_consts
+ .byte 0 # 0
+ .byte 159 # DW_OP_stack_value
+ .byte 0 # DW_LLE_end_of_list
+.Ldebug_list_header_end0:
+ .section .debug_abbrev,"", at progbits
+ .byte 1 # Abbreviation Code
+ .byte 17 # DW_TAG_compile_unit
+ .byte 1 # DW_CHILDREN_yes
+ .byte 37 # DW_AT_producer
+ .byte 37 # DW_FORM_strx1
+ .byte 19 # DW_AT_language
+ .byte 5 # DW_FORM_data2
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 114 # DW_AT_str_offsets_base
+ .byte 23 # DW_FORM_sec_offset
+ .byte 16 # DW_AT_stmt_list
+ .byte 23 # DW_FORM_sec_offset
+ .byte 27 # DW_AT_comp_dir
+ .byte 37 # DW_FORM_strx1
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 115 # DW_AT_addr_base
+ .byte 23 # DW_FORM_sec_offset
+ .ascii "\214\001" # DW_AT_loclists_base
+ .byte 23 # DW_FORM_sec_offset
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 2 # Abbreviation Code
+ .byte 46 # DW_TAG_subprogram
+ .byte 1 # DW_CHILDREN_yes
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 64 # DW_AT_frame_base
+ .byte 24 # DW_FORM_exprloc
+ .byte 122 # DW_AT_call_all_calls
+ .byte 25 # DW_FORM_flag_present
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 39 # DW_AT_prototyped
+ .byte 25 # DW_FORM_flag_present
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 63 # DW_AT_external
+ .byte 25 # DW_FORM_flag_present
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 3 # Abbreviation Code
+ .byte 5 # DW_TAG_formal_parameter
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 34 # DW_FORM_loclistx
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 4 # Abbreviation Code
+ .byte 52 # DW_TAG_variable
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 24 # DW_FORM_exprloc
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 5 # Abbreviation Code
+ .byte 52 # DW_TAG_variable
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 34 # DW_FORM_loclistx
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 6 # Abbreviation Code
+ .byte 11 # DW_TAG_lexical_block
+ .byte 1 # DW_CHILDREN_yes
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 7 # Abbreviation Code
+ .byte 36 # DW_TAG_base_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 62 # DW_AT_encoding
+ .byte 11 # DW_FORM_data1
+ .byte 11 # DW_AT_byte_size
+ .byte 11 # DW_FORM_data1
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 8 # Abbreviation Code
+ .byte 53 # DW_TAG_volatile_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 0 # EOM(3)
+ .section .debug_info,"", at progbits
+.Lcu_begin0:
+ .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit
+.Ldebug_info_start0:
+ .short 5 # DWARF version number
+ .byte 1 # DWARF Unit Type
+ .byte 8 # Address Size (in bytes)
+ .long .debug_abbrev # Offset Into Abbrev. Section
+ .byte 1 # Abbrev [1] 0xc:0x7d DW_TAG_compile_unit
+ .byte 0 # DW_AT_producer
+ .short 29 # DW_AT_language
+ .byte 1 # DW_AT_name
+ .long .Lstr_offsets_base0 # DW_AT_str_offsets_base
+ .long .Lline_table_start0 # DW_AT_stmt_list
+ .byte 2 # DW_AT_comp_dir
+ .byte 0 # DW_AT_low_pc
+ .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+ .long .Laddr_table_base0 # DW_AT_addr_base
+ .long .Lloclists_table_base0 # DW_AT_loclists_base
+ .byte 2 # Abbrev [2] 0x27:0x58 DW_TAG_subprogram
+ .byte 0 # DW_AT_low_pc
+ .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+ .byte 1 # DW_AT_frame_base
+ .byte 86
+ # DW_AT_call_all_calls
+ .byte 3 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ # DW_AT_prototyped
+ .long 127 # DW_AT_type
+ # DW_AT_external
+ .byte 3 # Abbrev [3] 0x36:0x9 DW_TAG_formal_parameter
+ .byte 0 # DW_AT_location
+ .byte 6 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 127 # DW_AT_type
+ .byte 3 # Abbrev [3] 0x3f:0x9 DW_TAG_formal_parameter
+ .byte 1 # DW_AT_location
+ .byte 7 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 127 # DW_AT_type
+ .byte 4 # Abbrev [4] 0x48:0xb DW_TAG_variable
+ .byte 2 # DW_AT_location
+ .byte 145
+ .byte 124
+ .byte 5 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 3 # DW_AT_decl_line
+ .long 131 # DW_AT_type
+ .byte 5 # Abbrev [5] 0x53:0x9 DW_TAG_variable
+ .byte 2 # DW_AT_location
+ .byte 8 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 4 # DW_AT_decl_line
+ .long 127 # DW_AT_type
+ .byte 5 # Abbrev [5] 0x5c:0x9 DW_TAG_variable
+ .byte 3 # DW_AT_location
+ .byte 9 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 4 # DW_AT_decl_line
+ .long 127 # DW_AT_type
+ .byte 5 # Abbrev [5] 0x65:0x9 DW_TAG_variable
+ .byte 4 # DW_AT_location
+ .byte 10 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 4 # DW_AT_decl_line
+ .long 127 # DW_AT_type
+ .byte 6 # Abbrev [6] 0x6e:0x10 DW_TAG_lexical_block
+ .byte 1 # DW_AT_low_pc
+ .long .Ltmp9-.Ltmp1 # DW_AT_high_pc
+ .byte 5 # Abbrev [5] 0x74:0x9 DW_TAG_variable
+ .byte 5 # DW_AT_location
+ .byte 11 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 6 # DW_AT_decl_line
+ .long 127 # DW_AT_type
+ .byte 0 # End Of Children Mark
+ .byte 0 # End Of Children Mark
+ .byte 7 # Abbrev [7] 0x7f:0x4 DW_TAG_base_type
+ .byte 4 # DW_AT_name
+ .byte 5 # DW_AT_encoding
+ .byte 4 # DW_AT_byte_size
+ .byte 8 # Abbrev [8] 0x83:0x5 DW_TAG_volatile_type
+ .long 127 # DW_AT_type
+ .byte 0 # End Of Children Mark
+.Ldebug_info_end0:
+ .section .debug_str_offsets,"", at progbits
+ .long 52 # Length of String Offsets Set
+ .short 5
+ .short 0
+.Lstr_offsets_base0:
+ .section .debug_str,"MS", at progbits,1
+.Linfo_string0:
+ .asciz "clang version 22.0.0git (https://github.com/UltimateForce21/llvm-project.git 79c0a9e1e7da0f727c41d27c9c6ff8a28bb7d06f)" # string offset=0
+.Linfo_string1:
+ .asciz "loop_reg_rotate.c" # string offset=119
+.Linfo_string2:
+ .asciz "." # string offset=137
+.Linfo_string3:
+ .asciz "loop_reg_rotate" # string offset=139
+.Linfo_string4:
+ .asciz "int" # string offset=155
+.Linfo_string5:
+ .asciz "acc" # string offset=159
+.Linfo_string6:
+ .asciz "n" # string offset=163
+.Linfo_string7:
+ .asciz "seed" # string offset=165
+.Linfo_string8:
+ .asciz "i" # string offset=170
+.Linfo_string9:
+ .asciz "j" # string offset=172
+.Linfo_string10:
+ .asciz "k" # string offset=174
+.Linfo_string11:
+ .asciz "t" # string offset=176
+ .section .debug_str_offsets,"", at progbits
+ .long .Linfo_string0
+ .long .Linfo_string1
+ .long .Linfo_string2
+ .long .Linfo_string3
+ .long .Linfo_string4
+ .long .Linfo_string5
+ .long .Linfo_string6
+ .long .Linfo_string7
+ .long .Linfo_string8
+ .long .Linfo_string9
+ .long .Linfo_string10
+ .long .Linfo_string11
+ .section .debug_addr,"", at progbits
+ .long .Ldebug_addr_end0-.Ldebug_addr_start0 # Length of contribution
+.Ldebug_addr_start0:
+ .short 5 # DWARF version number
+ .byte 8 # Address size
+ .byte 0 # Segment selector size
+.Laddr_table_base0:
+ .quad .Lfunc_begin0
+ .quad .Ltmp1
+.Ldebug_addr_end0:
+ .ident "clang version 22.0.0git (https://github.com/UltimateForce21/llvm-project.git 79c0a9e1e7da0f727c41d27c9c6ff8a28bb7d06f)"
+ .section ".note.GNU-stack","", at progbits
+ .addrsig
+ .section .debug_line,"", at progbits
+.Lline_table_start0:
diff --git a/lldb/test/API/functionalities/disassembler-variables/regs_fp_params.c b/lldb/test/API/functionalities/disassembler-variables/regs_fp_params.c
deleted file mode 100644
index 0f94076eb3299..0000000000000
--- a/lldb/test/API/functionalities/disassembler-variables/regs_fp_params.c
+++ /dev/null
@@ -1,5 +0,0 @@
-__attribute__((noinline))
-double regs_fp_params(double a, double b, double c, double d, double e, double f) {
- asm volatile("" :: "x"(a), "x"(b), "x"(c), "x"(d), "x"(e), "x"(f));
- return a + b + c + d + e + f;
-}
\ No newline at end of file
diff --git a/lldb/test/API/functionalities/disassembler-variables/regs_fp_params.s b/lldb/test/API/functionalities/disassembler-variables/regs_fp_params.s
new file mode 100644
index 0000000000000..502ab151e0c5b
--- /dev/null
+++ b/lldb/test/API/functionalities/disassembler-variables/regs_fp_params.s
@@ -0,0 +1,304 @@
+/* Original C (for context):
+* __attribute__((noinline))
+* double regs_fp_params(double a, double b, double c, double d, double e, double f) {
+* asm volatile("" :: "x"(a), "x"(b), "x"(c), "x"(d), "x"(e), "x"(f));
+* return a + b + c + d + e + f;
+* }*/
+ .file "regs_fp_params.c"
+ .text
+ .globl regs_fp_params # -- Begin function regs_fp_params
+ .p2align 4
+ .type regs_fp_params, at function
+regs_fp_params: # @regs_fp_params
+.Lfunc_begin0:
+ .file 0 "." "regs_fp_params.c" md5 0xdd883927454b0ea1cdce7b3c16c6a643
+ .loc 0 2 0 # regs_fp_params.c:2:0
+ .cfi_startproc
+# %bb.0: # %entry
+ #DEBUG_VALUE: regs_fp_params:a <- $xmm0
+ #DEBUG_VALUE: regs_fp_params:b <- $xmm1
+ #DEBUG_VALUE: regs_fp_params:c <- $xmm2
+ #DEBUG_VALUE: regs_fp_params:d <- $xmm3
+ #DEBUG_VALUE: regs_fp_params:e <- $xmm4
+ #DEBUG_VALUE: regs_fp_params:f <- $xmm5
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+.Ltmp0:
+ .loc 0 3 3 prologue_end # regs_fp_params.c:3:3
+ #APP
+ #NO_APP
+ .loc 0 4 12 # regs_fp_params.c:4:12
+ addsd %xmm1, %xmm0
+.Ltmp1:
+ #DEBUG_VALUE: regs_fp_params:a <- [DW_OP_LLVM_entry_value 1] $xmm0
+ .loc 0 4 16 is_stmt 0 # regs_fp_params.c:4:16
+ addsd %xmm2, %xmm0
+ .loc 0 4 20 # regs_fp_params.c:4:20
+ addsd %xmm3, %xmm0
+ .loc 0 4 24 # regs_fp_params.c:4:24
+ addsd %xmm4, %xmm0
+ .loc 0 4 28 # regs_fp_params.c:4:28
+ addsd %xmm5, %xmm0
+ .loc 0 4 3 epilogue_begin # regs_fp_params.c:4:3
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Ltmp2:
+.Lfunc_end0:
+ .size regs_fp_params, .Lfunc_end0-regs_fp_params
+ .cfi_endproc
+ # -- End function
+ .section .debug_loclists,"", at progbits
+ .long .Ldebug_list_header_end0-.Ldebug_list_header_start0 # Length
+.Ldebug_list_header_start0:
+ .short 5 # Version
+ .byte 8 # Address size
+ .byte 0 # Segment selector size
+ .long 1 # Offset entry count
+.Lloclists_table_base0:
+ .long .Ldebug_loc0-.Lloclists_table_base0
+.Ldebug_loc0:
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Lfunc_begin0-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp1-.Lfunc_begin0 # ending offset
+ .byte 1 # Loc expr size
+ .byte 97 # DW_OP_reg17
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp1-.Lfunc_begin0 # starting offset
+ .uleb128 .Lfunc_end0-.Lfunc_begin0 # ending offset
+ .byte 4 # Loc expr size
+ .byte 163 # DW_OP_entry_value
+ .byte 1 # 1
+ .byte 97 # DW_OP_reg17
+ .byte 159 # DW_OP_stack_value
+ .byte 0 # DW_LLE_end_of_list
+.Ldebug_list_header_end0:
+ .section .debug_abbrev,"", at progbits
+ .byte 1 # Abbreviation Code
+ .byte 17 # DW_TAG_compile_unit
+ .byte 1 # DW_CHILDREN_yes
+ .byte 37 # DW_AT_producer
+ .byte 37 # DW_FORM_strx1
+ .byte 19 # DW_AT_language
+ .byte 5 # DW_FORM_data2
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 114 # DW_AT_str_offsets_base
+ .byte 23 # DW_FORM_sec_offset
+ .byte 16 # DW_AT_stmt_list
+ .byte 23 # DW_FORM_sec_offset
+ .byte 27 # DW_AT_comp_dir
+ .byte 37 # DW_FORM_strx1
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 115 # DW_AT_addr_base
+ .byte 23 # DW_FORM_sec_offset
+ .ascii "\214\001" # DW_AT_loclists_base
+ .byte 23 # DW_FORM_sec_offset
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 2 # Abbreviation Code
+ .byte 46 # DW_TAG_subprogram
+ .byte 1 # DW_CHILDREN_yes
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 64 # DW_AT_frame_base
+ .byte 24 # DW_FORM_exprloc
+ .byte 122 # DW_AT_call_all_calls
+ .byte 25 # DW_FORM_flag_present
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 39 # DW_AT_prototyped
+ .byte 25 # DW_FORM_flag_present
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 63 # DW_AT_external
+ .byte 25 # DW_FORM_flag_present
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 3 # Abbreviation Code
+ .byte 5 # DW_TAG_formal_parameter
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 34 # DW_FORM_loclistx
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 4 # Abbreviation Code
+ .byte 5 # DW_TAG_formal_parameter
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 24 # DW_FORM_exprloc
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 5 # Abbreviation Code
+ .byte 36 # DW_TAG_base_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 62 # DW_AT_encoding
+ .byte 11 # DW_FORM_data1
+ .byte 11 # DW_AT_byte_size
+ .byte 11 # DW_FORM_data1
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 0 # EOM(3)
+ .section .debug_info,"", at progbits
+.Lcu_begin0:
+ .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit
+.Ldebug_info_start0:
+ .short 5 # DWARF version number
+ .byte 1 # DWARF Unit Type
+ .byte 8 # Address Size (in bytes)
+ .long .debug_abbrev # Offset Into Abbrev. Section
+ .byte 1 # Abbrev [1] 0xc:0x6b DW_TAG_compile_unit
+ .byte 0 # DW_AT_producer
+ .short 29 # DW_AT_language
+ .byte 1 # DW_AT_name
+ .long .Lstr_offsets_base0 # DW_AT_str_offsets_base
+ .long .Lline_table_start0 # DW_AT_stmt_list
+ .byte 2 # DW_AT_comp_dir
+ .byte 0 # DW_AT_low_pc
+ .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+ .long .Laddr_table_base0 # DW_AT_addr_base
+ .long .Lloclists_table_base0 # DW_AT_loclists_base
+ .byte 2 # Abbrev [2] 0x27:0x4b DW_TAG_subprogram
+ .byte 0 # DW_AT_low_pc
+ .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+ .byte 1 # DW_AT_frame_base
+ .byte 86
+ # DW_AT_call_all_calls
+ .byte 3 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ # DW_AT_prototyped
+ .long 114 # DW_AT_type
+ # DW_AT_external
+ .byte 3 # Abbrev [3] 0x36:0x9 DW_TAG_formal_parameter
+ .byte 0 # DW_AT_location
+ .byte 5 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 114 # DW_AT_type
+ .byte 4 # Abbrev [4] 0x3f:0xa DW_TAG_formal_parameter
+ .byte 1 # DW_AT_location
+ .byte 98
+ .byte 6 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 114 # DW_AT_type
+ .byte 4 # Abbrev [4] 0x49:0xa DW_TAG_formal_parameter
+ .byte 1 # DW_AT_location
+ .byte 99
+ .byte 7 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 114 # DW_AT_type
+ .byte 4 # Abbrev [4] 0x53:0xa DW_TAG_formal_parameter
+ .byte 1 # DW_AT_location
+ .byte 100
+ .byte 8 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 114 # DW_AT_type
+ .byte 4 # Abbrev [4] 0x5d:0xa DW_TAG_formal_parameter
+ .byte 1 # DW_AT_location
+ .byte 101
+ .byte 9 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 114 # DW_AT_type
+ .byte 4 # Abbrev [4] 0x67:0xa DW_TAG_formal_parameter
+ .byte 1 # DW_AT_location
+ .byte 102
+ .byte 10 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 114 # DW_AT_type
+ .byte 0 # End Of Children Mark
+ .byte 5 # Abbrev [5] 0x72:0x4 DW_TAG_base_type
+ .byte 4 # DW_AT_name
+ .byte 4 # DW_AT_encoding
+ .byte 8 # DW_AT_byte_size
+ .byte 0 # End Of Children Mark
+.Ldebug_info_end0:
+ .section .debug_str_offsets,"", at progbits
+ .long 48 # Length of String Offsets Set
+ .short 5
+ .short 0
+.Lstr_offsets_base0:
+ .section .debug_str,"MS", at progbits,1
+.Linfo_string0:
+ .asciz "clang version 22.0.0git (https://github.com/UltimateForce21/llvm-project.git 79c0a9e1e7da0f727c41d27c9c6ff8a28bb7d06f)" # string offset=0
+.Linfo_string1:
+ .asciz "regs_fp_params.c" # string offset=119
+.Linfo_string2:
+ .asciz "." # string offset=136
+.Linfo_string3:
+ .asciz "regs_fp_params" # string offset=138
+.Linfo_string4:
+ .asciz "double" # string offset=153
+.Linfo_string5:
+ .asciz "a" # string offset=160
+.Linfo_string6:
+ .asciz "b" # string offset=162
+.Linfo_string7:
+ .asciz "c" # string offset=164
+.Linfo_string8:
+ .asciz "d" # string offset=166
+.Linfo_string9:
+ .asciz "e" # string offset=168
+.Linfo_string10:
+ .asciz "f" # string offset=170
+ .section .debug_str_offsets,"", at progbits
+ .long .Linfo_string0
+ .long .Linfo_string1
+ .long .Linfo_string2
+ .long .Linfo_string3
+ .long .Linfo_string4
+ .long .Linfo_string5
+ .long .Linfo_string6
+ .long .Linfo_string7
+ .long .Linfo_string8
+ .long .Linfo_string9
+ .long .Linfo_string10
+ .section .debug_addr,"", at progbits
+ .long .Ldebug_addr_end0-.Ldebug_addr_start0 # Length of contribution
+.Ldebug_addr_start0:
+ .short 5 # DWARF version number
+ .byte 8 # Address size
+ .byte 0 # Segment selector size
+.Laddr_table_base0:
+ .quad .Lfunc_begin0
+.Ldebug_addr_end0:
+ .ident "clang version 22.0.0git (https://github.com/UltimateForce21/llvm-project.git 79c0a9e1e7da0f727c41d27c9c6ff8a28bb7d06f)"
+ .section ".note.GNU-stack","", at progbits
+ .addrsig
+ .section .debug_line,"", at progbits
+.Lline_table_start0:
diff --git a/lldb/test/API/functionalities/disassembler-variables/regs_int_params.c b/lldb/test/API/functionalities/disassembler-variables/regs_int_params.c
deleted file mode 100644
index 3b65a9e421f93..0000000000000
--- a/lldb/test/API/functionalities/disassembler-variables/regs_int_params.c
+++ /dev/null
@@ -1,7 +0,0 @@
-__attribute__((noinline))
-int regs_int_params(int a, int b, int c, int d, int e, int f) {
- // Keep all params in regs; avoid spilling to the stack.
- // The compiler will usually keep a..f in the 6 integer-arg regs.
- asm volatile("" :: "r"(a), "r"(b), "r"(c), "r"(d), "r"(e), "r"(f));
- return a + b + c + d + e + f;
-}
diff --git a/lldb/test/API/functionalities/disassembler-variables/regs_int_params.s b/lldb/test/API/functionalities/disassembler-variables/regs_int_params.s
new file mode 100644
index 0000000000000..0b2a60e2b4d5f
--- /dev/null
+++ b/lldb/test/API/functionalities/disassembler-variables/regs_int_params.s
@@ -0,0 +1,312 @@
+/* Original C (for context):
+* __attribute__((noinline))
+* int regs_int_params(int a, int b, int c, int d, int e, int f) {
+* // Keep all params in regs; avoid spilling to the stack.
+* // The compiler will usually keep a..f in the 6 integer-arg regs.
+* asm volatile("" :: "r"(a), "r"(b), "r"(c), "r"(d), "r"(e), "r"(f));
+* return a + b + c + d + e + f;
+* }
+*/
+ .file "regs_int_params.c"
+ .text
+ .globl regs_int_params # -- Begin function regs_int_params
+ .p2align 4
+ .type regs_int_params, at function
+regs_int_params: # @regs_int_params
+.Lfunc_begin0:
+ .file 0 "." "regs_int_params.c" md5 0xcf39432098ab893043cc8b4606354bd2
+ .loc 0 2 0 # regs_int_params.c:2:0
+ .cfi_startproc
+# %bb.0: # %entry
+ #DEBUG_VALUE: regs_int_params:a <- $edi
+ #DEBUG_VALUE: regs_int_params:b <- $esi
+ #DEBUG_VALUE: regs_int_params:c <- $edx
+ #DEBUG_VALUE: regs_int_params:d <- $ecx
+ #DEBUG_VALUE: regs_int_params:e <- $r8d
+ #DEBUG_VALUE: regs_int_params:f <- $r9d
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ # kill: def $r9d killed $r9d def $r9
+ # kill: def $r8d killed $r8d def $r8
+ # kill: def $ecx killed $ecx def $rcx
+ # kill: def $edx killed $edx def $rdx
+ # kill: def $esi killed $esi def $rsi
+ # kill: def $edi killed $edi def $rdi
+.Ltmp0:
+ .loc 0 5 3 prologue_end # regs_int_params.c:5:3
+ #APP
+ #NO_APP
+ .loc 0 6 12 # regs_int_params.c:6:12
+ addl %edi, %esi
+.Ltmp1:
+ #DEBUG_VALUE: regs_int_params:b <- [DW_OP_LLVM_entry_value 1] $esi
+ .loc 0 6 16 is_stmt 0 # regs_int_params.c:6:16
+ leal (%rdx,%rcx), %eax
+ .loc 0 6 20 # regs_int_params.c:6:20
+ addl %esi, %eax
+ .loc 0 6 28 # regs_int_params.c:6:28
+ addl %r8d, %eax
+ addl %r9d, %eax
+ .loc 0 6 3 epilogue_begin # regs_int_params.c:6:3
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Ltmp2:
+.Lfunc_end0:
+ .size regs_int_params, .Lfunc_end0-regs_int_params
+ .cfi_endproc
+ # -- End function
+ .section .debug_loclists,"", at progbits
+ .long .Ldebug_list_header_end0-.Ldebug_list_header_start0 # Length
+.Ldebug_list_header_start0:
+ .short 5 # Version
+ .byte 8 # Address size
+ .byte 0 # Segment selector size
+ .long 1 # Offset entry count
+.Lloclists_table_base0:
+ .long .Ldebug_loc0-.Lloclists_table_base0
+.Ldebug_loc0:
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Lfunc_begin0-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp1-.Lfunc_begin0 # ending offset
+ .byte 1 # Loc expr size
+ .byte 84 # super-register DW_OP_reg4
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp1-.Lfunc_begin0 # starting offset
+ .uleb128 .Lfunc_end0-.Lfunc_begin0 # ending offset
+ .byte 4 # Loc expr size
+ .byte 163 # DW_OP_entry_value
+ .byte 1 # 1
+ .byte 84 # super-register DW_OP_reg4
+ .byte 159 # DW_OP_stack_value
+ .byte 0 # DW_LLE_end_of_list
+.Ldebug_list_header_end0:
+ .section .debug_abbrev,"", at progbits
+ .byte 1 # Abbreviation Code
+ .byte 17 # DW_TAG_compile_unit
+ .byte 1 # DW_CHILDREN_yes
+ .byte 37 # DW_AT_producer
+ .byte 37 # DW_FORM_strx1
+ .byte 19 # DW_AT_language
+ .byte 5 # DW_FORM_data2
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 114 # DW_AT_str_offsets_base
+ .byte 23 # DW_FORM_sec_offset
+ .byte 16 # DW_AT_stmt_list
+ .byte 23 # DW_FORM_sec_offset
+ .byte 27 # DW_AT_comp_dir
+ .byte 37 # DW_FORM_strx1
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 115 # DW_AT_addr_base
+ .byte 23 # DW_FORM_sec_offset
+ .ascii "\214\001" # DW_AT_loclists_base
+ .byte 23 # DW_FORM_sec_offset
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 2 # Abbreviation Code
+ .byte 46 # DW_TAG_subprogram
+ .byte 1 # DW_CHILDREN_yes
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 64 # DW_AT_frame_base
+ .byte 24 # DW_FORM_exprloc
+ .byte 122 # DW_AT_call_all_calls
+ .byte 25 # DW_FORM_flag_present
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 39 # DW_AT_prototyped
+ .byte 25 # DW_FORM_flag_present
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 63 # DW_AT_external
+ .byte 25 # DW_FORM_flag_present
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 3 # Abbreviation Code
+ .byte 5 # DW_TAG_formal_parameter
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 24 # DW_FORM_exprloc
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 4 # Abbreviation Code
+ .byte 5 # DW_TAG_formal_parameter
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 34 # DW_FORM_loclistx
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 5 # Abbreviation Code
+ .byte 36 # DW_TAG_base_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 62 # DW_AT_encoding
+ .byte 11 # DW_FORM_data1
+ .byte 11 # DW_AT_byte_size
+ .byte 11 # DW_FORM_data1
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 0 # EOM(3)
+ .section .debug_info,"", at progbits
+.Lcu_begin0:
+ .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit
+.Ldebug_info_start0:
+ .short 5 # DWARF version number
+ .byte 1 # DWARF Unit Type
+ .byte 8 # Address Size (in bytes)
+ .long .debug_abbrev # Offset Into Abbrev. Section
+ .byte 1 # Abbrev [1] 0xc:0x6b DW_TAG_compile_unit
+ .byte 0 # DW_AT_producer
+ .short 29 # DW_AT_language
+ .byte 1 # DW_AT_name
+ .long .Lstr_offsets_base0 # DW_AT_str_offsets_base
+ .long .Lline_table_start0 # DW_AT_stmt_list
+ .byte 2 # DW_AT_comp_dir
+ .byte 0 # DW_AT_low_pc
+ .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+ .long .Laddr_table_base0 # DW_AT_addr_base
+ .long .Lloclists_table_base0 # DW_AT_loclists_base
+ .byte 2 # Abbrev [2] 0x27:0x4b DW_TAG_subprogram
+ .byte 0 # DW_AT_low_pc
+ .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+ .byte 1 # DW_AT_frame_base
+ .byte 86
+ # DW_AT_call_all_calls
+ .byte 3 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ # DW_AT_prototyped
+ .long 114 # DW_AT_type
+ # DW_AT_external
+ .byte 3 # Abbrev [3] 0x36:0xa DW_TAG_formal_parameter
+ .byte 1 # DW_AT_location
+ .byte 85
+ .byte 5 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 114 # DW_AT_type
+ .byte 4 # Abbrev [4] 0x40:0x9 DW_TAG_formal_parameter
+ .byte 0 # DW_AT_location
+ .byte 6 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 114 # DW_AT_type
+ .byte 3 # Abbrev [3] 0x49:0xa DW_TAG_formal_parameter
+ .byte 1 # DW_AT_location
+ .byte 81
+ .byte 7 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 114 # DW_AT_type
+ .byte 3 # Abbrev [3] 0x53:0xa DW_TAG_formal_parameter
+ .byte 1 # DW_AT_location
+ .byte 82
+ .byte 8 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 114 # DW_AT_type
+ .byte 3 # Abbrev [3] 0x5d:0xa DW_TAG_formal_parameter
+ .byte 1 # DW_AT_location
+ .byte 88
+ .byte 9 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 114 # DW_AT_type
+ .byte 3 # Abbrev [3] 0x67:0xa DW_TAG_formal_parameter
+ .byte 1 # DW_AT_location
+ .byte 89
+ .byte 10 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 114 # DW_AT_type
+ .byte 0 # End Of Children Mark
+ .byte 5 # Abbrev [5] 0x72:0x4 DW_TAG_base_type
+ .byte 4 # DW_AT_name
+ .byte 5 # DW_AT_encoding
+ .byte 4 # DW_AT_byte_size
+ .byte 0 # End Of Children Mark
+.Ldebug_info_end0:
+ .section .debug_str_offsets,"", at progbits
+ .long 48 # Length of String Offsets Set
+ .short 5
+ .short 0
+.Lstr_offsets_base0:
+ .section .debug_str,"MS", at progbits,1
+.Linfo_string0:
+ .asciz "clang version 22.0.0git (https://github.com/UltimateForce21/llvm-project.git 79c0a9e1e7da0f727c41d27c9c6ff8a28bb7d06f)" # string offset=0
+.Linfo_string1:
+ .asciz "regs_int_params.c" # string offset=119
+.Linfo_string2:
+ .asciz "." # string offset=137
+.Linfo_string3:
+ .asciz "regs_int_params" # string offset=139
+.Linfo_string4:
+ .asciz "int" # string offset=155
+.Linfo_string5:
+ .asciz "a" # string offset=159
+.Linfo_string6:
+ .asciz "b" # string offset=161
+.Linfo_string7:
+ .asciz "c" # string offset=163
+.Linfo_string8:
+ .asciz "d" # string offset=165
+.Linfo_string9:
+ .asciz "e" # string offset=167
+.Linfo_string10:
+ .asciz "f" # string offset=169
+ .section .debug_str_offsets,"", at progbits
+ .long .Linfo_string0
+ .long .Linfo_string1
+ .long .Linfo_string2
+ .long .Linfo_string3
+ .long .Linfo_string4
+ .long .Linfo_string5
+ .long .Linfo_string6
+ .long .Linfo_string7
+ .long .Linfo_string8
+ .long .Linfo_string9
+ .long .Linfo_string10
+ .section .debug_addr,"", at progbits
+ .long .Ldebug_addr_end0-.Ldebug_addr_start0 # Length of contribution
+.Ldebug_addr_start0:
+ .short 5 # DWARF version number
+ .byte 8 # Address size
+ .byte 0 # Segment selector size
+.Laddr_table_base0:
+ .quad .Lfunc_begin0
+.Ldebug_addr_end0:
+ .ident "clang version 22.0.0git (https://github.com/UltimateForce21/llvm-project.git 79c0a9e1e7da0f727c41d27c9c6ff8a28bb7d06f)"
+ .section ".note.GNU-stack","", at progbits
+ .addrsig
+ .section .debug_line,"", at progbits
+.Lline_table_start0:
diff --git a/lldb/test/API/functionalities/disassembler-variables/regs_mixed_params.c b/lldb/test/API/functionalities/disassembler-variables/regs_mixed_params.c
deleted file mode 100644
index 6bcb418f8ba17..0000000000000
--- a/lldb/test/API/functionalities/disassembler-variables/regs_mixed_params.c
+++ /dev/null
@@ -1,9 +0,0 @@
-__attribute__((noinline))
-double regs_mixed_params(int a, int b, double x, double y, int c, double z) {
- // Keep everything live; avoid spills.
- asm volatile("" :: "r"(a), "r"(b), "x"(x), "x"(y), "r"(c), "x"(z));
- // Some mixing so values stay in regs long enough to annotate.
- double r = (double)(a + b + c) + x + y + z;
- asm volatile("" :: "x"(r), "r"(a), "x"(x));
- return r;
-}
diff --git a/lldb/test/API/functionalities/disassembler-variables/regs_mixed_params.s b/lldb/test/API/functionalities/disassembler-variables/regs_mixed_params.s
new file mode 100644
index 0000000000000..691180b42f249
--- /dev/null
+++ b/lldb/test/API/functionalities/disassembler-variables/regs_mixed_params.s
@@ -0,0 +1,375 @@
+/* Original C (for context):
+* __attribute__((noinline))
+* double regs_mixed_params(int a, int b, double x, double y, int c, double z) {
+* // Keep everything live; avoid spills.
+* asm volatile("" :: "r"(a), "r"(b), "x"(x), "x"(y), "r"(c), "x"(z));
+* // Some mixing so values stay in regs long enough to annotate.
+* double r = (double)(a + b + c) + x + y + z;
+* asm volatile("" :: "x"(r), "r"(a), "x"(x));
+* return r;
+* }
+*/
+ .file "regs_mixed_params.c"
+ .file 0 "." "regs_mixed_params.c" md5 0x73c4bda40238ae460aaecb3a6a2603cf
+ .text
+ .globl regs_mixed_params # -- Begin function regs_mixed_params
+ .p2align 4
+ .type regs_mixed_params, at function
+regs_mixed_params: # @regs_mixed_params
+.Lfunc_begin0:
+ .loc 0 2 0 # regs_mixed_params.c:2:0
+ .cfi_startproc
+# %bb.0: # %entry
+ #DEBUG_VALUE: regs_mixed_params:a <- $edi
+ #DEBUG_VALUE: regs_mixed_params:b <- $esi
+ #DEBUG_VALUE: regs_mixed_params:x <- $xmm0
+ #DEBUG_VALUE: regs_mixed_params:y <- $xmm1
+ #DEBUG_VALUE: regs_mixed_params:c <- $edx
+ #DEBUG_VALUE: regs_mixed_params:z <- $xmm2
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+.Ltmp0:
+ .loc 0 4 3 prologue_end # regs_mixed_params.c:4:3
+ #APP
+ #NO_APP
+ .loc 0 6 25 # regs_mixed_params.c:6:25
+ addl %edi, %esi
+.Ltmp1:
+ #DEBUG_VALUE: regs_mixed_params:b <- [DW_OP_LLVM_entry_value 1] $esi
+ .loc 0 6 29 is_stmt 0 # regs_mixed_params.c:6:29
+ addl %edx, %esi
+ .loc 0 6 14 # regs_mixed_params.c:6:14
+ cvtsi2sd %esi, %xmm4
+ .loc 0 6 34 # regs_mixed_params.c:6:34
+ movapd %xmm0, %xmm3
+ addsd %xmm4, %xmm3
+ .loc 0 6 38 # regs_mixed_params.c:6:38
+ addsd %xmm1, %xmm3
+ .loc 0 6 42 # regs_mixed_params.c:6:42
+ addsd %xmm2, %xmm3
+.Ltmp2:
+ #DEBUG_VALUE: regs_mixed_params:r <- $xmm3
+ .loc 0 7 3 is_stmt 1 # regs_mixed_params.c:7:3
+ #APP
+ #NO_APP
+ .loc 0 8 3 # regs_mixed_params.c:8:3
+ movapd %xmm3, %xmm0
+.Ltmp3:
+ #DEBUG_VALUE: regs_mixed_params:x <- [DW_OP_LLVM_entry_value 1] $xmm0
+ .loc 0 8 3 epilogue_begin is_stmt 0 # regs_mixed_params.c:8:3
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Ltmp4:
+.Lfunc_end0:
+ .size regs_mixed_params, .Lfunc_end0-regs_mixed_params
+ .cfi_endproc
+ # -- End function
+ .section .debug_loclists,"", at progbits
+ .long .Ldebug_list_header_end0-.Ldebug_list_header_start0 # Length
+.Ldebug_list_header_start0:
+ .short 5 # Version
+ .byte 8 # Address size
+ .byte 0 # Segment selector size
+ .long 3 # Offset entry count
+.Lloclists_table_base0:
+ .long .Ldebug_loc0-.Lloclists_table_base0
+ .long .Ldebug_loc1-.Lloclists_table_base0
+ .long .Ldebug_loc2-.Lloclists_table_base0
+.Ldebug_loc0:
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Lfunc_begin0-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp1-.Lfunc_begin0 # ending offset
+ .byte 1 # Loc expr size
+ .byte 84 # super-register DW_OP_reg4
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp1-.Lfunc_begin0 # starting offset
+ .uleb128 .Lfunc_end0-.Lfunc_begin0 # ending offset
+ .byte 4 # Loc expr size
+ .byte 163 # DW_OP_entry_value
+ .byte 1 # 1
+ .byte 84 # super-register DW_OP_reg4
+ .byte 159 # DW_OP_stack_value
+ .byte 0 # DW_LLE_end_of_list
+.Ldebug_loc1:
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Lfunc_begin0-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp3-.Lfunc_begin0 # ending offset
+ .byte 1 # Loc expr size
+ .byte 97 # DW_OP_reg17
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp3-.Lfunc_begin0 # starting offset
+ .uleb128 .Lfunc_end0-.Lfunc_begin0 # ending offset
+ .byte 4 # Loc expr size
+ .byte 163 # DW_OP_entry_value
+ .byte 1 # 1
+ .byte 97 # DW_OP_reg17
+ .byte 159 # DW_OP_stack_value
+ .byte 0 # DW_LLE_end_of_list
+.Ldebug_loc2:
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp2-.Lfunc_begin0 # starting offset
+ .uleb128 .Lfunc_end0-.Lfunc_begin0 # ending offset
+ .byte 1 # Loc expr size
+ .byte 100 # DW_OP_reg20
+ .byte 0 # DW_LLE_end_of_list
+.Ldebug_list_header_end0:
+ .section .debug_abbrev,"", at progbits
+ .byte 1 # Abbreviation Code
+ .byte 17 # DW_TAG_compile_unit
+ .byte 1 # DW_CHILDREN_yes
+ .byte 37 # DW_AT_producer
+ .byte 37 # DW_FORM_strx1
+ .byte 19 # DW_AT_language
+ .byte 5 # DW_FORM_data2
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 114 # DW_AT_str_offsets_base
+ .byte 23 # DW_FORM_sec_offset
+ .byte 16 # DW_AT_stmt_list
+ .byte 23 # DW_FORM_sec_offset
+ .byte 27 # DW_AT_comp_dir
+ .byte 37 # DW_FORM_strx1
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 115 # DW_AT_addr_base
+ .byte 23 # DW_FORM_sec_offset
+ .ascii "\214\001" # DW_AT_loclists_base
+ .byte 23 # DW_FORM_sec_offset
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 2 # Abbreviation Code
+ .byte 36 # DW_TAG_base_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 62 # DW_AT_encoding
+ .byte 11 # DW_FORM_data1
+ .byte 11 # DW_AT_byte_size
+ .byte 11 # DW_FORM_data1
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 3 # Abbreviation Code
+ .byte 46 # DW_TAG_subprogram
+ .byte 1 # DW_CHILDREN_yes
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 64 # DW_AT_frame_base
+ .byte 24 # DW_FORM_exprloc
+ .byte 122 # DW_AT_call_all_calls
+ .byte 25 # DW_FORM_flag_present
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 39 # DW_AT_prototyped
+ .byte 25 # DW_FORM_flag_present
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 63 # DW_AT_external
+ .byte 25 # DW_FORM_flag_present
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 4 # Abbreviation Code
+ .byte 5 # DW_TAG_formal_parameter
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 24 # DW_FORM_exprloc
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 5 # Abbreviation Code
+ .byte 5 # DW_TAG_formal_parameter
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 34 # DW_FORM_loclistx
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 6 # Abbreviation Code
+ .byte 52 # DW_TAG_variable
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 34 # DW_FORM_loclistx
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 0 # EOM(3)
+ .section .debug_info,"", at progbits
+.Lcu_begin0:
+ .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit
+.Ldebug_info_start0:
+ .short 5 # DWARF version number
+ .byte 1 # DWARF Unit Type
+ .byte 8 # Address Size (in bytes)
+ .long .debug_abbrev # Offset Into Abbrev. Section
+ .byte 1 # Abbrev [1] 0xc:0x77 DW_TAG_compile_unit
+ .byte 0 # DW_AT_producer
+ .short 29 # DW_AT_language
+ .byte 1 # DW_AT_name
+ .long .Lstr_offsets_base0 # DW_AT_str_offsets_base
+ .long .Lline_table_start0 # DW_AT_stmt_list
+ .byte 2 # DW_AT_comp_dir
+ .byte 0 # DW_AT_low_pc
+ .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+ .long .Laddr_table_base0 # DW_AT_addr_base
+ .long .Lloclists_table_base0 # DW_AT_loclists_base
+ .byte 2 # Abbrev [2] 0x27:0x4 DW_TAG_base_type
+ .byte 3 # DW_AT_name
+ .byte 4 # DW_AT_encoding
+ .byte 8 # DW_AT_byte_size
+ .byte 3 # Abbrev [3] 0x2b:0x53 DW_TAG_subprogram
+ .byte 0 # DW_AT_low_pc
+ .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+ .byte 1 # DW_AT_frame_base
+ .byte 86
+ # DW_AT_call_all_calls
+ .byte 4 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ # DW_AT_prototyped
+ .long 39 # DW_AT_type
+ # DW_AT_external
+ .byte 4 # Abbrev [4] 0x3a:0xa DW_TAG_formal_parameter
+ .byte 1 # DW_AT_location
+ .byte 85
+ .byte 5 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 126 # DW_AT_type
+ .byte 5 # Abbrev [5] 0x44:0x9 DW_TAG_formal_parameter
+ .byte 0 # DW_AT_location
+ .byte 7 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 126 # DW_AT_type
+ .byte 5 # Abbrev [5] 0x4d:0x9 DW_TAG_formal_parameter
+ .byte 1 # DW_AT_location
+ .byte 8 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 39 # DW_AT_type
+ .byte 4 # Abbrev [4] 0x56:0xa DW_TAG_formal_parameter
+ .byte 1 # DW_AT_location
+ .byte 98
+ .byte 9 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 39 # DW_AT_type
+ .byte 4 # Abbrev [4] 0x60:0xa DW_TAG_formal_parameter
+ .byte 1 # DW_AT_location
+ .byte 81
+ .byte 10 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 126 # DW_AT_type
+ .byte 4 # Abbrev [4] 0x6a:0xa DW_TAG_formal_parameter
+ .byte 1 # DW_AT_location
+ .byte 99
+ .byte 11 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 39 # DW_AT_type
+ .byte 6 # Abbrev [6] 0x74:0x9 DW_TAG_variable
+ .byte 2 # DW_AT_location
+ .byte 12 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 6 # DW_AT_decl_line
+ .long 39 # DW_AT_type
+ .byte 0 # End Of Children Mark
+ .byte 2 # Abbrev [2] 0x7e:0x4 DW_TAG_base_type
+ .byte 6 # DW_AT_name
+ .byte 5 # DW_AT_encoding
+ .byte 4 # DW_AT_byte_size
+ .byte 0 # End Of Children Mark
+.Ldebug_info_end0:
+ .section .debug_str_offsets,"", at progbits
+ .long 56 # Length of String Offsets Set
+ .short 5
+ .short 0
+.Lstr_offsets_base0:
+ .section .debug_str,"MS", at progbits,1
+.Linfo_string0:
+ .asciz "clang version 22.0.0git (https://github.com/UltimateForce21/llvm-project.git 79c0a9e1e7da0f727c41d27c9c6ff8a28bb7d06f)" # string offset=0
+.Linfo_string1:
+ .asciz "regs_mixed_params.c" # string offset=119
+.Linfo_string2:
+ .asciz "." # string offset=139
+.Linfo_string3:
+ .asciz "double" # string offset=141
+.Linfo_string4:
+ .asciz "regs_mixed_params" # string offset=148
+.Linfo_string5:
+ .asciz "a" # string offset=166
+.Linfo_string6:
+ .asciz "int" # string offset=168
+.Linfo_string7:
+ .asciz "b" # string offset=172
+.Linfo_string8:
+ .asciz "x" # string offset=174
+.Linfo_string9:
+ .asciz "y" # string offset=176
+.Linfo_string10:
+ .asciz "c" # string offset=178
+.Linfo_string11:
+ .asciz "z" # string offset=180
+.Linfo_string12:
+ .asciz "r" # string offset=182
+ .section .debug_str_offsets,"", at progbits
+ .long .Linfo_string0
+ .long .Linfo_string1
+ .long .Linfo_string2
+ .long .Linfo_string3
+ .long .Linfo_string4
+ .long .Linfo_string5
+ .long .Linfo_string6
+ .long .Linfo_string7
+ .long .Linfo_string8
+ .long .Linfo_string9
+ .long .Linfo_string10
+ .long .Linfo_string11
+ .long .Linfo_string12
+ .section .debug_addr,"", at progbits
+ .long .Ldebug_addr_end0-.Ldebug_addr_start0 # Length of contribution
+.Ldebug_addr_start0:
+ .short 5 # DWARF version number
+ .byte 8 # Address size
+ .byte 0 # Segment selector size
+.Laddr_table_base0:
+ .quad .Lfunc_begin0
+.Ldebug_addr_end0:
+ .ident "clang version 22.0.0git (https://github.com/UltimateForce21/llvm-project.git 79c0a9e1e7da0f727c41d27c9c6ff8a28bb7d06f)"
+ .section ".note.GNU-stack","", at progbits
+ .addrsig
+ .section .debug_line,"", at progbits
+.Lline_table_start0:
diff --git a/lldb/test/API/functionalities/disassembler-variables/seed_reg_const_undef.c b/lldb/test/API/functionalities/disassembler-variables/seed_reg_const_undef.c
deleted file mode 100644
index 54546bc40d04f..0000000000000
--- a/lldb/test/API/functionalities/disassembler-variables/seed_reg_const_undef.c
+++ /dev/null
@@ -1,9 +0,0 @@
-// Inputs/seed_reg_const_undef.c
-__attribute__((noinline))
-int main(int argc, char **argv) {
- int i = argc; // i in a reg (DW_OP_regN)
- asm volatile("" :: "r"(i)); // keep i live here
- i = 0; // i becomes const 0 (DW_OP_constu 0, stack_value)
- asm volatile("" :: "r"(i)); // keep the const range materialized
- return 0; // i ends -> <undef> after its range
-}
diff --git a/lldb/test/API/functionalities/disassembler-variables/seed_reg_const_undef.s b/lldb/test/API/functionalities/disassembler-variables/seed_reg_const_undef.s
new file mode 100644
index 0000000000000..f85b8a712cbb2
--- /dev/null
+++ b/lldb/test/API/functionalities/disassembler-variables/seed_reg_const_undef.s
@@ -0,0 +1,289 @@
+/* Original C (for context):
+* __attribute__((noinline))
+* int main(int argc, char **argv) {
+* int i = argc; // i in a reg (DW_OP_regN)
+* asm volatile("" :: "r"(i)); // keep i live here
+* i = 0; // i becomes const 0 (DW_OP_constu 0, stack_value)
+* asm volatile("" :: "r"(i)); // keep the const range materialized
+* return 0; // i ends -> <undef> after its range
+* }
+*/
+
+ .file "seed_reg_const_undef.c"
+ .text
+ .globl main # -- Begin function main
+ .p2align 4
+ .type main, at function
+main: # @main
+.Lfunc_begin0:
+ .file 0 "." "seed_reg_const_undef.c" md5 0x5e8dbf089d1bd72d395da802210b3138
+ .loc 0 3 0 # seed_reg_const_undef.c:3:0
+ .cfi_startproc
+# %bb.0: # %entry
+ #DEBUG_VALUE: main:argc <- $edi
+ #DEBUG_VALUE: main:argv <- $rsi
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+.Ltmp0:
+ #DEBUG_VALUE: main:i <- $edi
+ .loc 0 5 3 prologue_end # seed_reg_const_undef.c:5:3
+ #APP
+ #NO_APP
+.Ltmp1:
+ #DEBUG_VALUE: main:i <- 0
+ .loc 0 7 3 # seed_reg_const_undef.c:7:3
+ xorl %eax, %eax
+ #APP
+ #NO_APP
+ .loc 0 8 3 # seed_reg_const_undef.c:8:3
+ xorl %eax, %eax
+ .loc 0 8 3 epilogue_begin is_stmt 0 # seed_reg_const_undef.c:8:3
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Ltmp2:
+.Lfunc_end0:
+ .size main, .Lfunc_end0-main
+ .cfi_endproc
+ # -- End function
+ .section .debug_loclists,"", at progbits
+ .long .Ldebug_list_header_end0-.Ldebug_list_header_start0 # Length
+.Ldebug_list_header_start0:
+ .short 5 # Version
+ .byte 8 # Address size
+ .byte 0 # Segment selector size
+ .long 1 # Offset entry count
+.Lloclists_table_base0:
+ .long .Ldebug_loc0-.Lloclists_table_base0
+.Ldebug_loc0:
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp0-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp1-.Lfunc_begin0 # ending offset
+ .byte 1 # Loc expr size
+ .byte 85 # super-register DW_OP_reg5
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp1-.Lfunc_begin0 # starting offset
+ .uleb128 .Lfunc_end0-.Lfunc_begin0 # ending offset
+ .byte 3 # Loc expr size
+ .byte 17 # DW_OP_consts
+ .byte 0 # 0
+ .byte 159 # DW_OP_stack_value
+ .byte 0 # DW_LLE_end_of_list
+.Ldebug_list_header_end0:
+ .section .debug_abbrev,"", at progbits
+ .byte 1 # Abbreviation Code
+ .byte 17 # DW_TAG_compile_unit
+ .byte 1 # DW_CHILDREN_yes
+ .byte 37 # DW_AT_producer
+ .byte 37 # DW_FORM_strx1
+ .byte 19 # DW_AT_language
+ .byte 5 # DW_FORM_data2
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 114 # DW_AT_str_offsets_base
+ .byte 23 # DW_FORM_sec_offset
+ .byte 16 # DW_AT_stmt_list
+ .byte 23 # DW_FORM_sec_offset
+ .byte 27 # DW_AT_comp_dir
+ .byte 37 # DW_FORM_strx1
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 115 # DW_AT_addr_base
+ .byte 23 # DW_FORM_sec_offset
+ .ascii "\214\001" # DW_AT_loclists_base
+ .byte 23 # DW_FORM_sec_offset
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 2 # Abbreviation Code
+ .byte 46 # DW_TAG_subprogram
+ .byte 1 # DW_CHILDREN_yes
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 64 # DW_AT_frame_base
+ .byte 24 # DW_FORM_exprloc
+ .byte 122 # DW_AT_call_all_calls
+ .byte 25 # DW_FORM_flag_present
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 39 # DW_AT_prototyped
+ .byte 25 # DW_FORM_flag_present
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 63 # DW_AT_external
+ .byte 25 # DW_FORM_flag_present
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 3 # Abbreviation Code
+ .byte 5 # DW_TAG_formal_parameter
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 24 # DW_FORM_exprloc
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 4 # Abbreviation Code
+ .byte 52 # DW_TAG_variable
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 34 # DW_FORM_loclistx
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 5 # Abbreviation Code
+ .byte 36 # DW_TAG_base_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 62 # DW_AT_encoding
+ .byte 11 # DW_FORM_data1
+ .byte 11 # DW_AT_byte_size
+ .byte 11 # DW_FORM_data1
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 6 # Abbreviation Code
+ .byte 15 # DW_TAG_pointer_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 0 # EOM(3)
+ .section .debug_info,"", at progbits
+.Lcu_begin0:
+ .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit
+.Ldebug_info_start0:
+ .short 5 # DWARF version number
+ .byte 1 # DWARF Unit Type
+ .byte 8 # Address Size (in bytes)
+ .long .debug_abbrev # Offset Into Abbrev. Section
+ .byte 1 # Abbrev [1] 0xc:0x5b DW_TAG_compile_unit
+ .byte 0 # DW_AT_producer
+ .short 29 # DW_AT_language
+ .byte 1 # DW_AT_name
+ .long .Lstr_offsets_base0 # DW_AT_str_offsets_base
+ .long .Lline_table_start0 # DW_AT_stmt_list
+ .byte 2 # DW_AT_comp_dir
+ .byte 0 # DW_AT_low_pc
+ .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+ .long .Laddr_table_base0 # DW_AT_addr_base
+ .long .Lloclists_table_base0 # DW_AT_loclists_base
+ .byte 2 # Abbrev [2] 0x27:0x2d DW_TAG_subprogram
+ .byte 0 # DW_AT_low_pc
+ .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+ .byte 1 # DW_AT_frame_base
+ .byte 86
+ # DW_AT_call_all_calls
+ .byte 3 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 3 # DW_AT_decl_line
+ # DW_AT_prototyped
+ .long 84 # DW_AT_type
+ # DW_AT_external
+ .byte 3 # Abbrev [3] 0x36:0xa DW_TAG_formal_parameter
+ .byte 1 # DW_AT_location
+ .byte 85
+ .byte 5 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 3 # DW_AT_decl_line
+ .long 84 # DW_AT_type
+ .byte 3 # Abbrev [3] 0x40:0xa DW_TAG_formal_parameter
+ .byte 1 # DW_AT_location
+ .byte 84
+ .byte 6 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 3 # DW_AT_decl_line
+ .long 88 # DW_AT_type
+ .byte 4 # Abbrev [4] 0x4a:0x9 DW_TAG_variable
+ .byte 0 # DW_AT_location
+ .byte 8 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 4 # DW_AT_decl_line
+ .long 84 # DW_AT_type
+ .byte 0 # End Of Children Mark
+ .byte 5 # Abbrev [5] 0x54:0x4 DW_TAG_base_type
+ .byte 4 # DW_AT_name
+ .byte 5 # DW_AT_encoding
+ .byte 4 # DW_AT_byte_size
+ .byte 6 # Abbrev [6] 0x58:0x5 DW_TAG_pointer_type
+ .long 93 # DW_AT_type
+ .byte 6 # Abbrev [6] 0x5d:0x5 DW_TAG_pointer_type
+ .long 98 # DW_AT_type
+ .byte 5 # Abbrev [5] 0x62:0x4 DW_TAG_base_type
+ .byte 7 # DW_AT_name
+ .byte 6 # DW_AT_encoding
+ .byte 1 # DW_AT_byte_size
+ .byte 0 # End Of Children Mark
+.Ldebug_info_end0:
+ .section .debug_str_offsets,"", at progbits
+ .long 40 # Length of String Offsets Set
+ .short 5
+ .short 0
+.Lstr_offsets_base0:
+ .section .debug_str,"MS", at progbits,1
+.Linfo_string0:
+ .asciz "clang version 22.0.0git (https://github.com/UltimateForce21/llvm-project.git 79c0a9e1e7da0f727c41d27c9c6ff8a28bb7d06f)" # string offset=0
+.Linfo_string1:
+ .asciz "seed_reg_const_undef.c" # string offset=119
+.Linfo_string2:
+ .asciz "." # string offset=142
+.Linfo_string3:
+ .asciz "main" # string offset=144
+.Linfo_string4:
+ .asciz "int" # string offset=149
+.Linfo_string5:
+ .asciz "argc" # string offset=153
+.Linfo_string6:
+ .asciz "argv" # string offset=158
+.Linfo_string7:
+ .asciz "char" # string offset=163
+.Linfo_string8:
+ .asciz "i" # string offset=168
+ .section .debug_str_offsets,"", at progbits
+ .long .Linfo_string0
+ .long .Linfo_string1
+ .long .Linfo_string2
+ .long .Linfo_string3
+ .long .Linfo_string4
+ .long .Linfo_string5
+ .long .Linfo_string6
+ .long .Linfo_string7
+ .long .Linfo_string8
+ .section .debug_addr,"", at progbits
+ .long .Ldebug_addr_end0-.Ldebug_addr_start0 # Length of contribution
+.Ldebug_addr_start0:
+ .short 5 # DWARF version number
+ .byte 8 # Address size
+ .byte 0 # Segment selector size
+.Laddr_table_base0:
+ .quad .Lfunc_begin0
+.Ldebug_addr_end0:
+ .ident "clang version 22.0.0git (https://github.com/UltimateForce21/llvm-project.git 79c0a9e1e7da0f727c41d27c9c6ff8a28bb7d06f)"
+ .section ".note.GNU-stack","", at progbits
+ .addrsig
+ .section .debug_line,"", at progbits
+.Lline_table_start0:
>From 75e6b44402683556ab2014fa9384418efca7dcb8 Mon Sep 17 00:00:00 2001
From: ultimateforce21 <abdullahmohammad155 at gmail.com>
Date: Thu, 21 Aug 2025 22:43:46 -0400
Subject: [PATCH 45/48] Formatting updates
---
lldb/source/Core/Disassembler.cpp | 45 ++++++++++---------
.../TestRichDisassembler.py | 9 ++--
2 files changed, 27 insertions(+), 27 deletions(-)
diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp
index 3be2379919e3a..d156c84d1f716 100644
--- a/lldb/source/Core/Disassembler.cpp
+++ b/lldb/source/Core/Disassembler.cpp
@@ -410,21 +410,21 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
// Stateful annotator: updates live_vars and returns only what should be
// printed for THIS instruction.
- auto annotate_static =
- [&](Instruction &inst, Target &target, ModuleSP module_sp)
- -> std::vector<std::string> {
-
+ auto annotate_static = [&](Instruction &inst, Target &target,
+ ModuleSP module_sp) -> std::vector<std::string> {
std::vector<std::string> events;
// Reset per-instruction seen flags.
- for (auto &kv : live_vars) kv.second.seen_this_inst = false;
+ for (auto &kv : live_vars)
+ kv.second.seen_this_inst = false;
const Address &iaddr = inst.GetAddress();
if (!module_sp) {
// Everything previously live becomes <undef>.
- for (auto I = live_vars.begin(), E = live_vars.end(); I != E; ) {
+ for (auto I = live_vars.begin(), E = live_vars.end(); I != E;) {
auto Cur = I++;
- events.push_back(llvm::formatv("{0} = <undef>", Cur->second.name).str());
+ events.push_back(
+ llvm::formatv("{0} = <undef>", Cur->second.name).str());
live_vars.erase(Cur);
}
return events;
@@ -434,11 +434,13 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
SymbolContext sc;
const lldb::SymbolContextItem mask =
eSymbolContextFunction | eSymbolContextBlock;
- if (!module_sp->ResolveSymbolContextForAddress(iaddr, mask, sc) || !sc.function) {
+ if (!module_sp->ResolveSymbolContextForAddress(iaddr, mask, sc) ||
+ !sc.function) {
// No function context: everything dies here.
- for (auto I = live_vars.begin(), E = live_vars.end(); I != E; ) {
+ for (auto I = live_vars.begin(), E = live_vars.end(); I != E;) {
auto Cur = I++;
- events.push_back(llvm::formatv("{0} = <undef>", Cur->second.name).str());
+ events.push_back(
+ llvm::formatv("{0} = <undef>", Cur->second.name).str());
live_vars.erase(Cur);
}
return events;
@@ -447,18 +449,16 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
Block *B = sc.block; ///< Innermost block containing iaddr.
VariableList var_list;
if (B) {
- auto filter = [](Variable *v) -> bool {
- return v && !v->IsArtificial();
- };
+ auto filter = [](Variable *v) -> bool { return v && !v->IsArtificial(); };
B->AppendVariables(/*can_create*/ true,
- /*get_parent_variables*/ true,
- /*stop_if_block_is_inlined_function*/ false,
- /*filter*/ filter,
- /*variable_list*/ &var_list);
+ /*get_parent_variables*/ true,
+ /*stop_if_block_is_inlined_function*/ false,
+ /*filter*/ filter,
+ /*variable_list*/ &var_list);
}
- const lldb::addr_t pc_file = iaddr.GetFileAddress();
+ const lldb::addr_t pc_file = iaddr.GetFileAddress();
const lldb::addr_t func_file = sc.function->GetAddress().GetFileAddress();
// ABI from Target (pretty reg names if plugin exists). Safe to be null.
@@ -495,8 +495,8 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
if (loc.empty())
continue;
- auto ins = live_vars.insert({v->GetID(),
- VarState{name.str(), loc.str(), /*seen*/ true}});
+ auto ins = live_vars.insert(
+ {v->GetID(), VarState{name.str(), loc.str(), /*seen*/ true}});
if (ins.second) {
// Newly live.
events.push_back(llvm::formatv("{0} = {1}", name, loc).str());
@@ -512,10 +512,11 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
// Anything previously live that we didn't see a location for at this inst
// is now <undef>.
- for (auto I = live_vars.begin(), E = live_vars.end(); I != E; ) {
+ for (auto I = live_vars.begin(), E = live_vars.end(); I != E;) {
auto Cur = I++;
if (!Cur->second.seen_this_inst) {
- events.push_back(llvm::formatv("{0} = <undef>", Cur->second.name).str());
+ events.push_back(
+ llvm::formatv("{0} = <undef>", Cur->second.name).str());
live_vars.erase(Cur);
}
}
diff --git a/lldb/test/API/functionalities/disassembler-variables/TestRichDisassembler.py b/lldb/test/API/functionalities/disassembler-variables/TestRichDisassembler.py
index 14d9ab5bf11ed..cdb617d183487 100644
--- a/lldb/test/API/functionalities/disassembler-variables/TestRichDisassembler.py
+++ b/lldb/test/API/functionalities/disassembler-variables/TestRichDisassembler.py
@@ -4,6 +4,7 @@
import os
import re
+
class TestRichDisassembler(TestBase):
def _compile_object(self, src_name, func_cflags="-g -gdwarf-5 -O2 -fno-inline"):
"""
@@ -40,7 +41,6 @@ def _compile_or_assemble_object(
self.assertTrue(os.path.exists(obj), f"missing object: {obj}")
return obj
-
def _create_target(self, path):
target = self.dbg.CreateTarget(path)
self.assertTrue(target, f"failed to create target for {path}")
@@ -59,7 +59,6 @@ def test_d_original_example_O1(self):
obj = self._compile_or_assemble_object("d_original_example.s")
target = self._create_target(obj)
out = self._disassemble_verbose_symbol("main")
-
print(out)
self.assertIn("argc = ", out)
@@ -83,7 +82,6 @@ def test_regs_int_params(self):
self.assertRegex(out, r"\bf\s*=\s*(DW_OP_reg9\b|R9\b)")
self.assertNotIn("<decoding error>", out)
-
@no_debug_info_test
def test_regs_fp_params(self):
obj = self._compile_or_assemble_object("regs_fp_params.s")
@@ -150,8 +148,9 @@ def test_seed_reg_const_undef(self):
can add an assertion for ' = 0' or similar.
"""
# Use O1 to help keep a first reg range; still object-only
- obj = self._compile_or_assemble_object("seed_reg_const_undef.s",
- cflags="-g -gdwarf-5 -O1 -fno-inline")
+ obj = self._compile_or_assemble_object(
+ "seed_reg_const_undef.s", cflags="-g -gdwarf-5 -O1 -fno-inline"
+ )
target = self._create_target(obj)
out = self._disassemble_verbose_symbol("main")
print(out)
>From e5e168f01e8b66deb4b2e8c8658140c3bd7874a4 Mon Sep 17 00:00:00 2001
From: ultimateforce21 <abdullahmohammad155 at gmail.com>
Date: Thu, 21 Aug 2025 23:08:07 -0400
Subject: [PATCH 46/48] Formatting change
---
.../disassembler-variables/TestRichDisassembler.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lldb/test/API/functionalities/disassembler-variables/TestRichDisassembler.py b/lldb/test/API/functionalities/disassembler-variables/TestRichDisassembler.py
index cdb617d183487..fb090e8e672b2 100644
--- a/lldb/test/API/functionalities/disassembler-variables/TestRichDisassembler.py
+++ b/lldb/test/API/functionalities/disassembler-variables/TestRichDisassembler.py
@@ -81,7 +81,7 @@ def test_regs_int_params(self):
self.assertRegex(out, r"\be\s*=\s*(DW_OP_reg8\b|R8\b)")
self.assertRegex(out, r"\bf\s*=\s*(DW_OP_reg9\b|R9\b)")
self.assertNotIn("<decoding error>", out)
-
+
@no_debug_info_test
def test_regs_fp_params(self):
obj = self._compile_or_assemble_object("regs_fp_params.s")
>From a72ca07a1889ada7df713c02708f8be0b2017f13 Mon Sep 17 00:00:00 2001
From: ultimateforce21 <abdullahmohammad155 at gmail.com>
Date: Sun, 24 Aug 2025 17:27:59 -0400
Subject: [PATCH 47/48] Moved the building of assembly source code files to
object files to MakeFile.
---
.../disassembler-variables/Makefile | 33 ++++++-
.../TestRichDisassembler.py | 88 ++++--------------
.../disassembler-variables/live_across_call.o | Bin 3488 -> 0 bytes
.../disassembler-variables/loop_reg_rotate.o | Bin 3680 -> 0 bytes
.../disassembler-variables/regs_fp_params.o | Bin 3304 -> 0 bytes
.../disassembler-variables/regs_int_params.o | Bin 3296 -> 0 bytes
.../regs_mixed_params.o | Bin 3440 -> 0 bytes
7 files changed, 50 insertions(+), 71 deletions(-)
delete mode 100644 lldb/test/API/functionalities/disassembler-variables/live_across_call.o
delete mode 100644 lldb/test/API/functionalities/disassembler-variables/loop_reg_rotate.o
delete mode 100644 lldb/test/API/functionalities/disassembler-variables/regs_fp_params.o
delete mode 100644 lldb/test/API/functionalities/disassembler-variables/regs_int_params.o
delete mode 100644 lldb/test/API/functionalities/disassembler-variables/regs_mixed_params.o
diff --git a/lldb/test/API/functionalities/disassembler-variables/Makefile b/lldb/test/API/functionalities/disassembler-variables/Makefile
index be53a34f7e265..3d4bcd22fa653 100644
--- a/lldb/test/API/functionalities/disassembler-variables/Makefile
+++ b/lldb/test/API/functionalities/disassembler-variables/Makefile
@@ -1 +1,32 @@
-include Makefile.rules
\ No newline at end of file
+# List all assembler inputs stable test fixtures.
+ASM_SOURCES := \
+ d_original_example.s \
+ regs_int_params.s \
+ regs_fp_params.s \
+ regs_mixed_params.s \
+ live_across_call.s \
+ loop_reg_rotate.s \
+ seed_reg_const_undef.s
+
+ASM_OBJS := $(ASM_SOURCES:.s=.o)
+
+# Provide a tiny dummy so the harness can link an exe without ASM_OBJS.
+C_SOURCES := dummy_main.c
+
+# Generating the dummy source on demand.
+dummy_main.c:
+ @echo 'int main(void){return 0;}' > $@
+
+# Assemble .s → .o using the configured compiler.
+%.o: %.s
+ $(CC) -c -x assembler $< -o $@
+
+# Default target: build all .o fixtures and the dummy exe.
+.PHONY: all
+all: $(ASM_OBJS) $(EXE)
+
+# Keeping things tidy.
+clean::
+ $(RM) -f $(ASM_OBJS) dummy_main.c
+
+include Makefile.rules
diff --git a/lldb/test/API/functionalities/disassembler-variables/TestRichDisassembler.py b/lldb/test/API/functionalities/disassembler-variables/TestRichDisassembler.py
index fb090e8e672b2..89679dc7fe807 100644
--- a/lldb/test/API/functionalities/disassembler-variables/TestRichDisassembler.py
+++ b/lldb/test/API/functionalities/disassembler-variables/TestRichDisassembler.py
@@ -6,38 +6,10 @@
class TestRichDisassembler(TestBase):
- def _compile_object(self, src_name, func_cflags="-g -gdwarf-5 -O2 -fno-inline"):
- """
- Compile a single C source to an object file using the host toolchain.
- We intentionally use `platform shell` to keep this as close to your
- existing style as possible (and avoid depending on the Makefile for .o).
- """
- src = self.getSourcePath(src_name)
- obj = self.getBuildArtifact(os.path.splitext(src_name)[0] + ".o")
- cmd = f"cc {func_cflags} -c '{src}' -o '{obj}'"
- self.runCmd(f"platform shell {cmd}", check=True)
- self.assertTrue(os.path.exists(obj), f"missing object: {obj}")
- return obj
-
- def _compile_or_assemble_object(
- self,
- src_name,
- cflags="-g -gdwarf-5 -O2 -fno-inline -fno-omit-frame-pointer",
- ):
- cc = self.getCompiler() # use the configured clang from dotest
- src = self.getSourcePath(src_name)
- stem, ext = os.path.splitext(src_name)
- obj = self.getBuildArtifact(stem + ".o")
-
- if ext == ".s":
- # Assemble the already-generated DWARF-rich .s
- cmd = f"'{cc}' -c -x assembler '{src}' -o '{obj}'"
- else:
- # Fallback for .c if needed
- cmd = f"'{cc}' {cflags} -c '{src}' -o '{obj}'"
-
- # run and validate
- self.runCmd(f"platform shell {cmd}", check=True)
+ def _build_obj(self, obj_name: str) -> str:
+ # Let the Makefile build all .o’s (pattern rule). Then grab the one we need.
+ self.build()
+ obj = self.getBuildArtifact(obj_name)
self.assertTrue(os.path.exists(obj), f"missing object: {obj}")
return obj
@@ -47,33 +19,25 @@ def _create_target(self, path):
return target
def _disassemble_verbose_symbol(self, symname):
- # For object-only tests, disassemble a named symbol from the .o
self.runCmd(f"disassemble -n {symname} -v", check=True)
return self.res.GetOutput()
def test_d_original_example_O1(self):
- """
- Tests disassembler output for d_original_example.c built with -O1,
- using the CLI with --rich for enabled annotations.
- """
- obj = self._compile_or_assemble_object("d_original_example.s")
+ obj = self._build_obj("d_original_example.o")
target = self._create_target(obj)
out = self._disassemble_verbose_symbol("main")
print(out)
-
self.assertIn("argc = ", out)
self.assertIn("argv = ", out)
self.assertIn("i = ", out)
self.assertNotIn("<decoding error>", out)
- @no_debug_info_test # we explicitly request -g in _compile_object
+ @no_debug_info_test
def test_regs_int_params(self):
- obj = self._compile_or_assemble_object("regs_int_params.s")
+ obj = self._build_obj("regs_int_params.o")
target = self._create_target(obj)
out = self._disassemble_verbose_symbol("regs_int_params")
print(out)
-
- # assertions (tweak as desired)
self.assertRegex(out, r"\ba\s*=\s*(DW_OP_reg5\b|RDI\b)")
self.assertRegex(out, r"\bb\s*=\s*(DW_OP_reg4\b|RSI\b)")
self.assertRegex(out, r"\bc\s*=\s*(DW_OP_reg1\b|RDX\b)")
@@ -84,12 +48,10 @@ def test_regs_int_params(self):
@no_debug_info_test
def test_regs_fp_params(self):
- obj = self._compile_or_assemble_object("regs_fp_params.s")
+ obj = self._build_obj("regs_fp_params.o")
target = self._create_target(obj)
out = self._disassemble_verbose_symbol("regs_fp_params")
print(out)
-
- # XMM0..5 typically map to DW_OP_reg17..22
self.assertRegex(out, r"\ba\s*=\s*(DW_OP_reg17\b|XMM0\b)")
self.assertRegex(out, r"\bb\s*=\s*(DW_OP_reg18\b|XMM1\b)")
self.assertRegex(out, r"\bc\s*=\s*(DW_OP_reg19\b|XMM2\b)")
@@ -100,11 +62,10 @@ def test_regs_fp_params(self):
@no_debug_info_test
def test_regs_mixed_params(self):
- obj = self._compile_or_assemble_object("regs_mixed_params.s")
+ obj = self._build_obj("regs_mixed_params.o")
target = self._create_target(obj)
out = self._disassemble_verbose_symbol("regs_mixed_params")
print(out)
-
self.assertRegex(out, r"\ba\s*=\s*(DW_OP_reg5\b|RDI\b)")
self.assertRegex(out, r"\bb\s*=\s*(DW_OP_reg4\b|RSI\b)")
self.assertRegex(out, r"\bx\s*=\s*(DW_OP_reg17\b|XMM0\b|DW_OP_reg\d+\b)")
@@ -115,46 +76,33 @@ def test_regs_mixed_params(self):
@no_debug_info_test
def test_live_across_call(self):
- obj = self._compile_or_assemble_object("live_across_call.s")
+ obj = self._build_obj("live_across_call.o")
target = self._create_target(obj)
out = self._disassemble_verbose_symbol("live_across_call")
print(out)
-
- # We just assert 'a' is in a register, then there's a call, then 'a' again.
self.assertRegex(out, r"\bx\s*=\s*(DW_OP_reg5\b|RDI\b)")
self.assertIn("call", out)
- self.assertRegex(out, r"\br\s*=\s*(DW_OP_reg4\b|RAX\b)")
+ self.assertRegex(out, r"\br\s*=\s*(DW_OP_reg0\b|RAX\b|DW_OP_reg\d+\b)")
self.assertNotIn("<decoding error>", out)
@no_debug_info_test
def test_loop_reg_rotate(self):
- obj = self._compile_or_assemble_object("loop_reg_rotate.s")
+ obj = self._build_obj("loop_reg_rotate.o")
target = self._create_target(obj)
out = self._disassemble_verbose_symbol("loop_reg_rotate")
print(out)
-
- self.assertRegex(out, r"\bn\s*=\s*()")
- self.assertRegex(out, r"\bseed\s*=\s*()")
- self.assertRegex(out, r"\bk\s*=\s*()")
- self.assertRegex(out, r"\bj\s*=\s*()")
- self.assertRegex(out, r"\bi\s*=\s*()")
+ self.assertRegex(out, r"\bn\s*=\s*(DW_OP_reg\d+\b|R[A-Z0-9]+)")
+ self.assertRegex(out, r"\bseed\s*=\s*(DW_OP_reg\d+\b|R[A-Z0-9]+)")
+ self.assertRegex(out, r"\bk\s*=\s*(DW_OP_reg\d+\b|R[A-Z0-9]+)")
+ self.assertRegex(out, r"\bj\s*=\s*(DW_OP_reg\d+\b|R[A-Z0-9]+)")
+ self.assertRegex(out, r"\bi\s*=\s*(DW_OP_reg\d+\b|R[A-Z0-9]+)")
self.assertNotIn("<decoding error>", out)
@no_debug_info_test
def test_seed_reg_const_undef(self):
- """
- For now, you mentioned constants aren’t printed; we still check that the
- register part shows up (first range). When you add CONST support, you
- can add an assertion for ' = 0' or similar.
- """
- # Use O1 to help keep a first reg range; still object-only
- obj = self._compile_or_assemble_object(
- "seed_reg_const_undef.s", cflags="-g -gdwarf-5 -O1 -fno-inline"
- )
+ obj = self._build_obj("seed_reg_const_undef.o")
target = self._create_target(obj)
out = self._disassemble_verbose_symbol("main")
print(out)
-
- # check that at least one var (i or argc) is shown as a register at start
self.assertRegex(out, r"\b(i|argc)\s*=\s*(DW_OP_reg\d+\b|R[A-Z0-9]+)")
self.assertNotIn("<decoding error>", out)
diff --git a/lldb/test/API/functionalities/disassembler-variables/live_across_call.o b/lldb/test/API/functionalities/disassembler-variables/live_across_call.o
deleted file mode 100644
index 643b21b30b08ef9a3b6aee5cfaf411d4c822d386..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 3488
zcmc&#&2Jl35TE^WoH$9H1o90rC8nuF*G_2aB%op>C`lVogcMLKgtcDp#-?jK+TElf
zDwG2UN_!|5Bu)r%>X8dnhy#cl2q6%M9{2;eAV5Om08*IQeG_ljTTzJvBfXtBznOV6
zvpe(l$}{U{WJv-h2~I;tViX{k?(ECKwhW_axpDRKta|NfT*!YW#kKWWRYBnVSF<Q-
zkOmn!xsT#fVL;kc2d34Jq|FaN9iEgnKpK+r`4LUYyJOk0sr=B?aLOCIE{y at mx<pcW
ze1Ag8uVfz29Gh4hd2#|k(Lg>LLTe*<>Pe8tJEDeSM4MWLD<iK at 0rcon=a?rZ=%BO)
zbU^PcDEZTwOa`EDF+iXdWG at AEOYlJFt;jHqDyC49UeZ9TBc?!4xKZVI)fA*0#56o4
zP0G{-d`QrvfJ|b5L9eQ*eI&vhE{!(#K&E4~wsO0c>owY~+`@uh)QejUKQ~wR{cUfh
zP(Y!+Q`OCOv#{y-ji%vSXWFi5EzB1jXSaD`+ikyUnZ8a=rDd~dEL-zdsb&=I(n85R
zIbXx)vU$q3myCs_YPD1=p0baF)7Z5thUvCFuVNaGqnl8ux0_aBN5rrXW7Mh at -}3yz
zT5Eg9_rS3X8yYRY>j*Gl4_vqpi>7AjqW`H<kq4zkB<;Td(q2}Y>hDS4zyTqg+aTai
z4Q4M4rePOxDx*X`lJwW$05O^<T)3GDhD(uV<RjXH!5$W;OUE}J`Q^7C>zAHe{Ns&R
zRBT5YnSe&MY~MrL<T*`Aoz=3b5iNs!GIc(X`%zX>nX|HtN8jsr=>CEnD~bPvpbEbq
zB<Zk?lVF?yEFpoEyL7jb^x4s0fxd)O2*)u^AO(gJP_oAfkI~yG4 at dInZ}}<W*_bVf
zg94+<DEi+AA#uY}rqQZ76T)ex+3tSa6A{iB$usmoBD at C|>mDW$iEss1KM6>zCp;A(
z;e+*zUlKa5iSc&?&i&2!#{wT0obgYD%<qZu&jg-}*?%MC*%<z<kmq9fcS7d6Xf&eT
z#rZQnEbMcFlWo-gsK6=DUa=>pi#_AVguE&^pD#pos9DvWt%^~ty4Eh}uH_iIZ|(U&
zS8LP43r$!!ZPRIZc+-V8p_}Ksm9}kr7U72wZ=FWVs-VdoE=H|(C(vlw?e1GsZ?%0(
zfA;y!6P|CF7eS|6y+Y1jV=KIEqlr)`5yq-lY}aU7U1#EDu46X7QAPe*llXt8U-1O$
z+c at Q@$&un0OL!%QI(kxn5*F=o!prnP;(iRJ?%6$p3YyC at 8wfrpeze@@G?y|i?mxz9
z{^B{&5iipNiTgN|x^2lf$^R|k at E$t~9=E-G7yn6N=>Cg8&3{t;Wl{g0 at TdCe9mnfm
zLM)yWxm*|Eqa;KqZu=POlH%VI at ox)XichiQ at y{VfXBKfq>6QreeH6q!$7g;qf<;ZQ
z(#J;P?;J`Uf0k|BB;|iYG$iLm`O|+Pp8q6bN%?;$;`94Qvw%2n=I<lQdtnl=iKL-Q
zu>B##wnezZBXYu@^JfL;&obTDN%emr>gV at I_0z1eKjU8_mX!a7h`*2eD8+3*M`cp{
z+ams|aHjb5&yB}_5ivS5Ugy^${tD`%#PONmM5VHR&_?!V)Q;~j#wb1!`-Kv~pSX#?
Td(x$;D5RWlAV&YBc>ezYqMSxX
diff --git a/lldb/test/API/functionalities/disassembler-variables/loop_reg_rotate.o b/lldb/test/API/functionalities/disassembler-variables/loop_reg_rotate.o
deleted file mode 100644
index 041781731d1d00a022f8eee3a05294cf4e4cdeea..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 3680
zcmc&$U1%It6h3!;Hk)ouH-DxzwOMLHE7;lHBsNJabsu82X{~8U at Tn}byEDnQyE|cb
z64O$wQ2S67RD2Odi}c9{K|vG?LQuitOD!V4)rufUA4Gf+T8QV&+_QU~ZXOiD19#3j
z-}%lxckbMKcF#X??64*Tun0H=Ess%vRA<Z11!fNVk$K4<zI$=w`Ny*BS1Ii?4Ii%S
zTHA(R3|L}%`0mTd$<Bvb92>qnhM10i_QuBbm7BM>21!ABcAdE5TX9WC&X;fD6eh$W
zhI%`WF&xKn6mYEKcnOCAePY4rn>Mb9h0DhDEIP9%11AX0hoSi(G#`fMFN0z9>D_P{
zgJKqw$PO_PU?&1hNK6Z`xd79exgfg5$VjiLkJJZ}1A`;ogFUgvz$;<^K(hpSx$S3n
z=pzRb2NQdC9O-?02Y_yZwmXE5^y1pbLEGMPO>~uLED_8C&@l}9$e~0c0T6!_C=o8=
zFhRso;YWC|^~p3iNtl$-6VK|PokvK=oeT}p5gLZ}`Z*Ky3m7!DI2gn-^-gIRTS6ly
za)g at +h!Fu-WSB-gFvlqnVE^n<tk|{1)T&c&l)YMNbkxdNnZ>f7+PmcY%Z&r+G&)Nw
z1*_;)(+d^9T(y1YuvafSquF$&vRd7 at T=z~nMc<;NiK$}7o^rCzM9I#$6QdKw at oWj7
zQ^ozRJ86$j777!k%zpP_sCeFTzV0mM>z<D at T17}Nc~vL9B1^c<5~V`kcN%{BNNst=
zZ$uNJT=T&$7NG_W$0<P>PQhvLp)c5u;IsUFKmMop4HiTz5ohN>tR=<Z){gjVcvESi
zJLMA96Z1Y|bmb-}M~FLJ_mQHjjRTD#GC>zZ+BS1nV8i6Ab2nFio!*`K<<gzEfBN`0
z1I*+W=%h{%NT1pNw5i7|{j6nn#^UG9okvYGHiLGrYIYnoyAd6nG5cfs87s_~F&~KO
z$*bthm|e*4iCO0T7 at N@quIAr;JN)RI%MA<}@cTwP2Hv7}FkUNt6WrJY at 7M%anykXm
zVT<CmOdXOfN3nG1QM}N64^@w{e*WfsRH=fx+HN at m;dH;5*RSA=5j3RW^mZJ3NWp2B
zwo0GlG(WX>7LGIioDA at rGJZ+obdE_F|1flep9>XVmpGq!=6 at yel+=u0lm2i7|3><I
zBlvacKN7*|JdtEV)m-Oy(x?7O!gaq7-RAz$6;n?g?*rrf%~CRtxGI0Q#A$x at bXgI6
zkMxg9&GSnaz$!U~mBqYWDAb)*uzY9D2kOpM2Roq*R=i at R+`w*W)t!nh!G>SYd#>AX
z2oEPgh|Nm5=HvrkNwG^MBq%_+=6Yb&JnYy{9$(nk at a^Jhu;_OnPuxa%@ty{)rc)6)
zpR<&A>vq+N^06scxD4MepgvP2{h!FsI7_?kuvcDyKO$9V-&NU$4q at sYp?HoSD147Y
z5ANv*yD$))leq85gm<N873Gp2#fW=Qg6B9<>L{1)ZxkcyrxN6QEYt7d)-gb5JjVpq
zazoanAO7&R`VReFBpJieu6|An!Y~=C<}=Erd(y7{nyjCa2~=P0dtCo8;zZ&5k7gii
zvi=VYNX_+GyNI)`QlF8BitBJJ%C#}jZvAh{pCDcrt)F at X_hp`!`aR9bJTwDYl=asb
zkechWzJNH}D)q;9m!b{#>kwO(+tr-nmduZ?i`UP at FVJZB{@3LD^Lx_!cj4fC#y>%<
z-TIR>Dar?-YCfafk1*1%{w-PmDVa(2lQ^RFPasBfM&I*uS$_&+s&IYQZ(vYAw&9_A
gLFIA(VT|gNa9-#E+(sk1_Z0h6){n~sQti&qUz*2sSO5S3
diff --git a/lldb/test/API/functionalities/disassembler-variables/regs_fp_params.o b/lldb/test/API/functionalities/disassembler-variables/regs_fp_params.o
deleted file mode 100644
index 2bec9fd94d98abc0ccc0315666b125ab0493bd74..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 3304
zcmc&$&2Jl35TEt#Ce8=>E=fpfqEahGqH8Bqby84?gqk*Pgb-2$2awrazl{yH*Yd7I
zXb->z^}qq~4<JB7+{guq6H+NHLa0Ec9=LGe$gx~Glo{_E at 5_rp2q7`j?#%q=>+SQ*
zByU~Z-pm*VFd1+GQjJl7&jwR at qbqK}G4%X>{G}WCe2vdde7?cw*3C!I!G>%%k%h7a
zmf>2~G3yh<{Sb^vBbS at D&0KqSVs;@nxiB>l&Aw;M0%RP6w2ir|^Jeb+$WtTB^IOx;
z%mbJ<WKJf~)->irbuLxyQ()#UjEsz6si<4p$Rfk?kEY*}!Q-ZsXvT{sWZH<Dc-*dF
z8QuU({vb=0EK6dDrC>Obz_zR`6flXMrvFEhUw50kOM9Xn)xze|>Z((43cIy<>2x)Y
zThaM^9+hgxbNsN8->Jv7h8v5`u<eW0m3+Ov*ErK^hp!1gb||R0?ibv3u_B5Uw-6Lp
zi~iY_3clC<b3w4?uC94ru~Ik}JPvKK8<m4rx#hOqM&$UAuZ9hg at 5l;4uR_Hu$0CaJ
zTg_G{j<h(agdMLgzy%L{sDOX~M!T2Lz53(={--ab;xow!z_|JX82b~(!suZ3eQa#P
zk4u}{5LLveWsArS!}w$DA!0NM_ at J=Z9SYM}%#7P7x-$H7eSP`jm9dZR{BZ5*hi`xS
ztOd3?3_}^29cJv&X?tRzWY3iBxsrXnWSgaofyw^Wh2szPck~k)Ex7-npAR;9KN!2-
z{0p3h_9&U4ECI!A!jPh+q6x#PqDUr8C|W9-FqJA&+C)C3oS%O)?lP*No_12k5l#ci
zejG<QW8}I>FC at b0>)7 at ffk=eYEm3b1v7PXNE|R?1&iEVB#$U?#b%}F-GXA;5=OkzR
zE2(qOGJZ?qOPc?;Qa`2P-%0(nhJP>hCp7$y)M>F1aoryiwRaCR3Tk&PNjqUGep2F;
zN9`6z!<VIgMRGkqI2GY_cFV5kwZ$GdvDlA+R%AmoQAmvS(684b+*wXr)LjWial0G_
zK_rguvecuOq1F^-j5w6RtyB&JwPp~4(+p$bTzYQjOccBR>)_CCE|YIm+dULt$D|Sv
z)pCIAS7=)mhup!cvFjm!vqAhnqF*sjziB^^O?+RgldQND97mBbwdcvYK`$hpuSA0*
z6Mg~>dQS4)kpW~^N%tjP$H<@l-V?o&u-;queHSe{M$WW1iFjTU?WpfNXrMi_!48st
zDVNW_)M>8Oy~FeTB;x(*Uy|Ti8P81jr8((WzaanX$jSh!ukJCfPxnkNJkKb0>6(nY
z%YfuupLqvymQ~m7Orr6gW1H^VLPNjvugQkubJF=~NAR9yocfD$>gRu1*55~)N?f1$
z2WVtj^}8qIRg7fa_jZD{boZYnpr4D+&v|d4(C_=7 at ALg*=*#i^{a+;YIbDxu<xtmq
zNFwOxKR~!&{i>{gS;o`-PvE21e-$yxseflI>#w0rC9co>V>FuE4?<*btA4zH7^C_`
Y9G7SSzoDStJ=qRqeeQpX>(@Vj0-oqJ5&!@I
diff --git a/lldb/test/API/functionalities/disassembler-variables/regs_int_params.o b/lldb/test/API/functionalities/disassembler-variables/regs_int_params.o
deleted file mode 100644
index 3f7d635f6cd560187493164b40aa8edbd1326f12..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 3296
zcmc&$&2Jl35TCc}%{Og6T9S~`Sfy5jMAuFzbyHBW)DRlAszjCWaX at Ch-c1bt2;NNy
zy at 0r&3XqVva098AN?gi?b5#%&6hYx%;K&8U5s5P6oyoqut`I^<Occ+|Z@%7po*DVA
zXD%&UN`a7qbC7C+0({z^%9Wm2fy3yj{`?>}q<&pex0ckMFCRjS0|UJbIh1o?snwiy
zAa`A at egbMt6$%rMUFc4ZPR<m at X2$!X$@kSHfaNOEDpOad?ZVmNCx=f=uS`5W4PZN9
zO(oFE1m;8aSgJb5!7iK|9v;S0QLnTbK!)WXPA_4>0m7IPO}$`))kVlwYnYOx9P|nF
zT)#+H21I&sP^6cJ5(%7~lY<h5I-L8TGQa7y*Yn#!H)?d+`Gp0y<d)VOaei(ijyI#T
z#Ud&jTQ%43w2EuZxY6?BV7b%vgN6BGv$@?mx!LW!8u+nGLFGli<ShpCLAmag!tz4d
zKQ&*+&qe=q7 at qMK&eUq<dg*ldD0GAMsM=`9)lIMKwIbJt;zp+x6t}oUxL2ZHtHwbT
z7gySwTXAFt0_Fe at YT!d10tjKG_XfR}PoCg^x*!!ll$-$6)hnQOM%Bzn|G<aX(1agh
zo8AurV$`u^<VvYO4m?1NCIJ6YnC%UNt!Ax*&e5I>KP;ZlfBf0C()r!@miwN#_)`uX
zdk6+C&JGVcBNNVOpX(fY$~odXc4*<4sDJm?_$B>4{fOoq{B|$P3wt~Z1{?pf$i_ at r
zWP>*Rnu864nwE+-45f-D*)XbUsc6G^s<5<-d|W%fd}q>SR6#%O7{?J#^C*6YHC!-)
zW;L8{C$>j4oL-4~n at H@GzpsZRA7U5$4YtWm1;56)^rzrA8J}V<_$}6 at X9fR?@jP?E
zzh?cof!|?$&cMH8{V at aIWu5jKk>vkAQTN_~MnUhcJlhG=@neir9=%&!13$s~MdoIH
zaO**BYrX2#YTaNP+&I{Yfi`3-z-`3FX2)+fBHUPRH)whcMsc^=3BxGJ*kq~8UW!IL
zsA9;z6kff)A85404!G at 39JtS3UOO4Zp8p!Sw41Bs8#UJV#Wyi21Hnc$#O)iHOBDy*
z#j>$iL;hxq_<uq_W0tIGzrjuXyHO=saVdBZMZ)x+Cu at a1kYp|s4ep!pqi8U5lJ8{>
zAiGYcFX<*m{!I>gnYl32_gyrY82Nt70Wz<w-sk%c8fec{#DV!uUOqdl(_HI&N9Or)
z#Ix#G7(B)C!c1S9ldSr4{G(%l1E{{f$E5!Ah?9%VGm1TmIqt3i%%#5YZNx>^U6-sx
z8`Z%K-`5e#I)8~9O3q2=ryU`CR&d(iRNp-RD%amZn@)08;UA$<WZiF<<8_Q=)AySM
z+ce$pGGLxd&M$dyqmXs~3mNyPS(13U{})MpNtbdS>PCw6B=h{ABbrtJ6|R4Q<LUkB
zyEN;2h*3`So(-<Qh&G+1zVPd4v at hN3AbVT)ll>zY)hCj;L<9H*1 at rC6c7y9n|5IF6
G|NI3cpe+Od
diff --git a/lldb/test/API/functionalities/disassembler-variables/regs_mixed_params.o b/lldb/test/API/functionalities/disassembler-variables/regs_mixed_params.o
deleted file mode 100644
index 26391797be2e29cf4bcc9004762955ba70d10e09..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 3440
zcmc(gO>7%g5P;|HdYvDK{5DELk~Ru;6DqoPLKG({D!C9yTOolOkqffc>viJdpWxku
zGzW?RArKt6AdWo%*CN3M4wVq?rK%7 at 2x$4a!V&0&0}^FscgEgqHWG*fPkNa*-^`nt
zw|;MO`LXjWno=M`!5L_2LIJYvt at X0MT!umPRDY>&@AjPhxzsa#`)<$rk2vq({2uk|
z*XPmEfOgPPcHz7q=QPd)XzD^j8&ABaE?hxvOj(IB(k#@5$5jzjpGv0(3_aZ#>Kz(Q
z_l@=^-J!SC5P)_Z^z at ljDg~gMiVVxcdn0;!DRnwEHL^PJ=m^F)KpPIw>HwC2>SRkz
z&h1m-GFD`O@(-wBymfpHmDbSoM7!=2^$Z?hiq4_dF!YQ5{x)7<9JUoYWc5^MU`!}G
z6|kh%1xX44TGB|sf**p^e?RPsRoj@@aT;#9UYnSmHM3 at RqwGygZ+hOAyOhbGvbkL_
z?Rqt{R`JSJ%X3!h4cnQW$y6#k)l*xI`bEd~Oxl!Nw6oTtGvnlnR<@Lz&DnD^Mf|&H
z&zDLI*6c!|kSk{AOAkWB*>LmK@~%_NZ&?ki>Y6rWHtSU<v&~6L%_PM_-g8_pvs&BQ
z_FO2|w+j{LU<9zBT=Sp+yKo6?*n at _DYv=|w$NzL%IKmaEy{AF#_NvjYw)VI2p%5Qq
zTlltAkx}6}luD^TyYC}GFAFCwj`@#FSI4v-<EXy|_r|sNwS}uIomX!yT|A+GnE*rY
zgbt0P!=N#8#28PSMz?89o;6OqY at 9r6=*e>f8s1*@55Ilf&n5}t<ktuj at cX?un-0y|
z_L+76!S6%9qGP1Ep+j37#wZ<!@XkYc?;*VZ0AE4UAF5w|JGp9<AzW=AM<8zC6u-d`
z7e?q<h|}rB_CSbBTO=fQil1a364W+n2!A0^K~5_CP3Ce>g at 43+n5FPfS(m#j{1)?x
zi2oO?ACK^_Sf7q?x;Hc)3PiL1Z&=Sp_;;+&Mfe at osYB9`I)4b%=69nf6uylUY$q1x
zlguej_;#5Q?)PS7=a@(1gIRP6+Z%bSP-r+iV0zB32h?M$4z at 5hR_b=8>|)0=8&1Vy
z=z5KOy;O1?<Og1(>_jJ7R?0Oe at 9Uu;R<Vc<AyBTB>R{IDo?|}#<k~6Mv+U=<r2d{K
zU$?xm&z*uYa5nQL?B6l{7(2R&i9M@;@=}%Le;&VJ1iJUpvA`|hkBA{?jV;)NOJd<S
zLe^#agG1hNph5GUh<+3o(U at d=mK~N at iirA>9%bbFE^nx?6cP1(0}W9|zB<EKWFyO{
z at 2kkEFD<iOL at zZZ9J_(=|1X96l)V3k(H at OSTV7-M2=5mW^`&<bm;ZInzrY))4~6 at l
z<X=ZlF7p0y>5tEO-%kQqN`8 at aK3RuduV{fLeNk*tUl$E=^?$|}O6sKgsYgg(7Eb-1
zVn*x#kn>Z0;UW1&zJo?thyAAF9z~4r&46ujx at L%J-XE<?>KDfwxQILd?U?g-qp$3j
z^M8UYuKsn-|GXq(8TI`bd0hVAIR6v8kG?<smeKq+GKv{J=S|Lk8g1br`9*$!M*aM~
kI*OOVe$s!0QGOb-FVFyf#YObnldYW&MUpp=h`T?313)rGtpET3
>From 78158a0bd5d79f36ad426423cf714f6f6c730042 Mon Sep 17 00:00:00 2001
From: ultimateforce21 <abdullahmohammad155 at gmail.com>
Date: Sun, 24 Aug 2025 18:27:18 -0400
Subject: [PATCH 48/48] Show constants in rich disassembler annotations when
using PrintRegisterOnly
Previously, when `DIDumpOptions::PrintRegisterOnly` was enabled, the disassembler
annotations only displayed registers. This omitted useful cases where variables
are assigned to constant values (e.g., `i = 0`).
This change updates the DWARF expression printer to still emit constants when
`PrintRegisterOnly` is active, while continuing to suppress other DWARF opcodes.
As a result, users now see clearer annotations for constants alongside registers.
---
.../DWARF/DWARFExpressionPrinter.cpp | 35 +++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/llvm/lib/DebugInfo/DWARF/DWARFExpressionPrinter.cpp b/llvm/lib/DebugInfo/DWARF/DWARFExpressionPrinter.cpp
index 3622ff3a886a1..505556feba4ec 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFExpressionPrinter.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFExpressionPrinter.cpp
@@ -51,6 +51,41 @@ static bool printOp(const DWARFExpression::Operation *Op, raw_ostream &OS,
OS << "<decoding error>";
return false;
}
+ // In "register-only" mode, still show simple constant-valued locations.
+ // This lets clients print annotations like "i = 0" when the location is
+ // a constant (e.g. DW_OP_constu/consts ... DW_OP_stack_value).
+ // We continue to suppress all other non-register ops in this mode.
+ if (DumpOpts.PrintRegisterOnly) {
+ // First, try pretty-printing registers (existing behavior below also does
+ // this, but we need to short-circuit here to avoid printing opcode names).
+ if ((Op->getCode() >= DW_OP_breg0 && Op->getCode() <= DW_OP_breg31) ||
+ (Op->getCode() >= DW_OP_reg0 && Op->getCode() <= DW_OP_reg31) ||
+ Op->getCode() == DW_OP_bregx || Op->getCode() == DW_OP_regx ||
+ Op->getCode() == DW_OP_regval_type) {
+ if (prettyPrintRegisterOp(U, OS, DumpOpts, Op->getCode(),
+ Op->getRawOperands()))
+ return true;
+ // If we couldn't pretty-print, fall through and suppress.
+ }
+
+ // Show constants (decimal), suppress everything else.
+ if (Op->getCode() == DW_OP_constu) {
+ OS << (uint64_t)Op->getRawOperand(0);
+ return true;
+ }
+ if (Op->getCode() == DW_OP_consts) {
+ OS << (int64_t)Op->getRawOperand(0);
+ return true;
+ }
+ if (Op->getCode() >= DW_OP_lit0 && Op->getCode() <= DW_OP_lit31) {
+ OS << (unsigned)(Op->getCode() - DW_OP_lit0);
+ return true;
+ }
+ if (Op->getCode() == DW_OP_stack_value)
+ return true; // metadata; don't print a token
+
+ return true; // suppress other opcodes silently in register-only mode
+ }
if (!DumpOpts.PrintRegisterOnly) {
StringRef Name = OperationEncodingString(Op->getCode());
More information about the llvm-commits
mailing list