Skip to content

Add MRV debug info for multi-register FP/mixed struct returns on Unix x64#129992

Open
tommcdon wants to merge 2 commits into
dotnet:mainfrom
tommcdon:dev/tommcdon/mrv-multireg-fp-debuginfo
Open

Add MRV debug info for multi-register FP/mixed struct returns on Unix x64#129992
tommcdon wants to merge 2 commits into
dotnet:mainfrom
tommcdon:dev/tommcdon/mrv-multireg-fp-debuginfo

Conversation

@tommcdon

Copy link
Copy Markdown
Member

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

tommcdon and others added 2 commits June 26, 2026 20:08
… 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>
@tommcdon tommcdon added this to the 11.0.0 milestone Jun 29, 2026
@tommcdon tommcdon self-assigned this Jun 29, 2026
Copilot AI review requested due to automatic review settings June 29, 2026 18:19
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @steveisok, @tommcdon, @dotnet/dotnet-diag
See info in area-owners.md if you want to be subscribed.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 VarLocType encodings (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 storeVariableInTwoRegisters helper 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_FPSTK read 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.

Comment on lines +301 to +306
// 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);
Comment on lines +100 to +105
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;
Comment on lines +266 to +269
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)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Managed Return Value debug info cannot encode multi-register floating-point return values

3 participants