Skip to content

JIT: fix switch peeling stealing the wrong operand of its compare - #132395

Open
EgorBo wants to merge 1 commit into
dotnet:mainfrom
EgorBo:fix-switch-peel-operand-swap
Open

JIT: fix switch peeling stealing the wrong operand of its compare#132395
EgorBo wants to merge 1 commit into
dotnet:mainfrom
EgorBo:fix-switch-peel-operand-swap

Conversation

@EgorBo

@EgorBo EgorBo commented Aug 17, 2026

Copy link
Copy Markdown
Member

fgPeelSwitch reattached the switch value via fgMakeMultiUse(&compare->gtOp1) after the compare was put into a statement. Sequencing runs gtSetEvalOrder, which swaps a compare's operands when op1 is a constant, so the switch got the dominant case constant instead of the switch value.

The switch value is a constant when the loop around the switch is unrolled, so each unrolled copy dispatched on case 0 -> wrong results.

Fix: build the compare fully before creating the statement.

No asm diffs (benchmarks.run, libraries.pmi).

Fixes #132370

Copilot AI lite review requested due to automatic review settings August 17, 2026 09:59
@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 Aug 17, 2026
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 8 pipeline(s).
8 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

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

@EgorBo
EgorBo force-pushed the fix-switch-peel-operand-swap branch 2 times, most recently from 3eedfbe to de3ab29 Compare August 17, 2026 10:09

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

Fixes a JIT correctness issue in fgPeelSwitch where operand swapping during statement sequencing could cause the peeled switch to dispatch on the wrong value, and adds a new Arm64 LSE atomics “light-up” path via a new Lse intrinsic plus associated JIT/AOT plumbing. Also introduces a JIT regression test for the switch-peeling miscompile. (Not built/tested as part of this review.)

Changes:

  • JIT: build the dominant-case compare fully before statement creation in fgPeelSwitch to avoid operand swapping changing which operand is “stolen”.
  • Arm64 atomics: introduce System.Runtime.Intrinsics.Arm.Lse and route Interlocked atomic ops through if (Lse.IsSupported) where applicable; update JIT importer/lowering/LSRA/codegen and AOT/R2R metadata/tooling to support it.
  • Tests: add Runtime_132370 regression test reproducing the miscompile scenario.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/tests/JIT/Regression/JitBlue/Runtime_132370/Runtime_132370.cs Adds regression coverage for the switch-peeling/unroll miscompile.
src/libraries/System.Private.CoreLib/src/System/Threading/Interlocked.cs Adds Arm64 Lse.IsSupported fast-paths for And/Or on word-sized integers.
src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Arm/Lse.cs Introduces new internal Arm64 LSE intrinsic surface used by Interlocked.
src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems Includes the new Lse.cs source file in CoreLib builds.
src/coreclr/tools/Common/JitInterface/ThunkGenerator/InstructionSetDesc.txt Names the ARM64 atomics instruction set implementation type as Lse.
src/coreclr/tools/Common/JitInterface/CorInfoInstructionSet.cs Maps System.Runtime.Intrinsics.Arm.Lse to ARM64_Atomics and roots the type.
src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunHardwareIntrinsicRootProvider.cs Avoids rooting generic intrinsic method definitions for R2R compilation.
src/coreclr/System.Private.CoreLib/src/System/Threading/Interlocked.CoreCLR.cs Adds Arm64 Lse.IsSupported light-up for key atomic Interlocked APIs (CoreCLR flavor).
src/coreclr/nativeaot/System.Private.CoreLib/src/System/Threading/Interlocked.cs Adds Arm64 Lse.IsSupported light-up for key atomic Interlocked APIs (NativeAOT flavor).
src/coreclr/jit/lsraarm64.cpp Uses per-node atomics implementation choice to adjust internal regs / lifetimes.
src/coreclr/jit/lowerarmarch.cpp Disables contained immediates for atomic ops when emitting LSE forms.
src/coreclr/jit/jitconfigvalues.h Adds Arm64 stress config for exercising managed atomics light-up.
src/coreclr/jit/importercalls.cpp Leaves select Interlocked calls unexpanded in the “dynamic” atomics mode to enable managed light-up.
src/coreclr/jit/hwintrinsiclistarm64.h Adds Arm64 Atomics intrinsic definitions (CompareAndSwap/LoadAdd/LoadClear/LoadSet/Swap).
src/coreclr/jit/hwintrinsicarm64.cpp Maps Atomics intrinsics to existing atomic IR nodes and enforces type constraints.
src/coreclr/jit/hwintrinsic.cpp Registers Atomics intrinsic range so they’re recognized as a real ISA group.
src/coreclr/jit/gentree.h Adds GTF_ATOMIC_LSE flag to distinguish LSE-vs-LL/SC expansion.
src/coreclr/jit/gentree.cpp Prevents CSE/merging of atomic nodes that differ by GTF_ATOMIC_LSE.
src/coreclr/jit/fgopt.cpp Fixes fgPeelSwitch sequencing so the peeled compare steals the correct operand.
src/coreclr/jit/compiler.h Introduces AtomicsImpl + per-node selection helper for atomic expansion strategy.
src/coreclr/jit/compiler.cpp Implements cached atomics expansion mode selection (Lse/LlSc/Dynamic).
src/coreclr/jit/codegenarm64.cpp Uses per-node atomics expansion decision to emit LSE instructions vs LL/SC loops.

Comment thread src/tests/JIT/Regression/JitBlue/Runtime_132370/Runtime_132370.cs
Comment thread src/tests/JIT/Regression/JitBlue/Runtime_132370/Runtime_132370.cs Outdated
Copilot AI review requested due to automatic review settings August 17, 2026 10:10

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

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

fgPeelSwitch creates "EQ(switchValue, dominantCase)" for the peeled check, puts
it into a statement and only then reattaches the switch value to the switch via
fgMakeMultiUse(&compare->gtOp1).

Creating the statement sequences the tree, and gtSetEvalOrder is free to swap the
operands of a compare - it does exactly that when op1 is a constant ("encourage
constants to be evaluated last"). The switch value is a constant whenever the
loop containing the switch was unrolled, so the switch ended up dispatching on
the dominant case constant instead of on its own value: every unrolled copy of
the switch jumped to the case 0 target, silently producing wrong results.

Build the compare completely (including the multi-use and the flag updates)
before handing it to fgNewStmtFromTree.

Fixes dotnet#132370

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: bd309226-4fb2-49aa-8586-b3dad59f3a36
Copilot AI review requested due to automatic review settings August 17, 2026 10:28
@EgorBo
EgorBo force-pushed the fix-switch-peel-operand-swap branch from de3ab29 to dc550f6 Compare August 17, 2026 10:28

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

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

@EgorBo

EgorBo commented Aug 17, 2026

Copy link
Copy Markdown
Member Author

PTAL, user-reported bug, small fix, no diffs @jakobbotsch @dotnet/jit-contrib

@EgorBo
EgorBo requested a review from jakobbotsch August 17, 2026 16:46
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.

JIT: unrolled loop re-dispatches its switch with the index folded to 0 (wrong code, new in .NET 10)

2 participants