[release/9.0] JIT: Preserve range check for HW intrinsics with non-const/out-of-range immediates #107006
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.
Manual backport of #106765 to release/9.0
Customer Impact
Internal fuzz testing exposed multiple cases (#106608, #106546, etc) where the usage of a hardware intrinsic with an out-of-range, non-const immediate would throw an exception in Debug builds, but not in Release builds. In Debug builds, if the immediate is non-const, the JIT would convert the intrinsic into a user call with a range check. In Release, the JIT might be able to convert the immediate into a constant via later optimizations like forward substitution, so it wouldn't pessimistically convert the intrinsic into a user call during importation. However, the JIT wouldn't always emit a range check to represent the exceptional path in IR, so even if IR rationalization would eventually convert an intrinsic with an unknown immediate into a user call, earlier optimizations could optimize away the intrinsic completely, hence the Debug/Release diffs.
In .NET 10, we would like to add fallback intrinsic implementations for cases where the immediate is out-of-range or non-const, not only to eliminate Debug/Release diffs, but to also improve the usability and performance of these intrinsics in non-const immediate scenarios. However, the scope of this work is too large to target .NET 9. For now, this PR adjusts the JIT's importation logic for HW intrinsics to always emit a range check for non-const immediates, and to always convert to a user call if the immediate is known to be out-of-range.
While working on this, we noticed the JIT was calculating the vector count for some variants of
AdvSimd.StoreSelectedScalar
incorrectly. The variants in question operate onValueTuples
ofVectors
, rather thanVectors
directly, so we had to add a special case to the importer to parse the intrinsic's SIMD size from theValueTuple
argument.Regression
I'm not sure how long the potential for Debug/Release diffs for intrinsics with immediates has been possible. We found this problem after updating one of our fuzzing tools to generate tests with vectorized code. Since Roslyn already warns users to not use non-const immediates with intrinsics marked with
ConstantExpected
, the code pattern required to trigger this issue is explicitly discouraged, thus decreasing the odds of encountering this. However,AdvSimd.StoreSelectedScalar
's SIMD size calculation was regressed during the .NET 9 cycle when itsValueTuple
overloads were added in #93223.Testing
This fix includes regression tests picked from the generated programs that exposed this issue.
Risk
Low. The
StoreSelectedScalar
fix is quite targeted; while there are other intrinsics withValueTuple
arguments, their SIMD sizes are hard-coded, so the JIT doesn't need to go down the newly-addedValueTuple
path to parse the SIMD size at runtime. As for the range check fix, while this touches the common path for importing HW intrinsics across all supported platforms, the range check can be trivially optimized away by the JIT if the immediate is later known to be in-range, so code quality is not impacted. And as mentioned above, the usage of non-const immediates is likely uncommon in users' programs to begin with.