Add MRV debug info for multi-register FP/mixed struct returns on Unix x64#129992
Add MRV debug info for multi-register FP/mixed struct returns on Unix x64#129992tommcdon wants to merge 2 commits into
Conversation
… x64 Adds managed-return-value (MRV) debug-info encoding for value-class returns that live in two registers where at least one is a floating-point register (e.g. a 16-byte struct returned in XMM0+XMM1, or a mixed ValueTuple<double,int> returned in XMM0+RAX on SysV x64). Previously these were silently skipped. - New VarLocType values VLT_REG_FP_REG_FP, VLT_REG_FP_REG, VLT_REG_REG_FP. - JIT producer (scopeinfo/codegencommon) emits the appropriate encoding via storeVariableInTwoRegisters instead of skipping non-integer register pairs. - Serializer (debuginfostore) encodes the two register indices. - DBI consumer reconstructs the value via a snapshot-based TwoRegisterValueHome (64-bit only). - CordbType::ReturnedByValue permits FP/generic fields only for two-register (>8 byte) returns, leaving single-register value classes unsupported. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CordbJITILFrame::GetNativeVariable returned CORDBG_E_IL_VAR_NOT_AVAILABLE for the VLT_FPSTK location kind, leaving the implementation commented out since the initial CoreCLR commit. On x86, floating-point values (including return values, surfaced via GetReturnValueForILOffset) live on the x87 FP stack and are reported as VLT_FPSTK, so inspecting a float/double return or local failed with an unavailable-variable error. Enable the existing GetLocalFloatingPointValue path for TARGET_X86, which already handles loading the x87 stack state and R4/R8 conversion. Behavior on non-x86 targets is unchanged (ARM remains E_NOTIMPL; others retain the CORDBG_E_IL_VAR_NOT_AVAILABLE fallback, where VLT_FPSTK is never produced). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Tagging subscribers to this area: @steveisok, @tommcdon, @dotnet/dotnet-diag |
There was a problem hiding this comment.
Pull request overview
This PR extends CoreCLR debug-info (ICorDebugInfo VarLocType / MRV encoding) to represent value-class locations spanning two registers where at least one register is a floating-point register, enabling managed-return-value inspection for Unix x64 multi-reg FP/mixed struct returns and adding x86 x87 FP-stack (VLT_FPSTK) support in CordbJITILFrame::GetNativeVariable.
Changes:
- Add new
VarLocTypeencodings (VLT_REG_FP_REG_FP,VLT_REG_FP_REG,VLT_REG_REG_FP) and thread them through varloc equality, debug-info encoding/decoding, and tooling (R2R/AOT readers/writers). - Update the JIT’s scope/return-value debug-info emission to use a new
storeVariableInTwoRegistershelper that can encode int+fp / fp+fp register pairs (not just int+int). - Add debugger-side decoding/building of values for the new encodings via a two-register snapshot home, plus implement the missing x86
VLT_FPSTKread path.
Reviewed changes
Copilot reviewed 19 out of 19 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/coreclr/vm/util.cpp | Extend VarLoc equality to treat new two-register FP/mixed loc types like VLT_REG_REG. |
| src/coreclr/vm/debuginfostore.cpp | Encode the two register indices for new loc types using the existing vlRegReg layout. |
| src/coreclr/tools/r2rdump/Extensions.cs | Add display handling for new loc types (currently prints raw indices). |
| src/coreclr/tools/Common/JitInterface/CorInfoTypes.VarInfo.cs | Add new VarLocType enum values for tooling/shared interface parity. |
| src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/DebugInfoTypes.cs | Mirror new VarLocType values for R2R reflection reader. |
| src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/DebugInfo.cs | Parse new loc types as two uint payload fields. |
| src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/DebugInfoTableNode.cs | Emit new loc types with two register fields in var blobs. |
| src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectWriter/CodeView/CodeViewSymbolsBuilder.cs | Recognize new loc types in CodeView symbol emission logic. |
| src/coreclr/jit/scopeinfo.cpp | Add storeVariableInTwoRegisters; emit mixed/fp two-reg locations for SysV AMD64 params; update equality/dumps/assert-sync. |
| src/coreclr/jit/ee_il_dll.cpp | Print new loc types correctly in JIT debug dumps by mapping fp indices back to fp regs. |
| src/coreclr/jit/codegeninterface.h | Add new loc type enum members and the new storeVariableInTwoRegisters API. |
| src/coreclr/jit/codegencommon.cpp | Emit MRV return location using storeVariableInTwoRegisters; keep 32-bit guardrails. |
| src/coreclr/inc/cordebuginfo.h | Add new VarLocType values and document the vlRegReg reuse contract for fp/mixed pairs. |
| src/coreclr/debug/ee/functioninfo.cpp | Add logging for the new loc types in native-var dumps. |
| src/coreclr/debug/ee/debugger.cpp | Treat new loc types as “enregistered” for valuetype size/classification decisions. |
| src/coreclr/debug/di/valuehome.cpp | Make reg-reg reads tolerant of struct sizes < 2 registers; add TwoRegisterValueHome::GetEnregisteredValue; relax offset assumption for multi-reg returns. |
| src/coreclr/debug/di/rstype.cpp | Expand CordbType::ReturnedByValue gating for 64-bit multi-reg return scenarios, including mixed and generic-parameter fields (two-reg only). |
| src/coreclr/debug/di/rsthread.cpp | Add GetLocalTwoRegisterValue; handle new loc types in GetNativeVariable; implement x86 VLT_FPSTK read path. |
| src/coreclr/debug/di/rspriv.h | Declare GetLocalTwoRegisterValue and introduce TwoRegisterValueHome. |
| // The low half occupies the first register-sized chunk; the high half the second. | ||
| // The out buffer may be smaller than two registers (e.g. a 12-byte struct returned | ||
| // in two 8-byte registers), so clamp each copy to the bytes that actually remain. | ||
| const SIZE_T cbReg = sizeof(*lowWordAddr); | ||
| const SIZE_T cbTotal = valueOutBuffer.Size(); | ||
| _ASSERTE(cbTotal <= 2 * cbReg); |
| case VarLocType.VLT_REG_FP_REG_FP: | ||
| case VarLocType.VLT_REG_FP_REG: | ||
| case VarLocType.VLT_REG_REG_FP: | ||
| writer.WriteLine($" Register 1: {varLoc.VariableLocation.Data1}"); | ||
| writer.WriteLine($" Register 2: {varLoc.VariableLocation.Data2}"); | ||
| break; |
| VLT_REG_FP_REG_FP, // variable lives in two fp registers (e.g. a 16-byte struct returned in XMM0+XMM1 on Unix x64) | ||
| VLT_REG_FP_REG, // low part lives in an fp register, high part in an int register (mixed multi-reg return) | ||
| VLT_REG_REG_FP, // low part lives in an int register, high part in an fp register (mixed multi-reg return) | ||
|
|
There was a problem hiding this comment.
Would it be messier to make RegNum include FP registers too instead? It would likely look quite a bit more natural on the JIT side.
There was a problem hiding this comment.
This representation also does not scale very well, e.g. HFAs on arm32/arm64 can be returned in up to 4 registers (albeit not mixed registers). Also struct locals can have their fields enregistered in an arbitrary number of mixed registers, so a representation like this excludes us from ever trying to represent that.
Adds managed-return-value (MRV) debug-info encoding for value-class returns that live in two registers. Additionally, this change implements CordbJITILFrame::GetNativeVariable for x86 x87 FP-stack return/local variables (missed scenario from #129321)
Fixes #129344