<feed xmlns='http://www.w3.org/2005/Atom'>
<title>llvm-project.git/lldb/test/API/functionalities/disassembler-variables/Makefile, branch main</title>
<subtitle>Unnamed repository; edit this file 'description' to name the repository.
</subtitle>
<link rel='alternate' type='text/html' href='https://git.belthelziquor.com/llvm-project.git/'/>
<entry>
<title>Stateful variable-location annotations in Disassembler::PrintInstructions() (follow-up to #147460) (#152887)</title>
<updated>2025-08-28T17:41:38+00:00</updated>
<author>
<name>Abdullah Mohammad Amin</name>
<email>67847674+UltimateForce21@users.noreply.github.com</email>
</author>
<published>2025-08-28T17:41:38+00:00</published>
<link rel='alternate' type='text/html' href='https://git.belthelziquor.com/llvm-project.git/commit/?id=8ec4db5e17f654670cd0d5e43e3a57a9b39b4f9d'/>
<id>8ec4db5e17f654670cd0d5e43e3a57a9b39b4f9d</id>
<content type='text'>
**Context**  
Follow-up to
[#147460](https://github.com/llvm/llvm-project/pull/147460), which added
the ability to surface register-resident variable locations.
This PR moves the annotation logic out of `Instruction::Dump()` and into
`Disassembler::PrintInstructions()`, and adds lightweight state tracking
so we only print changes at range starts and when variables go out of
scope.

---

## What this does

While iterating the instructions for a function, we maintain a “live
variable map” keyed by `lldb::user_id_t` (the `Variable`’s ID) to
remember each variable’s last emitted location string. For each
instruction:

- **New (or newly visible) variable** → print `name = &lt;location&gt;` once
at the start of its DWARF location range, cache it.
- **Location changed** (e.g., DWARF range switched to a different
register/const) → print the updated mapping.
- **Out of scope** (was tracked previously but not found for the current
PC) → print `name = &lt;undef&gt;` and drop it.

This produces **concise, stateful annotations** that highlight variable
lifetime transitions without spamming every line.

---

## Why in `PrintInstructions()`?

- Keeps `Instruction` stateless and avoids changing the
`Instruction::Dump()` virtual API.
- Makes it straightforward to diff state across instructions (`prev →
current`) inside the single driver loop.

---

## How it works (high-level)

1. For the current PC, get in-scope variables via
`StackFrame::GetInScopeVariableList(/*get_parent=*/true)`.
2. For each `Variable`, query
`DWARFExpressionList::GetExpressionEntryAtAddress(func_load_addr,
current_pc)` (added in #144238).
3. If the entry exists, call `DumpLocation(..., eDescriptionLevelBrief,
abi)` to get a short, ABI-aware location string (e.g., `DW_OP_reg3 RBX →
RBX`).
4. Compare against the last emitted location in the live map:
   - If not present → emit `name = &lt;location&gt;` and record it.
   - If different → emit updated mapping and record it.
5. After processing current in-scope variables, compute the set
difference vs. the previous map and emit `name = &lt;undef&gt;` for any that
disappeared.

Internally:
- We respect file↔load address translation already provided by
`DWARFExpressionList`.
- We reuse the ABI to map LLVM register numbers to arch register names.

---

## Example output (x86_64, simplified)

```
-&gt;  0x55c6f5f6a140 &lt;+0&gt;:  cmpl   $0x2, %edi                                                         ; argc = RDI, argv = RSI
    0x55c6f5f6a143 &lt;+3&gt;:  jl     0x55c6f5f6a176            ; &lt;+54&gt; at d_original_example.c:6:3
    0x55c6f5f6a145 &lt;+5&gt;:  pushq  %r15
    0x55c6f5f6a147 &lt;+7&gt;:  pushq  %r14
    0x55c6f5f6a149 &lt;+9&gt;:  pushq  %rbx
    0x55c6f5f6a14a &lt;+10&gt;: movq   %rsi, %rbx
    0x55c6f5f6a14d &lt;+13&gt;: movl   %edi, %r14d
    0x55c6f5f6a150 &lt;+16&gt;: movl   $0x1, %r15d                                                        ; argc = R14
    0x55c6f5f6a156 &lt;+22&gt;: nopw   %cs:(%rax,%rax)                                                    ; i = R15, argv = RBX
    0x55c6f5f6a160 &lt;+32&gt;: movq   (%rbx,%r15,8), %rdi
    0x55c6f5f6a164 &lt;+36&gt;: callq  0x55c6f5f6a030            ; symbol stub for: puts
    0x55c6f5f6a169 &lt;+41&gt;: incq   %r15
    0x55c6f5f6a16c &lt;+44&gt;: cmpq   %r15, %r14
    0x55c6f5f6a16f &lt;+47&gt;: jne    0x55c6f5f6a160            ; &lt;+32&gt; at d_original_example.c:5:10
    0x55c6f5f6a171 &lt;+49&gt;: popq   %rbx                                                               ; i = &lt;undef&gt;
    0x55c6f5f6a172 &lt;+50&gt;: popq   %r14                                                               ; argv = RSI
    0x55c6f5f6a174 &lt;+52&gt;: popq   %r15                                                               ; argc = RDI
    0x55c6f5f6a176 &lt;+54&gt;: xorl   %eax, %eax
    0x55c6f5f6a178 &lt;+56&gt;: retq  
```

Only transitions are shown: the start of a location, changes, and
end-of-lifetime.

---

## Scope &amp; limitations (by design)

- Handles **simple locations** first (registers, const-in-register cases
surfaced by `DumpLocation`).
- **Memory/composite locations** are out of scope for this PR.
- Annotations appear **only at range boundaries** (start/change/end) to
minimize noise.
- Output is **target-independent**; register names come from the target
ABI.

## Implementation notes

- All annotation printing now happens in
`Disassembler::PrintInstructions()`.
- Uses `std::unordered_map&lt;lldb::user_id_t, std::string&gt;` as the live
map.
- No persistent state across calls; the map is rebuilt while walking
instruction by instruction.
- **No changes** to the `Instruction` interface.

---

## Requested feedback

- Placement and wording of the `&lt;undef&gt;` marker.
- Whether we should optionally gate this behind a setting (currently
always on when disassembling with an `ExecutionContext`).
- Preference for immediate inclusion of tests vs. follow-up patch.

---

Thanks for reviewing! Happy to adjust behavior/format based on feedback.

---------

Co-authored-by: Jonas Devlieghere &lt;jonas@devlieghere.com&gt;
Co-authored-by: Adrian Prantl &lt;adrian.prantl@gmail.com&gt;</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
**Context**  
Follow-up to
[#147460](https://github.com/llvm/llvm-project/pull/147460), which added
the ability to surface register-resident variable locations.
This PR moves the annotation logic out of `Instruction::Dump()` and into
`Disassembler::PrintInstructions()`, and adds lightweight state tracking
so we only print changes at range starts and when variables go out of
scope.

---

## What this does

While iterating the instructions for a function, we maintain a “live
variable map” keyed by `lldb::user_id_t` (the `Variable`’s ID) to
remember each variable’s last emitted location string. For each
instruction:

- **New (or newly visible) variable** → print `name = &lt;location&gt;` once
at the start of its DWARF location range, cache it.
- **Location changed** (e.g., DWARF range switched to a different
register/const) → print the updated mapping.
- **Out of scope** (was tracked previously but not found for the current
PC) → print `name = &lt;undef&gt;` and drop it.

This produces **concise, stateful annotations** that highlight variable
lifetime transitions without spamming every line.

---

## Why in `PrintInstructions()`?

- Keeps `Instruction` stateless and avoids changing the
`Instruction::Dump()` virtual API.
- Makes it straightforward to diff state across instructions (`prev →
current`) inside the single driver loop.

---

## How it works (high-level)

1. For the current PC, get in-scope variables via
`StackFrame::GetInScopeVariableList(/*get_parent=*/true)`.
2. For each `Variable`, query
`DWARFExpressionList::GetExpressionEntryAtAddress(func_load_addr,
current_pc)` (added in #144238).
3. If the entry exists, call `DumpLocation(..., eDescriptionLevelBrief,
abi)` to get a short, ABI-aware location string (e.g., `DW_OP_reg3 RBX →
RBX`).
4. Compare against the last emitted location in the live map:
   - If not present → emit `name = &lt;location&gt;` and record it.
   - If different → emit updated mapping and record it.
5. After processing current in-scope variables, compute the set
difference vs. the previous map and emit `name = &lt;undef&gt;` for any that
disappeared.

Internally:
- We respect file↔load address translation already provided by
`DWARFExpressionList`.
- We reuse the ABI to map LLVM register numbers to arch register names.

---

## Example output (x86_64, simplified)

```
-&gt;  0x55c6f5f6a140 &lt;+0&gt;:  cmpl   $0x2, %edi                                                         ; argc = RDI, argv = RSI
    0x55c6f5f6a143 &lt;+3&gt;:  jl     0x55c6f5f6a176            ; &lt;+54&gt; at d_original_example.c:6:3
    0x55c6f5f6a145 &lt;+5&gt;:  pushq  %r15
    0x55c6f5f6a147 &lt;+7&gt;:  pushq  %r14
    0x55c6f5f6a149 &lt;+9&gt;:  pushq  %rbx
    0x55c6f5f6a14a &lt;+10&gt;: movq   %rsi, %rbx
    0x55c6f5f6a14d &lt;+13&gt;: movl   %edi, %r14d
    0x55c6f5f6a150 &lt;+16&gt;: movl   $0x1, %r15d                                                        ; argc = R14
    0x55c6f5f6a156 &lt;+22&gt;: nopw   %cs:(%rax,%rax)                                                    ; i = R15, argv = RBX
    0x55c6f5f6a160 &lt;+32&gt;: movq   (%rbx,%r15,8), %rdi
    0x55c6f5f6a164 &lt;+36&gt;: callq  0x55c6f5f6a030            ; symbol stub for: puts
    0x55c6f5f6a169 &lt;+41&gt;: incq   %r15
    0x55c6f5f6a16c &lt;+44&gt;: cmpq   %r15, %r14
    0x55c6f5f6a16f &lt;+47&gt;: jne    0x55c6f5f6a160            ; &lt;+32&gt; at d_original_example.c:5:10
    0x55c6f5f6a171 &lt;+49&gt;: popq   %rbx                                                               ; i = &lt;undef&gt;
    0x55c6f5f6a172 &lt;+50&gt;: popq   %r14                                                               ; argv = RSI
    0x55c6f5f6a174 &lt;+52&gt;: popq   %r15                                                               ; argc = RDI
    0x55c6f5f6a176 &lt;+54&gt;: xorl   %eax, %eax
    0x55c6f5f6a178 &lt;+56&gt;: retq  
```

Only transitions are shown: the start of a location, changes, and
end-of-lifetime.

---

## Scope &amp; limitations (by design)

- Handles **simple locations** first (registers, const-in-register cases
surfaced by `DumpLocation`).
- **Memory/composite locations** are out of scope for this PR.
- Annotations appear **only at range boundaries** (start/change/end) to
minimize noise.
- Output is **target-independent**; register names come from the target
ABI.

## Implementation notes

- All annotation printing now happens in
`Disassembler::PrintInstructions()`.
- Uses `std::unordered_map&lt;lldb::user_id_t, std::string&gt;` as the live
map.
- No persistent state across calls; the map is rebuilt while walking
instruction by instruction.
- **No changes** to the `Instruction` interface.

---

## Requested feedback

- Placement and wording of the `&lt;undef&gt;` marker.
- Whether we should optionally gate this behind a setting (currently
always on when disassembling with an `ExecutionContext`).
- Preference for immediate inclusion of tests vs. follow-up patch.

---

Thanks for reviewing! Happy to adjust behavior/format based on feedback.

---------

Co-authored-by: Jonas Devlieghere &lt;jonas@devlieghere.com&gt;
Co-authored-by: Adrian Prantl &lt;adrian.prantl@gmail.com&gt;</pre>
</div>
</content>
</entry>
</feed>
