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

[bcl][jit] implement Interlocked.Exchange<T> in terms of object #17341

Merged
merged 7 commits into from Oct 16, 2019

Conversation

lambdageek
Copy link
Member

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 the Exchange (ref object, ref object, ref object) overload instead.

This works around a limitation in the JIT:

if (plt_entry) {
if (generic_shared) {
target_ji =
mini_jit_info_table_find (mono_domain_get (), (char *)mono_get_addr_from_ftnptr (compiled_method), NULL);
if (!ji)
ji = mini_jit_info_table_find (mono_domain_get (), (char*)code, NULL);
if (ji && ji->has_generic_jit_info) {
if (target_ji && !target_ji->has_generic_jit_info) {
no_patch = TRUE;

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

@lambdageek
Copy link
Member Author

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
@lambdageek
Copy link
Member Author

@monojenkins build deb with monolite

@lambdageek
Copy link
Member Author

I should also get rid of CompareExchange_T<T> for the same reason, actually

@lambdageek
Copy link
Member Author

@monojenkins build deb with monolite

@vargaz
Copy link
Contributor

vargaz commented Oct 15, 2019

Not sure this will work:
src/System.Threading/Interlocked.cs(94,25): error CS1503: Argument 1: cannot convert from 'ref T' to 'ref object' [/Users/vargaz/git/netcore/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj]

@vargaz
Copy link
Contributor

vargaz commented Oct 15, 2019

It might need some Unsafe method usage.

@jaykrell
Copy link
Contributor

Did I break it when I did coop conversion?

@jaykrell
Copy link
Contributor

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

@lambdageek
Copy link
Member Author

lambdageek commented Oct 15, 2019

Yea this doesn't work.
@vargaz I don't think there's an Unsafe method that does this.

@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 Exchange<T> is an intrinsic that isn't being intrinsifies correctly, and that's exposing this second issue. See #17335

@vargaz
Copy link
Contributor

vargaz commented Oct 15, 2019

I.e.:
CompareExchange (ref Unsafe.As<T, object> (ref location1), ref Unsafe.As<T, object> (ref value), ref Unsafe.As<T, object> (ref comparand), ref Unsafe.As<T, object> (ref result));

@lambdageek
Copy link
Member Author

@vargaz thanks!

@vargaz
Copy link
Contributor

vargaz commented Oct 15, 2019

Should probably add some [Intrinsic] attributes as well.

@jaykrell
Copy link
Contributor

jaykrell commented Oct 15, 2019

  • So, it wasn't being intrinsified. Not my fault. A bit slow.
  • But then, when I changed it, I also broke patching, making it much slower.
  • Both are fixable.
    ?

Mark the non-icall methods that we expect the JIT to treet as intrinsics with [Intrinsic]
@@ -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> */
Copy link
Contributor

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?

Copy link
Member Author

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:

[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:

[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;

@lambdageek
Copy link
Member Author

Does comparand need it?

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
@lambdageek
Copy link
Member Author

@monojenkins build deb with monolite

@vargaz
Copy link
Contributor

vargaz commented Oct 16, 2019

@monojenkins build failed

@lambdageek
Copy link
Member Author

@monojenkins build

@lambdageek lambdageek marked this pull request as ready for review October 16, 2019 02:19
@vargaz
Copy link
Contributor

vargaz commented Oct 16, 2019

The netcore build is missing a using:
https://dev.azure.com/dnceng/public/_build/results?buildId=388963

@jaykrell
Copy link
Contributor

netcore errors.

  Restore completed in 337.08 ms for /home/vsts/work/1/s/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj.
/home/vsts/.nuget/packages/microsoft.sourcelink.common/1.0.0-beta2-19367-01/build/Microsoft.SourceLink.Common.targets(50,5): warning : Source control information is not available - the generated source link is empty. [/home/vsts/work/1/s/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj]
src/System.Threading/Interlocked.cs(94,25): error CS0103: The name 'Unsafe' does not exist in the current context [/home/vsts/work/1/s/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj]
src/System.Threading/Interlocked.cs(94,68): error CS0103: The name 'Unsafe' does not exist in the current context [/home/vsts/work/1/s/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj]
src/System.Threading/Interlocked.cs(94,106): error CS0103: The name 'Unsafe' does not exist in the current context [/home/vsts/work/1/s/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj]
src/System.Threading/Interlocked.cs(94,148): error CS0103: The name 'Unsafe' does not exist in the current context [/home/vsts/work/1/s/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj]
src/System.Threading/Interlocked.cs(119,18): error CS0103: The name 'Unsafe' does not exist in the current context [/home/vsts/work/1/s/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj]
src/System.Threading/Interlocked.cs(119,59): error CS0103: The name 'Unsafe' does not exist in the current context [/home/vsts/work/1/s/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj]
src/System.Threading/Interlocked.cs(119,97): error CS0103: The name 'Unsafe' does not exist in the current context [/home/vsts/work/1/s/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj]

Build FAILED.

/home/vsts/.nuget/packages/microsoft.sourcelink.common/1.0.0-beta2-19367-01/build/Microsoft.SourceLink.Common.targets(50,5): warning : Source control information is not available - the generated source link is empty. [/home/vsts/work/1/s/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj]
src/System.Threading/Interlocked.cs(94,25): error CS0103: The name 'Unsafe' does not exist in the current context [/home/vsts/work/1/s/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj]
src/System.Threading/Interlocked.cs(94,68): error CS0103: The name 'Unsafe' does not exist in the current context [/home/vsts/work/1/s/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj]
src/System.Threading/Interlocked.cs(94,106): error CS0103: The name 'Unsafe' does not exist in the current context [/home/vsts/work/1/s/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj]
src/System.Threading/Interlocked.cs(94,148): error CS0103: The name 'Unsafe' does not exist in the current context [/home/vsts/work/1/s/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj]
src/System.Threading/Interlocked.cs(119,18): error CS0103: The name 'Unsafe' does not exist in the current context [/home/vsts/work/1/s/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj]
src/System.Threading/Interlocked.cs(119,59): error CS0103: The name 'Unsafe' does not exist in the current context [/home/vsts/work/1/s/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj]
src/System.Threading/Interlocked.cs(119,97): error CS0103: The name 'Unsafe' does not exist in the current context [/home/vsts/work/1/s/netcore/System.Private.CoreLib/System.Private.CoreLib.csproj]
    1 Warning(s)

@lambdageek lambdageek merged commit f017835 into mono:master Oct 16, 2019
lambdageek added a commit to lambdageek/mono that referenced this pull request Oct 16, 2019
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.
lambdageek added a commit to lambdageek/mono that referenced this pull request Oct 16, 2019
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.
lambdageek added a commit that referenced this pull request Oct 17, 2019
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.
@migueldeicaza
Copy link
Contributor

From Nathan, with this fix and this one [1] Mono/LLVM/.NET5 configuration stands like this:

Mono/LLVM/.NET5 before change (req/sec):  1,478,095.55
Mono/LLVM/.NET5 after chang (req/sec) :   5,414,536.91
.NET Core 3/RyuJIT (req/sec):             6,626,015.90

So about 80% of the performance of .NET Core 3 now, looking good!

[1] #17338

filipnavara added a commit to filipnavara/mono that referenced this pull request Oct 19, 2019
vargaz pushed a commit that referenced this pull request Oct 20, 2019
lambdageek added a commit that referenced this pull request Oct 23, 2019
…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
ManickaP pushed a commit to ManickaP/runtime that referenced this pull request Jan 20, 2020
…/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
ManickaP pushed a commit to ManickaP/runtime that referenced this pull request Jan 20, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants