Fix Avx.Compare miscompile with non-constant FloatComparisonMode - #131482
Open
tannergooding wants to merge 1 commit into
Open
Fix Avx.Compare miscompile with non-constant FloatComparisonMode#131482tannergooding wants to merge 1 commit into
tannergooding wants to merge 1 commit into
Conversation
A non-constant mode makes Compare defer to its managed fallback via a user call. When the intrinsic is promoted to the mask-returning form, the result is wrapped in a mask-to-vector conversion, so attaching the fallback handle to that wrapper left the rewritten call missing its right/mode operands. Attach the handle to the inner mask node instead. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Member
Author
|
@JulieLeeMSFT this is a bug fix (pre-existing issue, not new this release) and should be considered for .NET 11 |
|
Azure Pipelines: Successfully started running 5 pipeline(s). 11 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
Contributor
|
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch |
Contributor
There was a problem hiding this comment.
Pull request overview
This PR aims to fix a CoreCLR JIT miscompile in the Avx.Compare / Avx512F.Compare non-constant FloatComparisonMode path (FullOpts on EVEX-capable hardware) by adjusting where the JIT attaches the “rewrite as user call” method handle, and adds a JIT regression test to cover the scenario.
Changes:
- Update
impHWIntrinsicto retargetSetMethodHandlefrom aConvertMaskToVectorwrapper to its inner operand. - Add a new JitBlue regression test for variable
FloatComparisonModeover all 32 modes forVector256<float>andVector512<float>. - Include the new regression test source in the
Regression_ro_2.csprojcompilation list.
Show a summary per file
| File | Description |
|---|---|
| src/coreclr/jit/hwintrinsic.cpp | Changes method-handle attachment logic for late-stage intrinsic→user-call rewriting when a mask-to-vector wrapper is present. |
| src/tests/JIT/Regression/JitBlue/GitHub_131472/GitHub_131472.cs | Adds regression coverage comparing FullOpts vs NoOptimization results across all FloatComparisonMode values for AVX/AVX512. |
| src/tests/JIT/Regression/Regression_ro_2.csproj | Wires the new test into the merged regression project build. |
Copilot's findings
- Files reviewed: 3/3 changed files
- Comments generated: 1
This was referenced Jul 28, 2026
Open
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #131472.
Avx.Compare/Avx512F.Compareproduced garbage or AV'd under FullOpts when theFloatComparisonModeargument was not a compile-time constant (e.g. a loop induction variable). It reproduces on shipped11.0.0-preview.6withDOTNET_TieredCompilation=0; tiering masked it.Root cause
A non-constant mode has no register-variable encoding, so
Comparedefers to its managed fallback via a user call (setMethodHandleinimpHWIntrinsic). On EVEX-capable hardware the intrinsic is promoted to the mask-returning form and wrapped in aConvertMaskToVectornode. The shared attach block tagged that unary wrapper rather than the inner 3-operand compare, so the rationalizer built a call carrying onlyleft—right/modewere never set up, and the call fell through to the self-recursive managed body on uninitialized registers.512-bit is affected for the same reason and cannot avoid the mask:
NI_AVX512_ComparecarriesHW_Flag_InvalidNodeId, so the mask form is mandatory.Fix
In the rare
setMethodHandlepath, when the return node is aConvertMaskToVector, attach the fallback handle to its inner mask operand instead of the wrapper. This runs only for the non-constant-imm-under-optimization case (no common-path cost) and covers 128/256-bit EVEX and mandatory-mask 512-bit uniformly.ConvertMaskToVectoris unary on both xarch and arm64, soOp(1)is unambiguous; asserts guard against future wrong-node tagging.Testing
GitHub_131472) coveringVector256<float>(optional EVEX promotion) andVector512<float>(mandatory mask), comparing FullOpts against a MinOpts reference over all 32 modes. Fails without the fix, passes with it.Comparecodegen is provably unchanged (the new code only executes whensetMethodHandleis set, which never happens for a constant mode); verified correct results.jitformatclean.Note
This change was authored with the assistance of GitHub Copilot.