Add return value variable homes#128397
Conversation
Historically we've used CALL_INSTRUCTION source mapping entries to imply return value homes for IL call instructions. That encoding requires debuggers to embed architecture specific disassembly and ABI logic to infer the where the return value is live and where it will be stored. Adding an explicit return value home shifts that responsibility to the code generator in exchange for making the encoded data slightly larger (likely 2-3 extra nibbles per callsite). We only expect this data to be produced for debug code.
|
Tagging subscribers to this area: @dotnet/runtime-infrastructure |
There was a problem hiding this comment.
Pull request overview
This PR updates the DebugInfo data contract design document to describe an explicit encoding for “call return value homes” in the Vars stream, instead of relying on CALL_INSTRUCTION source mappings and ABI inference in debuggers.
Changes:
- Extends the documented
DebugVarInfoshape with aReturnValueILOffsetfield. - Introduces a
CALL_RETURN_ILNUMsentinel var number and updates the documented var-number bias constant. - Updates the Vars nibble encoding to conditionally interpret field #2 as either
endOffsetDeltaorreturnValueILOffset.
| public uint StartOffset { get; init; } | ||
| public uint EndOffset { get; init; } | ||
| public uint VarNumber { get; init; } | ||
| public uint ReturnValueILOffset { get; init; } |
| | `MAX_ILNUM` | Bias for adjusted encoding of variable numbers | `0xfffffffb` (-5) | | ||
| | `CALL_RETURN_ILNUM` | A sentinel variable number indicating this variable home stores a call return value | `0xfffffffb` (-5) | |
| Each variable entry in the Vars section is nibble-encoded as follows: | ||
|
|
||
| 1. `startOffset` — encoded unsigned 32-bit integer | ||
| 2. `endOffset` — encoded as delta from `startOffset` (unsigned) | ||
| 2. `endOffset` or `returnValueILOffset` depending on the following `varNumber` | ||
|
|
||
| If varNumber == CALL_RETURN_ILNUM: `returnValueILOffset` encoded as unsigned 32-bit integer. `EndOffset` is implicitly `StartOffset+1`. `ReturnValueILOffset` identifies the offset of the IL call instruction whose return value is stored in this home. | ||
| If varNumber != CALL_RETURN_ILNUM: `endOffset` encoded as delta from `startOffset` (unsigned). `ReturnValueILOffset` is implicitly zero though it is unused and meaningless in this case. | ||
|
|
||
| 3. `varNumber` — encoded as adjusted unsigned (`value - MAX_ILNUM`) |
How is this done in C/C++ debugger? Does the debugger embed architecture specific details, or is the home encoded in debug info? |
My understanding is C/C++ debuggers, if they support the feature at all, do it by embedding all the architecture knowledge and reverse engineering it from the assembly. They can use the PDB to lookup function return type information but the rest would be calculated using disassembly and known ABIs. .NET could implement it that way too but IMO it pushes an unnecessary amount of detail into the data contract. |
Historically we've used CALL_INSTRUCTION source mapping entries to imply return value homes for IL call instructions. That encoding requires debuggers to embed architecture specific disassembly and ABI logic to infer the where the return value is live and where it will be stored. Adding an explicit return value home shifts that responsibility to the code generator in exchange for making the encoded data slightly larger (likely 2-3 extra nibbles per callsite). We only expect this data to be produced for debug code.
@kotlarmilos @matouskozak @jakobbotsch @AndyAyersMS @jkotas @davidwrighton @dotnet/dotnet-diag
Following up from my comment on #127760 - this is a strawman design for how we could adjust the debug info contract. I wanted to get feedback if folks like the direction as well as this particular way of encoding it. If we keep going in this direction I imagine we'd: