Skip to content

JIT: Fix register move ordering bug in genConsumeBlockOp for CpObjUnroll#126118

Closed
Copilot wants to merge 2 commits intomainfrom
copilot/fix-passing-variable-tags-parameters-test
Closed

JIT: Fix register move ordering bug in genConsumeBlockOp for CpObjUnroll#126118
Copilot wants to merge 2 commits intomainfrom
copilot/fix-passing-variable-tags-parameters-test

Conversation

Copy link
Contributor

Copilot AI commented Mar 25, 2026

Under JitStressRegs, LSRA can assign the source address of a block copy to the register that is the fixed requirement for the destination (e.g. source → RDI, dest → RAX, with CpObjUnroll needing dst=RDI, src=RSI on Linux x64 SysV ABI). The original code always moved the destination first, clobbering the source:

mov rdi, rax   ; dst → dstReg  ← overwrites source already in rdi
mov rsi, rdi   ; src → srcReg  ← copies destination, not source → corrupt struct copy

This manifested as MetricsTests.PassingVariableTagsParametersTest corrupting the first KeyValuePair element when passing 4+ tags via params ReadOnlySpan<KeyValuePair<string, object?>> (which triggers inline-array-at-call-site codegen, producing a CpObjUnroll with GC-ref struct fields).

Changes

  • src/coreclr/jit/codegenlinear.cppgenConsumeBlockOp
    • After consuming all operands, detect when the source address's current register equals the required destination register (srcCurReg == dstReg). In that case, emit the source move first, then the destination — avoiding the clobber.
    • Only applies to CopyBlk with a GT_IND source; local sources use a LEA (no conflict possible).
    • Adds assert(dstAddr->GetRegNum() != srcReg) to surface any circular-dependency (register-swap) case, which LSRA is not expected to produce.
    • Corrects the function comment, which incorrectly claimed LSRA guarantees conflict-freedom when copying to fixed-register requirements.

Security

No security impact. Pure codegen correctness fix affecting register-constrained struct copy sequences.

Original prompt

This section details on the original issue you should resolve

<issue_title>Test failure: System.Diagnostics.Metrics.Tests.MetricsTests.PassingVariableTagsParametersTest</issue_title>
<issue_description>Failed in: runtime-coreclr libraries-jitstress2-jitstressregs 20260307.1

Failed tests:

net11.0-linux-Release-x64-jitstress2_jitstressregs2-AzureLinux.3.Amd64.Open
- System.Diagnostics.Metrics.Tests.MetricsTests.PassingVariableTagsParametersTest
net11.0-linux-Release-x64-jitstress2_jitstressregs0x80-AzureLinux.3.Amd64.Open
- System.Diagnostics.Metrics.Tests.MetricsTests.PassingVariableTagsParametersTest

Error message:

Microsoft.DotNet.RemoteExecutor.RemoteExecutionException : Remote process failed with an unhandled exception.

Stack trace:


Child exception:
  Xunit.Sdk.EqualException: Assert.Equal() Failure: Collections differ
           ↓ (pos 0)
Expected: [["K1"] = "V1", ["K2"] = "V2", ["K3"] = "V3", ["K4"] = "V4", ["K5"] = "V5", ···]
Actual:   [[null] = null, ["K2"] = "V2", ["K3"] = "V3", ["K4"] = "V4", ["K5"] = "V5", ···]
           ↑ (pos 0)
   at System.Diagnostics.Metrics.Tests.MetricsTests.<>c__DisplayClass32_0`1.<InstrumentPassingVariableTagsParametersTest>b__1(Instrument inst, T measurement, ReadOnlySpan`1 tags, Object state) in /_/src/libraries/System.Diagnostics.DiagnosticSource/tests/MetricsTests.cs:line 1965
   at System.Diagnostics.Metrics.Instrument`1.RecordMeasurement(T measurement, ReadOnlySpan`1 tags) in /_/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Instrument.common.cs:line 99
   at System.Diagnostics.Metrics.Tests.MetricsTests.PublishCounterMeasurement[T](Counter`1 counter, T value, KeyValuePair`2[] tags) in /_/src/libraries/System.Diagnostics.DiagnosticSource/tests/MetricsTests.cs:line 1791
   at System.Diagnostics.Metrics.Tests.MetricsTests.<PassingVariableTagsParametersTest>b__10_3(Instrument`1 instrument, Int32 value, KeyValuePair`2[] tags) in /_/src/libraries/System.Diagnostics.DiagnosticSource/tests/MetricsTests.cs:line 751
   at System.Diagnostics.Metrics.Tests.MetricsTests.InstrumentPassingVariableTagsParametersTest[T](Instrument`1 instrument, Func`4 record) in /_/src/libraries/System.Diagnostics.DiagnosticSource/tests/MetricsTests.cs:line 2015
   at System.Diagnostics.Metrics.Tests.MetricsTests.<PassingVariableTagsParametersTest>b__10_0() in /_/src/libraries/System.Diagnostics.DiagnosticSource/tests/MetricsTests.cs:line 749
   at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args) in /_/src/coreclr/System.Private.CoreLib/src/System/Reflection/MethodBaseInvoker.CoreCLR.cs:line 36
   at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr) in /_/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodBaseInvoker.cs:line 57

Child process:
  System.Diagnostics.DiagnosticSource.Tests, Version=11.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51 System.Diagnostics.Metrics.Tests.MetricsTests Void <PassingVariableTagsParametersTest>b__10_0()
```</issue_description>

<agent_instructions>Please look through the suggested fix in the latest comment and create a PR with the fix. </agent_instructions>

## Comments on the Issue (you are @copilot in this section)

<comments>
<comment_new><author>@mangod9</author><body>
Here is some copilot analysis for this failure:

```text 
Crash Context

  ┌────────────┬───────────────────────────────────────────────────────────────────────────────────────────────────┐
  │ Field      │ Value                                                                                             │
  ├────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────┤
  │ Build      │ runtime-coreclr libraries-jitstress2-jitstressregs 20260307.1                                     │
  │            │ (https://dev.azure.com/dnceng-public/cbb18261-c48f-4abb-8651-8cdcb5474649/_buil                   │
  │            │ d/results?buildId=1324993)                                                                        │
  ├────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────┤
  │ Helix Job  │ 3f3cc108-6550-42e6-80a5-5e70ed9c556a                                                              │
  │ (regs2)    │                                                                                                   │
  ├────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────┤
  │ Helix Job  │ acfbbeb5-9555-4c5b-b5ea-892f475ef22b                                                              │
  │ (regs0x80) │                                                                                                   │
  ├────────────┼───────────────────────────────────...

</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

- Fixes dotnet/runtime#125312

<!-- START COPILOT CODING AGENT TIPS -->
---

📱 Kick off Copilot coding agent tasks wherever you are with [GitHub Mobile](https://gh.io/cca-mobile-docs), available on iOS and Android.

@github-actions github-actions bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Mar 25, 2026
@dotnet-policy-service
Copy link
Contributor

Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch
See info in area-owners.md if you want to be subscribed.

Under JitStressRegs, LSRA can legitimately assign the source address to
the register that is fixed for the destination (e.g. source → RDI, dest
→ RAX, with CpObjUnroll requiring dst=RDI, src=RSI on Linux x64 SysV).

The original code always moved the destination first:
  mov rdi, rax   ; dst → dstReg (CLOBBERS source already in rdi!)
  mov rsi, rdi   ; src → srcReg (now copies wrong value)

Fix: detect when the source's current register equals the required
destination register, and if so swap the move order (source first):
  mov rsi, rdi   ; src → srcReg first
  mov rdi, rax   ; dst → dstReg (safe: source already moved)

Also add an assert to catch the circular-dependency case (both operands
in each other's required register), which LSRA is not expected to produce.

Fixes: System.Diagnostics.Metrics.Tests.MetricsTests.PassingVariableTagsParametersTest
under JitStress=2 + JitStressRegs={2,0x80} on Linux x64.

Co-authored-by: mangod9 <61718172+mangod9@users.noreply.github.com>
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/5680c424-5cec-471e-b646-b2dada458fed
Copilot AI changed the title [WIP] Fix test failure in MetricsTests.PassingVariableTagsParametersTest JIT: Fix register move ordering bug in genConsumeBlockOp for CpObjUnroll Mar 25, 2026
Copilot AI requested a review from mangod9 March 25, 2026 20:17
@mangod9
Copy link
Member

mangod9 commented Mar 26, 2026

Closing, not sure why copilot is creating multiple PRs

@mangod9 mangod9 closed this Mar 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants