Skip to content

Remove ShuffleNativeFallback - #134792

Open
BrzVlad wants to merge 3 commits into
dotnet:mainfrom
BrzVlad:feature-remove-shuffle-native-fallback
Open

BrzVlad wants to merge 3 commits into
dotnet:mainfrom
BrzVlad:feature-remove-shuffle-native-fallback

Conversation

@BrzVlad

@BrzVlad BrzVlad commented Sep 28, 2026

Copy link
Copy Markdown
Member

Direct ShuffleNative calls are expanded at the caller and preserve the underlying hardware behavior for out-of-range indices. Calling ShuffleFallback directly from the public method would make indirect invocations through reflection, delegates, or function pointers execute the deterministic fallback instead, which zeroes out-of-range elements and can disagree with direct calls in the same process.

ShuffleNativeFallback existed to avoid that mismatch. The public method called a separate intrinsic helper, giving the JIT a second opportunity to emit ShuffleNative when compiling the public method itself. If expansion was unavailable, the helper was non-recursive, so its managed body could safely call ShuffleFallback.

Replace this secondary intrinsic with the standard recursive intrinsic pattern. The public ShuffleNative methods recursively call themselves when their required hardware support is available. Recursive intrinsic calls are must-expand, so direct and indirect invocation use the same JIT expansion; when acceleration is unavailable, control reaches ShuffleFallback without recursion.

Vector512 byte and sbyte shuffles require AVX512 VBMI even when Vector512 is otherwise hardware accelerated. Guard those overloads with Avx512Vbmi.IsSupported, allowing AVX512 targets without VBMI to use the managed fallback instead of failing recursive expansion. Mark these methods as CompExactlyDependsOn and CompHasFallback to disable compilation of the methods when the ISA is not known at R2R compilation time. This addresses issue of R2R code using the managed fallback, with no negative fixup recorded since negative dependencies from IsSupported are normally suppressed within SPC, while at runtime we could also have code using the Avx512Vbmi path.

Remove NI_Vector_ShuffleNativeFallback and add reflection coverage that verifies direct and indirect ShuffleNative calls agree.

Direct ShuffleNative calls are expanded at the caller and preserve the underlying hardware behavior for out-of-range indices. Calling ShuffleFallback directly from the public method would make indirect invocations through reflection, delegates, or function pointers execute the deterministic fallback instead, which zeroes out-of-range elements and can disagree with direct calls in the same process.

ShuffleNativeFallback existed to avoid that mismatch. The public method called a separate intrinsic helper, giving the JIT a second opportunity to emit ShuffleNative when compiling the public method itself. If expansion was unavailable, the helper was non-recursive, so its managed body could safely call ShuffleFallback.

Replace this secondary intrinsic with the standard recursive intrinsic pattern. The public ShuffleNative methods recursively call themselves when their required hardware support is available. Recursive intrinsic calls are must-expand, so direct and indirect invocation use the same JIT expansion; when acceleration is unavailable, control reaches ShuffleFallback without recursion.

Vector512 byte and sbyte shuffles require AVX512 VBMI even when Vector512 is otherwise hardware accelerated. Guard those overloads with `Avx512Vbmi.IsSupported`, allowing AVX512 targets without VBMI to use the managed fallback instead of failing recursive expansion. Mark these methods as `CompExactlyDependsOn` and `CompHasFallback` to disable compilation of the methods when the ISA is not known at R2R compilation time. This addresses issue of R2R code using the managed fallback, with no negative fixup recorded since negative dependencies from `IsSupported` are normally suppressed within SPC, while at runtime we could also have code using the `Avx512Vbmi` path.

Remove NI_Vector_ShuffleNativeFallback and add reflection coverage that verifies direct and indirect ShuffleNative calls agree.
@azure-pipelines

Copy link
Copy Markdown
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.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/area-system-runtime-intrinsics
See info in area-owners.md if you want to be subscribed.

System.Runtime.Intrinsics.Tests failed with:

System.InvalidProgramException:
The JIT compiler encountered invalid IL code or an internal limitation.
public static Vector256<byte> ShuffleNative(Vector256<byte> vector, Vector256<byte> indices)
{
#if !MONO
if (IsHardwareAccelerated)

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.

So I don't think this alone is sufficient, particularly because of DOTNET_EnablePreferredVectorBitWidth=128 in which case Vector256.IsHardwareAccelerated is false but we still generate AVX2 code

I have some changes that I think work and it mostly involves replacing these with the correct Isa.IsSupported checks that the JIT keys codegen off of instead; then passing down mustExpand so we can make the right decision.

Is it alright if I push that change up to the PR here?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes, feel free to push here, it will just require another reviewer. I was initially hoping the hardware accelerated check would be consistent with the generation of vectorized/fallback code. It's unfortunate that we have these special cases.

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.

Agreed. Some of its necessary for to ensure user testing and production scenarios work well, but I think the new changes handle it in a way that keeps it understandable and within what we support.

Use ISA support guards independent of preferred vector width and expand guarded recursive shuffles immediately with optimistic ISA support.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

This branch has not been deployed

No deployments
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.

2 participants