llvm / llvm/llvm-project

[lldb] DW_OP_bit_piece extracts stack-value fragments but does not assemble them into the composite value

Open
#209,093 2 comments 0 reactions 0 assignees View on GitHub
lldb
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

LLDB's `DW_OP_bit_piece` extracts the requested bits from the current scalar in place, but neither consumes the source nor appends the extracted fragment to the composite piece buffer.

[DWARF v5 Section 2.6.1.2](https://dwarfstd.org/doc/DWARF5.pdf) describes `DW_OP_piece` and `DW_OP_bit_piece` as composition operations. In particular, `DW_OP_bit_piece` describes a bit range from the preceding location description; for an implicit value or stack value, that range is taken from the least significant bits of the value. In LLVM's [A.2.5.4 DWARF Operation Expressions](https://llvm.org/docs/AMDGPUDwarfExtensionAllowLocationDescriptionOnTheDwarfExpressionStack/AMDGPUDwarfExtensionAllowLocationDescriptionOnTheDwarfExpressionStack.html#a-2-5-4), the "Composite Location Description Operations" subsection also defines `DW_OP_bit_piece` as having the same action as `DW_OP_piece`, except that the created part has a bit size and an applied bit displacement.

The scalar branch violates that composition contract directly in the evaluator: it modifies the source value in place, resets the location-description kind to `Memory`, and finishes without adding a fragment to the composite result.

## Source Evidence

In [`DWARFExpression.cpp`](https://github.com/llvm/llvm-project/blob/main/lldb/source/Expression/DWARFExpression.cpp), `Evaluate_DW_OP_piece` consumes the current source fragment, updates it according to the current location-description kind, and appends it to `eval_ctx.pieces`. That creates an accumulated composite result.

The `DW_OP_bit_piece` handler does not follow the same composite contract for scalar sources. It updates the current top-of-stack value in place:

```cpp
case DW_OP_bit_piece:
...
UpdateValueTypeFromLocationDescription(eval_ctx, eval_ctx.loc_desc_kind,
&stack.back());
eval_ctx.loc_desc_kind = Memory;
...
case Value::ValueType::Scalar: {
if (!stack.back().GetScalar().ExtractBitfield(piece_bit_size,
piece_bit_offset)) {
...
}
} break;
```

That path does not pop the consumed fragment or append anything to `eval_ctx.pieces`. This differs from `Evaluate_DW_OP_piece`, which consumes the source and appends the resulting fragment to the accumulated composite value.

At the end of evaluation, LLDB returns `eval_ctx.pieces` only when the expression stack is empty; otherwise it returns `stack.back()`:

```cpp
if (stack.empty()) {
if (eval_ctx.pieces.GetBuffer().GetByteSize())
return eval_ctx.pieces;
return llvm::createStringError("stack empty after evaluation");
}

UpdateValueTypeFromLocationDescription(eval_ctx, eval_ctx.loc_desc_kind,
&stack.back());
return stack.back();
```

Because the scalar `DW_OP_bit_piece` path leaves its source on the stack, `stack.empty()` is false and the evaluator returns that source after interpreting it with the reset `Memory` location kind. The extracted fragment is therefore returned as a memory location instead of becoming part of `eval_ctx.pieces`.

Contributor guide

Open the contributing guide

Research direction

Start in lldb/source/Expression/DWARFExpression.cpp at the DW_OP_bit_piece handler and compare its scalar path with Evaluate_DW_OP_piece. Trace how eval_ctx.pieces and the final stack-empty check determine the returned value. Done means a scalar bit_piece fragment is consumed into the composite piece buffer and the evaluator returns the accumulated composite result.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
devtools
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.