Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow explicitly implemented ISimdVector APIs to be handled as intrinsic #104983

Merged
merged 2 commits into from
Aug 1, 2024

Conversation

tannergooding
Copy link
Member

This should improve the handling of generics constrained to ISimdVector on all runtimes and also ensures that get_IsHardwareAccelerated has the correct behavior in R2R (otherwise it would return a different result from querying the concrete API).

Copy link
Contributor

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

@tannergooding
Copy link
Member Author

CC. @dotnet/jit-contrib, @fanyang-mono, @kg

As per the title this fixes the handling so that the explicitly implemented get_IsHardwareAccelerated from ISimdVector returns the correct result for both direct and indirect invocation.

It also ensures that the explicitly implemented ISimdVector APIs get the same handling as the regular intrinsic APIs to ensure that they don't pessimize the inlining budget and don't get unnecessarily viewed as generic when the types are well known. -- For Mono, there are still cases they'll hit the pessimized generic handling of course, but it should be more consistent now.

if (!strcmp (cmethod->name, "get_IsHardwareAccelerated")) {
const char* cmethod_name = cmethod->name;

if (strncmp(cmethod_name, "System.Runtime.Intrinsics.ISimdVector<System.Runtime.Intrinsics.Vector", 70) == 0) {
Copy link
Contributor

Choose a reason for hiding this comment

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

if memory serves, MonoMethod->name is just the method name, not the fullname, so these tests won't work. i think you would want to do mono_method_get_name_full, but that allocates so it would slow down interp code transforms to a probably unacceptable degree. looking at m_class_get_name(cmethod->klass) plus the method name might be enough. i'm not sure where the generic args go in the name, if anywhere.

Copy link
Member Author

Choose a reason for hiding this comment

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

This is checking just the method name., the full name includes the prefix System.Runtime.Intrinsics.Vector128<T>.

For C#, explicitly implemented intrinsics have names in the format of FullNameOfInterface.Name. For example see: https://sharplab.io/#v2:EYLgxg9gTgpgtADwGwBYA0AXEBLANgHwAEAmARgFgAoKgOwEMBbGAZwAc6wYACAWQE8AcoxbtOVAN5Uu0roQDMXbDQwwoAMw7cAkvy3LVGsZRldJxkzMIpeACgCUAbikyAvlWfT5s4lwDCXEC4dPj0VdU0PU0iTLytbO1MuN3MLLmjLa2DQg00AOh57ROSTZJcgA

Note that we have both:

.method public hidebysig 
        instance void M () cil managed 

and

    .method private final hidebysig newslot virtual 
        instance void MyNamespace.IMyInterface.M () cil managed 

Copy link
Member Author

Choose a reason for hiding this comment

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

-- so the method name is MyNamespace.IMyInterface.M and the full name of the method is MyNamespace.C::MyNamespace.IMyInterface.M (where I used :: to separate the class name from the method name)

Copy link
Contributor

Choose a reason for hiding this comment

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

I see, so in this specific case due to how we implement the interface methods, the name resembles a fullname

@tannergooding
Copy link
Member Author

I expect we can let this one can slip to .NET 10. Since ISimdVector is internal, the overall usage is limited.

Copy link
Member

@EgorBo EgorBo left a comment

Choose a reason for hiding this comment

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

Jit side LGTM

int id = lookup_intrins (sri_vector_methods, sizeof (sri_vector_methods), cmethod);
const char *cmethod_name = cmethod->name;

if (strncmp(cmethod_name, "System.Runtime.Intrinsics.ISimdVector<System.Runtime.Intrinsics.Vector", 70) == 0) {
Copy link
Member

Choose a reason for hiding this comment

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

It would be nice to create a helper function to share the logic cross emit_sri_vector and emit_sri_vector_t.

Copy link
Member Author

Choose a reason for hiding this comment

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

I agree, but I had initially viewed that as a larger/riskier refactoring this late in the .NET 9 cycle

Would you be fine with me logging an issue to track the cleanup for .NET 10? -- I think in practice we want S.Numerics and S.R.intrinsics to share the same importation helper and for any functionality to be differentiated based on the vector size. This is how we do it in RyuJIT since the behavior is typically functionally identical and the only real difference tends to be metadata passed into the IR nodes created (i.e. Vector4.operator + and Vector128.operator + are identical modulo the type handle; and Vector64/128/256/512<T> and Vector<T> are all the same modulo the vector size, etc).

Copy link
Member

Choose a reason for hiding this comment

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

That sounds great!

int id = lookup_intrins (sri_vector128_methods, sizeof (sri_vector128_methods), cmethod);
const char *cmethod_name = cmethod->name;

if (strncmp(cmethod_name, "System.Runtime.Intrinsics.ISimdVector<System.Runtime.Intrinsics.Vector", 70) == 0) {
Copy link
Member

Choose a reason for hiding this comment

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

It would be nice to create a helper function to share the logic.

Copy link
Member

@fanyang-mono fanyang-mono left a comment

Choose a reason for hiding this comment

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

Mono change looks good to me overall.

@tannergooding
Copy link
Member Author

hmmm, @fanyang-mono seeing some perf improvements, but its more like 1-5% in places; not what we were expecting and not to the level of resolving the regressions.

For x64, there's the general issue of #105319 which is blocking most SIMD expansion.

But for Arm64 there's still some cases that aren't being recognized and expanded, but I don't have enough context to dig into that and root cause it. It's possible that this is similar to the issue Egor was looking at for RyuJIT and is related to Generic Virtual Methods (#93174) and is places that Mono isn't able to see that it can be resolved to a concrete call and therefore can be expanded as an intrinsic; but its also possible its something else.

@fanyang-mono
Copy link
Member

@tannergooding Thanks for running the microbenchmarks. Let's go ahead and merge this, since there will be some improvements. And we could dig more later, when we have some bandwidth.

@tannergooding tannergooding merged commit ae30cba into dotnet:main Aug 1, 2024
177 checks passed
@tannergooding tannergooding deleted the isimdvector branch August 1, 2024 16:11
@github-actions github-actions bot locked and limited conversation to collaborators Sep 1, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants