Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

JIT: recover types from helper calls and more #20447

Merged
merged 5 commits into from
Oct 24, 2018

Conversation

AndyAyersMS
Copy link
Member

The jit needs to recover class handles in order to devirtualize and do other
type-based optimizations. This change allows the jit to find the type for more
trees: in particular, helper calls, intrinsics, and expanded static field accesses.

Also, annotate a few methods to control jit optimization

We don't want to optimize special methods that are used to inform crossgen
about desirable generic instantiations. CommonlyUsedGenericInstantiations
was already annotated but CommonlyUsedWinRTRedirectedInterfaceStubs wasn't.

Because RuntimeType is sealed calls through types are now often
devirtualized. Name lookups on types are frequent, especially on error paths.
The method GetCachedName looks like an attractive inline but simply expands
into a larger sequence of two other calls. So block it from being inlined.

The jit needs to recover class handles in order to devirtualize and
do other type-based optimizations. This change allows the jit to find
the type for more trees: in particular, helper calls, intrinsics, and
expanded static field accesses.
We don't want to optimize special methods that are used to inform crossgen
about desirable generic instantiations. `CommonlyUsedGenericInstantiations`
was already annotated but `CommonlyUsedWinRTRedirectedInterfaceStubs` wasn't.

Also because `RuntimeType` is sealed calls through types are now often
devirtualized. Name lookups on types are frequent, especially on error paths.
The method `GetCachedName` looks like an attractive inline but simply expands
into a larger sequence of two other calls. So block it from being inlined.
@AndyAyersMS
Copy link
Member Author

@briansull PTAL
cc @dotnet/jit-contrib

Per PMI: increases the rate of successful (virtual) call devirtualization from 0.182 to 0.195, and overall devirtualization from 0.156 to 0.166.

About 3000 more virtual call sites devirtualized (out of ~190000).

Jit-diffs (PMI, x64) shows:

Total bytes of diff: -6782 (-0.02% of base)
    diff is an improvement.

Top file regressions by size (bytes):
         251 : System.Net.Sockets.dasm (0.13% of base)
         110 : CommandLine.dasm (0.03% of base)
         109 : System.Security.Permissions.dasm (0.52% of base)
          40 : System.Drawing.Primitives.dasm (0.14% of base)
          16 : System.Private.Uri.dasm (0.02% of base)

Top file improvements by size (bytes):
       -2875 : System.Private.Xml.dasm (-0.08% of base)
       -1879 : System.Private.CoreLib.dasm (-0.05% of base)
        -430 : System.Linq.Expressions.dasm (-0.06% of base)
        -402 : System.Linq.Queryable.dasm (-0.20% of base)
        -328 : System.Private.DataContractSerialization.dasm (-0.04% of base)

54 total files with size differences (41 improved, 13 regressed), 75 unchanged.

Top method regressions by size (bytes):
         405 : System.Private.CoreLib.dasm - ICustomPropertyProviderProxy`2:GetInterface(byref,byref):int:this (5 methods)
         131 : System.Private.Xml.dasm - XsltCompileContext:GetExtentionMethod(ref,ref,ref,byref):ref:this
          91 : System.Private.CoreLib.dasm - CustomAttributeData:Init(ref):this (5 methods)
          62 : System.Private.CoreLib.dasm - ComActivator:GetClassFactoryForType(struct):ref
          55 : CommandLine.dasm - EnumerableExtensions:Memorize(ref):ref (5 methods)

Top method improvements by size (bytes):
        -283 : System.Private.Xml.dasm - XmlAnyConverter:ChangeType(ref,ref,ref):ref:this (2 methods)
        -230 : System.Private.Xml.dasm - XmlUntypedConverter:ChangeType(ref,ref,ref):ref:this (2 methods)
        -180 : System.Private.Xml.dasm - XmlUntypedConverter:ToString(ref,ref):ref:this
        -160 : System.Private.Xml.dasm - XmlNumeric10Converter:ChangeType(ref,ref,ref):ref:this (2 methods)
        -155 : System.Linq.Queryable.dasm - EnumerableQuery`1:System.Linq.IQueryProvider.CreateQuery(ref):ref:this (30 methods)

828 total methods with size differences (554 improved, 274 regressed), 191789 unchanged.

Size regressions are mostly from inlining methods with GUID arguments, where we subsequently fail to remove the struct copies.

Size improvements and devirtualizations largely come from the fact that many type operations return RuntimeType and that type is sealed.

@AndyAyersMS
Copy link
Member Author

AndyAyersMS commented Oct 17, 2018

OSX the usual file io pal test failures

Test Result (5 failures / +5)
file_io.GetFileSize.test1
file_io.GetFileSizeEx.test1
file_io.SetFilePointer.test5
file_io.SetFilePointer.test6
file_io.SetFilePointer.test7

Ubuntu arm64 cross: all tests seemed to pass, but one test wrapper wouldn't load:

00:01:51.119   System.IO.FileLoadException: Could not load file or assembly 'GC.Coverage.XUnitWrapper, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. An operation is not legal in the current state. (Exception from HRESULT: 0x80131509 (COR_E_INVALIDOPERATION))

@BruceForstall
Copy link
Member

I recently saw that "fail to load wrapper" in another run. It was a different wrapper. Can't remember the run (one of the cron jobs).

{
objClass = castHnd;
*isExact = false;
*isNonNull = isCastHelper;
Copy link

@pentp pentp Oct 17, 2018

Choose a reason for hiding this comment

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

CORINFO_HELP_CHKCAST* methods guarantee a non-null result only if the result type is a struct. Is this checked somehow before that already?

Copy link

Choose a reason for hiding this comment

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

It looks like in some cases the helpers are called only after a null check and direct MT compare have been done inline, but in other cases the helper is called directly, e.g. for array casts.

Copy link
Member Author

Choose a reason for hiding this comment

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

No, it's not always checked. Thanks for catching this...

@AndyAyersMS
Copy link
Member Author

A few more xunit load failures in the Ubuntu arm cross job

00:07:15.432   System.IO.FileLoadException: Could not load file or assembly 'baseservices.threading.XUnitWrapper, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. An operation is not legal in the current state. (Exception from HRESULT: 0x80131509 (COR_E_INVALIDOPERATION))
00:07:15.452   System.IO.FileLoadException: Could not load file or assembly 'baseservices.varargs.XUnitWrapper, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. An operation is not legal in the current state. (Exception from HRESULT: 0x80131509 (COR_E_INVALIDOPERATION))

// This method looks like an attractive inline but expands to two calls,
// neither of which can be inlined or optimized further. So block it
// from inlining.
[MethodImpl(MethodImplOptions.NoInlining)]

Choose a reason for hiding this comment

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

It would be nice if in the future the JIT could discover this fact and have a way to persist it so the next time we try to ngen/jit it we consult the database of known methods for this kind of information.
Perhaphs could add an option to ngen/crossgen to create a simplist .IBC data file that contained this information and then check it in along with the real .IBC data.

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 like the idea but do not know how we'd actually do something like this.

I think it might be easier to improve the inlining heuristics in this area (a relatively small method body has multiple sequential calls that are not themselves inline candidates). A strong enough conviction here would automatically propagate noinline into the runtime metadata when prejitting.

Copy link
Member Author

Choose a reason for hiding this comment

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

Opened #20526 to track this idea.

src/jit/gentree.cpp Outdated Show resolved Hide resolved
src/jit/gentree.cpp Outdated Show resolved Hide resolved
src/jit/gentree.cpp Outdated Show resolved Hide resolved
Copy link

@briansull briansull left a comment

Choose a reason for hiding this comment

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

I left some comments

Copy link

@CarolEidt CarolEidt left a comment

Choose a reason for hiding this comment

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

LGTM

src/jit/gentree.cpp Outdated Show resolved Hide resolved
@AndyAyersMS
Copy link
Member Author

AndyAyersMS commented Oct 22, 2018

CI not kicking off PR jobs, probably fallout from the github outage?

[Edit: the jobs showed up finally]

@AndyAyersMS
Copy link
Member Author

CoreFX test list seems to be messed up:

00:14:30.134 Error validating test list: 
00:14:30.134 JSON Validation Error: Required properties are missing from object: methods. Path '[23].exclusions', line 566, position 9.

@AndyAyersMS
Copy link
Member Author

Seems like this last entry needs methods. Will add fix to my PR.

    {
        "name": "System.Diagnostics.Tests",
        "enabled": true,
        "exclusions": {
            "namespaces": null,
            "classes": [
                {
                    "name" : "System.Diagnostics.Tests.DebugTests",
                    "reason" : "refactoring Debug"
                }
            ]
        }
    }

@AndyAyersMS
Copy link
Member Author

After fixing the json issue via #20528

@dotnet-bot test Ubuntu x64 Checked CoreFX Tests
@dotnet-bot test Windows_NT x64 Checked CoreFX Tests
@dotnet-bot test Windows_NT x64 Release CoreFX Tests

and since OSX likes to randomly drop connections:

@dotnet-bot test OSX10.12 x64 Checked Innerloop Build and Test

@AndyAyersMS
Copy link
Member Author

Looks like more CI issues overnight.

@dotnet-bot test this please

@AndyAyersMS
Copy link
Member Author

Ubuntu runs hitting a "Failed to run DSL Script" error...

08:18:36 Schedule job dotnet_coreclr » master » x64_checked_ubuntu_innerloop_prtest
08:19:05 ERROR: Failed to run DSL Script
08:19:05 java.util.concurrent.CancellationException
08:19:05 	at hudson.remoting.AsyncFutureImpl.get(AsyncFutureImpl.java:77)
08:19:05 	at hudson.model.queue.FutureImpl.waitForStart(FutureImpl.java:68)
08:19:05 	at hudson.model.queue.QueueTaskFuture$waitForStart.call(Unknown Source)
08:19:05 	at com.cloudbees.plugins.flow.JobInvocation.waitForStart(JobInvocation.groovy:242)

@AndyAyersMS
Copy link
Member Author

Am going to retrigger again, hoping for better luck this time.

@dotnet-bot test this please

@AndyAyersMS
Copy link
Member Author

Ubuntu arm failure seems like more CI flakiness:

14:04:17 Selected Git installation does not exist. Using Default
14:04:18 FATAL: java.nio.channels.ClosedChannelException
14:04:18 java.nio.channels.ClosedChannelException
14:04:18 Also:   hudson.remoting.Channel$CallSiteStackTrace: Remote call to JNLP4-connect connection from 167.220.2.137/167.220.2.137:7327

@AndyAyersMS
Copy link
Member Author

Will give this one more shot:

@dotnet-bot test Ubuntu arm Cross Checked no_tiered_compilation_innerloop Build and Test

@AndyAyersMS AndyAyersMS merged commit 5af4d64 into dotnet:master Oct 24, 2018
@AndyAyersMS AndyAyersMS deleted the TypesFromHelpers branch October 24, 2018 00:00
A-And pushed a commit to A-And/coreclr that referenced this pull request Nov 20, 2018
The jit needs to recover class handles in order to devirtualize and
do other type-based optimizations. This change allows the jit to find
the type for more trees: in particular, helper calls, intrinsics, and
expanded static field accesses.

Also, annotate a few methods to control jit optimization

We don't want to optimize special methods that are used to inform crossgen
about desirable generic instantiations. `CommonlyUsedGenericInstantiations`
was already annotated but `CommonlyUsedWinRTRedirectedInterfaceStubs` wasn't.

And because `RuntimeType` is sealed calls through types are now often
devirtualized. Name lookups on types are frequent, especially on error paths.
The method `GetCachedName` looks like an attractive inline but simply expands
into a larger sequence of two other calls. So block it from being inlined.
picenka21 pushed a commit to picenka21/runtime that referenced this pull request Feb 18, 2022
The jit needs to recover class handles in order to devirtualize and
do other type-based optimizations. This change allows the jit to find
the type for more trees: in particular, helper calls, intrinsics, and
expanded static field accesses.

Also, annotate a few methods to control jit optimization

We don't want to optimize special methods that are used to inform crossgen
about desirable generic instantiations. `CommonlyUsedGenericInstantiations`
was already annotated but `CommonlyUsedWinRTRedirectedInterfaceStubs` wasn't.

And because `RuntimeType` is sealed calls through types are now often
devirtualized. Name lookups on types are frequent, especially on error paths.
The method `GetCachedName` looks like an attractive inline but simply expands
into a larger sequence of two other calls. So block it from being inlined.


Commit migrated from dotnet/coreclr@5af4d64
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
5 participants