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
[bcl][jit] implement Interlocked.Exchange<T> in terms of object #17341
Conversation
H/T to @lewurm who did the hard work of identifying the cause. |
Exchange<T> is supposed to be picked up as an intrinsic. But if it isn't, since T has a class constraint, call the `Exchange (ref object, ref object, ref object)` overload instead. This works around a limitation in the JIT: https://github.com/mono/mono/blob/82a07273c22b996b08fa88ee2e6632d782ac2242/mono/mini/mini-trampolines.c#L704-L713 if we're in the common call trampoline, and the caller is generic method and the callee is a generic method, if we're using generic sharing for the caller, but there's no generic jit info for the callee, we will not patch the call site. In this case we had Exchange<T> calling Exchange_T<T> where the callee is an icall (and evidently didn't have generic jit info). So every time we called Exchange<T>, we would go through the trampoline when trying to call Exchange_T<T> without patching the call site. Addresses part of mono#17334
85f5b4b
to
357c9c8
Compare
@monojenkins build deb with monolite |
I should also get rid of |
@monojenkins build deb with monolite |
Not sure this will work: |
It might need some Unsafe method usage. |
Did I break it when I did coop conversion? |
I don't think so, since I thought I preserved the layering. Only change should be, when it wasn't inlined/intrinsic. Let me double check.. |
Yea this doesn't work. @jaykrell the issue is that there's an extra generic method->generic icall callsite now. And it's hitting a condition where the trampoline code is deciding whether it can patch that callsite. Separately, it happens that |
I.e.: |
@vargaz thanks! |
Should probably add some [Intrinsic] attributes as well. |
|
Mark the non-icall methods that we expect the JIT to treet as intrinsics with [Intrinsic]
mono/mini/aot-runtime.c
Outdated
@@ -4810,34 +4810,14 @@ mono_aot_get_method (MonoDomain *domain, MonoMethod *method, MonoError *error) | |||
gboolean volatil = FALSE; | |||
MonoMethodSignature *sig; | |||
|
|||
/* Same for CompareExchange<T> and Exchange<T> */ | |||
/* Same for Volatile.Read<T>/Write<T> */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe similar to do here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh yea that looks like dead code. We already have method bodies like this.
In framework Mono:
mono/mcs/class/corlib/System.Threading/Volatile.cs
Lines 166 to 172 in 73ae9db
[Intrinsic] | |
public static T Read<T>(ref T location) where T : class => | |
Unsafe.As<T>(Unsafe.As<T, VolatileObject>(ref location).Value); | |
[Intrinsic] | |
public static void Write<T>(ref T location, T value) where T : class => | |
Unsafe.As<T, VolatileObject>(ref location).Value = value; |
and in netcore Mono:
mono/netcore/System.Private.CoreLib/shared/System/Threading/Volatile.cs
Lines 220 to 229 in 73ae9db
[Intrinsic] | |
[NonVersionable] | |
[return: NotNullIfNotNull("location")] | |
public static T Read<T>(ref T location) where T : class? => | |
Unsafe.As<T>(Unsafe.As<T, VolatileObject>(ref location).Value); | |
[Intrinsic] | |
[NonVersionable] | |
public static void Write<T>([NotNullIfNotNull("value")] ref T location, T value) where T : class? => | |
Unsafe.As<T, VolatileObject>(ref location).Value = value; |
Could do: object v = value;
object c = comparand;
Exchange (...., ref v, ref c, ...); |
Both of those methods are implemented as calls to the object overload of Volatile.Read/Write
@monojenkins build deb with monolite |
@monojenkins build failed |
@monojenkins build |
The netcore build is missing a using: |
netcore errors.
|
In mono#17341 we change the caller of CompareExchange_T<T>(ref T, ...) to call CompareExchange (ref object, ...), but didn't delete the declaration. It's already deleted in unmanaged and in the netcore version of this file.
In mono#17341 we change the caller of CompareExchange_T<T>(ref T, ...) to call CompareExchange (ref object, ...), but didn't delete the declaration. It's already deleted in unmanaged and in the netcore version of this file.
In #17341 we change the caller of CompareExchange_T<T>(ref T, ...) to call CompareExchange (ref object, ...), but didn't delete the declaration. It's already deleted in unmanaged and in the netcore version of this file.
From Nathan, with this fix and this one [1] Mono/LLVM/.NET5 configuration stands like this:
So about 80% of the performance of .NET Core 3 now, looking good! [1] #17338 |
…ect (#17372) * [bcl][jit] implement Interlocked.Exchange<T> in terms of object Exchange<T> is supposed to be picked up as an intrinsic. But if it isn't, since T has a class constraint, call the `Exchange (ref object, ref object, ref object)` overload instead. This works around a limitation in the JIT: https://github.com/mono/mono/blob/82a07273c22b996b08fa88ee2e6632d782ac2242/mono/mini/mini-trampolines.c#L704-L713 if we're in the common call trampoline, and the caller is generic method and the callee is a generic method, if we're using generic sharing for the caller, but there's no generic jit info for the callee, we will not patch the call site. In this case we had Exchange<T> calling Exchange_T<T> where the callee is an icall (and evidently didn't have generic jit info). So every time we called Exchange<T>, we would go through the trampoline when trying to call Exchange_T<T> without patching the call site. Addresses part of #17334 * Bump mono corlib version * Also drop CompareExchange_T<T> * Sprinkle some Unsafe magic. Mark the non-icall methods that we expect the JIT to treet as intrinsics with [Intrinsic] * aot-runtime doesn't need Volatile.Read<T>/Write<T> code Both of those methods are implemented as calls to the object overload of Volatile.Read/Write * fix netcore build * one more IntrinsicAttribute * [bcl] Remove CompareExchange_T In #17341 we change the caller of CompareExchange_T<T>(ref T, ...) to call CompareExchange (ref object, ...), but didn't delete the declaration. It's already deleted in unmanaged and in the netcore version of this file. * [bcl] Add null reference checks to Interlocked.Exchange<T> and Interlocked.CompareExchange<T> This is to mimic the old behavior in unmanaged when the intrinsic cannot be used. The issue was detected via the coreclr acceptance test: https://github.com/mono/coreclr/blob/mono/tests/src/baseservices/threading/interlocked/exchange/exchangetneg.il * and netcore * Another Mono corlib version bump
…/mono#17341) * [bcl][jit] implement Interlocked.Exchange<T> in terms of object Exchange<T> is supposed to be picked up as an intrinsic. But if it isn't, since T has a class constraint, call the `Exchange (ref object, ref object, ref object)` overload instead. This works around a limitation in the JIT: https://github.com/mono/mono/blob/mono/mono@82a07273c22b996b08fa88ee2e6632d782ac2242/mono/mini/mini-trampolines.c#L704-L713 if we're in the common call trampoline, and the caller is generic method and the callee is a generic method, if we're using generic sharing for the caller, but there's no generic jit info for the callee, we will not patch the call site. In this case we had Exchange<T> calling Exchange_T<T> where the callee is an icall (and evidently didn't have generic jit info). So every time we called Exchange<T>, we would go through the trampoline when trying to call Exchange_T<T> without patching the call site. Addresses part of mono/mono#17334 * Bump Mono * Also drop CompareExchange_T<T> * Sprinkle some Unsafe magic. Mark the non-icall methods that we expect the JIT to treet as intrinsics with [Intrinsic] * aot-runtime doesn't need Volatile.Read<T>/Write<T> code Both of those methods are implemented as calls to the object overload of Volatile.Read/Write Commit migrated from mono/mono@f017835
Commit migrated from mono/mono@91f10ae
Exchange<T>
is supposed to be picked up as an intrinsic. But if it isn't (until #17338 is merged),since
T
has a class constraint, call theExchange (ref object, ref object, ref object)
overload instead.This works around a limitation in the JIT:
mono/mono/mini/mini-trampolines.c
Lines 704 to 713 in 82a0727
if we're in the common call trampoline, and the caller is generic method and the callee is a generic method, if we're using generic sharing for the caller, but there's no generic jit info for the callee, we will not patch the call site.
In this case we had
Exchange<T>
callingExchange_T<T>
where the callee is an icall (and evidently didn't have generic jit info).
So every time we called
Exchange<T>
, we would go through the trampoline when trying to callExchange_T<T>
without patching the call site.Addresses part of #17334