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

JIT: import static readonly fields holding frozen objects as const handles #76112

Merged
merged 70 commits into from
Oct 10, 2022

Conversation

EgorBo
Copy link
Member

@EgorBo EgorBo commented Sep 24, 2022

JIT is able to fold static readonly fields to constants for primitive types. This PR extends that to handle such fields pointing to frozen objects, example:

// static readonly frozen string
static string MyStr { get; } = "Hello";

// static readonly frozen Type
static readonly Type s_type = typeof(int);

// static readonly null (env var doesn't exist)
static readonly string MyConfig = Environment.GetEnvironmentVariable("NonExistingEnvVar");


static void Foo()
{
    WriteLine(MyStr);
    WriteLine(s_type);
    WriteLine(MyConfig);
}

Tier1 codegen diff for Foo:

; Assembly listing for method Foo()
; Tier-1 compilation
G_M14074_IG01:              
       4883EC28             sub      rsp, 40
G_M14074_IG02:              
-      48B9C01EC0CB9E020000 mov      rcx, 0x29ECBC01EC0      ; const ptr
-      488B09               mov      rcx, gword ptr [rcx]
+      48B9B017954DDA010000 mov      rcx, 0x1DA4D9517B0      ; 'Hello'
       FF15B9073800         call     [System.Console:WriteLine(System.String)]
-      48B9C81EC0CB9E020000 mov      rcx, 0x29ECBC01EC8      ; const ptr
-      488B09               mov      rcx, gword ptr [rcx]
+      48B9D017954DDA010000 mov      rcx, 0x1DA4D9517D0      ; 'System.Int32'
       FF158E073800         call     [System.Console:WriteLine(System.Object)]
-      48B9D01E80DDBD010000 mov      rcx, 0x1BDDD801ED0      ; data for ConsoleApp1.Program:MyConfig
-      488B09               mov      rcx, gword ptr [rcx]
+      33C9                 xor      rcx, rcx
       FF155C871A00         call     [ConsoleApp1.Program:WriteLine(System.Object)]
       90                   nop      
G_M14074_IG03:              
       4883C428             add      rsp, 40
       C3                   ret      

UPD: Also, this PR helps NativeAOT:

see #76112 (comment) thread

Motivation

  1. static readonly Type .* = typeof pattern found 211 matches in dotnet/runtime (tests are not included)
    Example: https://github.com/dotnet/runtime/blob/main/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.Cache.cs#L15-L18
  2. static readonly string .* = "\w+"; found 119 results in dotnet/runtime (tests are not included)
  3. It's needed for future ideas, e.g. Array.Empty<T> as was suggested by @jkotas

Jit-diff (-f --pmi --cctors)

Total bytes of base: 63732878
Total bytes of diff: 63700726
Total bytes of delta: -32152 (-0.05 % of base)
Total relative delta: -62.29
    diff is an improvement.
    relative diff is an improvement.


Total byte diff includes -54 bytes from reconciling methods
        Base had    3 unique methods,       54 unique bytes
        Diff had    0 unique methods,        0 unique bytes

Top file regressions (bytes):
        3160 : Newtonsoft.Json.dasm (0.33% of base)
         715 : Microsoft.CSharp.dasm (0.17% of base)
         456 : Microsoft.CodeAnalysis.CSharp.dasm (0.01% of base)
         419 : System.Private.Uri.dasm (0.48% of base)
         239 : System.IO.Packaging.dasm (0.26% of base)
         205 : xunit.execution.dotnet.dasm (0.07% of base)
         155 : Microsoft.Diagnostics.Tools.RuntimeClient.dasm (1.40% of base)
          68 : System.Linq.Expressions.dasm (0.01% of base)
          54 : Microsoft.CodeAnalysis.dasm (0.00% of base)
          40 : System.Threading.RateLimiting.dasm (0.02% of base)
          37 : Microsoft.Extensions.Configuration.Abstractions.dasm (0.86% of base)
          32 : tieringtest.dasm (0.45% of base)
          27 : System.Drawing.Common.dasm (0.00% of base)
          16 : System.ComponentModel.Composition.dasm (0.00% of base)
          16 : runincontext.dasm (0.11% of base)
           8 : System.DirectoryServices.Protocols.dasm (0.01% of base)
           8 : System.Net.Http.dasm (0.00% of base)
           8 : Microsoft.Extensions.Http.dasm (0.02% of base)
           8 : System.Runtime.Caching.dasm (0.01% of base)

Top file improvements (bytes):
      -35586 : Microsoft.Diagnostics.Tracing.TraceEvent.dasm (-0.94% of base)
        -576 : Microsoft.Diagnostics.FastSerialization.dasm (-0.24% of base)
        -374 : System.Private.Xml.dasm (-0.01% of base)
        -286 : System.Private.CoreLib.dasm (-0.00% of base)
        -183 : System.Private.DataContractSerialization.dasm (-0.02% of base)
        -176 : xunit.runner.utility.netcoreapp10.dasm (-0.08% of base)
         -81 : System.Data.OleDb.dasm (-0.03% of base)
         -80 : System.Runtime.Serialization.Formatters.dasm (-0.08% of base)
         -76 : System.Data.Odbc.dasm (-0.03% of base)
         -74 : Microsoft.Extensions.Options.dasm (-0.26% of base)
         -72 : Microsoft.CodeAnalysis.VisualBasic.dasm (-0.00% of base)
         -51 : System.ComponentModel.Annotations.dasm (-0.11% of base)
         -48 : Microsoft.Extensions.Hosting.Abstractions.dasm (-0.50% of base)
         -45 : System.DirectoryServices.AccountManagement.dasm (-0.01% of base)
         -32 : Newtonsoft.Json.Bson.dasm (-0.03% of base)
         -24 : FSharp.Core.dasm (-0.00% of base)
         -17 : System.Text.Json.dasm (-0.00% of base)
          -9 : System.Private.Xml.Linq.dasm (-0.00% of base)
          -9 : System.Runtime.Serialization.Schema.dasm (-0.02% of base)
          -6 : System.Configuration.ConfigurationManager.dasm (-0.00% of base)
          -6 : System.Net.Primitives.dasm (-0.01% of base)
          -6 : System.Data.Common.dasm (-0.00% of base)
          -3 : xunit.console.dasm (-0.00% of base)
          -3 : Microsoft.Extensions.DependencyModel.dasm (-0.00% of base)

43 total files with Code Size differences (24 improved, 19 regressed), 231 unchanged.

Top method regressions (bytes):
         425 (123.19% of base) : System.Private.Uri.dasm - System.UriBuilder:.ctor(System.String):this
         342 (170.15% of base) : Microsoft.CSharp.dasm - System.Runtime.InteropServices.ComEventsMethod:RemoveDelegates(System.Func`2[System.Delegate,bool]):this
         242 (27.66% of base) : System.IO.Packaging.dasm - System.IO.Packaging.PackUriHelper:Create(System.Uri,System.Uri,System.String):System.Uri
         228 (140.74% of base) : Microsoft.CodeAnalysis.CSharp.dasm - Microsoft.CodeAnalysis.CSharp.ErrorFacts:GetDescription(int):Microsoft.CodeAnalysis.LocalizableResourceString
         228 (140.74% of base) : Microsoft.CodeAnalysis.CSharp.dasm - Microsoft.CodeAnalysis.CSharp.ErrorFacts:GetTitle(int):Microsoft.CodeAnalysis.LocalizableResourceString
         199 (13.71% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.JsonTextReader:ParseValue():bool:this
         198 (18.80% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.JsonTextWriter:WriteValueAsync(System.Nullable`1[ushort],System.Threading.CancellationToken):System.Threading.Tasks.Task:this (2 methods)
         195 (330.51% of base) : Microsoft.CSharp.dasm - System.Runtime.InteropServices.ComEventsMethod:Invoke(System.Object[]):System.Object:this
         195 (39.47% of base) : xunit.execution.dotnet.dasm - Xunit.ExecutionHelper:get_PlatformSuffix():System.String
         183 (22.10% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.JsonTextWriter:DoWriteValueAsync(System.Nullable`1[ushort],System.Threading.CancellationToken):System.Threading.Tasks.Task:this (2 methods)
         134 (22.22% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.JsonTextWriter:WriteValueAsync(System.Nullable`1[System.Decimal],System.Threading.CancellationToken):System.Threading.Tasks.Task:this
         123 (20.60% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.JsonTextWriter:WriteValueAsync(System.Nullable`1[ulong],System.Threading.CancellationToken):System.Threading.Tasks.Task:this
         120 (800.00% of base) : Microsoft.CSharp.dasm - System.Runtime.InteropServices.ComEventsMethod:get_Empty():bool:this
         114 (23.27% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.JsonTextWriter:DoWriteValueAsync(System.Nullable`1[ulong],System.Threading.CancellationToken):System.Threading.Tasks.Task:this
         114 (30.89% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.JsonTextWriter:WriteValueAsync(System.Nullable`1[System.DateTimeOffset],System.Threading.CancellationToken):System.Threading.Tasks.Task:this
         114 (31.40% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.JsonTextWriter:WriteValueAsync(System.Nullable`1[System.Guid],System.Threading.CancellationToken):System.Threading.Tasks.Task:this
         107 (24.65% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.JsonTextWriter:DoWriteValueAsync(System.Nullable`1[System.Decimal],System.Threading.CancellationToken):System.Threading.Tasks.Task:this
          96 (20.00% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.JsonTextWriter:WriteValueAsync(System.Nullable`1[bool],System.Threading.CancellationToken):System.Threading.Tasks.Task:this
          96 (16.78% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.JsonTextWriter:WriteValueAsync(System.Nullable`1[byte],System.Threading.CancellationToken):System.Threading.Tasks.Task:this
          96 (17.05% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.JsonTextWriter:WriteValueAsync(System.Nullable`1[int],System.Threading.CancellationToken):System.Threading.Tasks.Task:this
          96 (16.81% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.JsonTextWriter:WriteValueAsync(System.Nullable`1[short],System.Threading.CancellationToken):System.Threading.Tasks.Task:this
          96 (16.90% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.JsonTextWriter:WriteValueAsync(System.Nullable`1[ubyte],System.Threading.CancellationToken):System.Threading.Tasks.Task:this
          96 (17.11% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.JsonTextWriter:WriteValueAsync(System.Nullable`1[uint],System.Threading.CancellationToken):System.Threading.Tasks.Task:this
          93 (16.85% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.JsonTextWriter:WriteValueAsync(System.Nullable`1[long],System.Threading.CancellationToken):System.Threading.Tasks.Task:this
          90 (24.52% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.JsonTextWriter:DoWriteValueAsync(System.Nullable`1[bool],System.Threading.CancellationToken):System.Threading.Tasks.Task:this
          88 (20.66% of base) : Microsoft.Diagnostics.Tools.RuntimeClient.dasm - Microsoft.Diagnostics.Tools.RuntimeClient.EventPipeClient:ListAvailablePorts():System.Collections.Generic.IEnumerable`1[int]
          87 (19.04% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.JsonTextWriter:DoWriteValueAsync(System.Nullable`1[byte],System.Threading.CancellationToken):System.Threading.Tasks.Task:this
          87 (19.08% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.JsonTextWriter:DoWriteValueAsync(System.Nullable`1[int],System.Threading.CancellationToken):System.Threading.Tasks.Task:this
          87 (19.04% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.JsonTextWriter:DoWriteValueAsync(System.Nullable`1[short],System.Threading.CancellationToken):System.Threading.Tasks.Task:this
          87 (19.04% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.JsonTextWriter:DoWriteValueAsync(System.Nullable`1[ubyte],System.Threading.CancellationToken):System.Threading.Tasks.Task:this

Top method improvements (bytes):
      -16117 (-16.63% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - Microsoft.Diagnostics.Tracing.Parsers.ApplicationServerTraceEventParser:EnumerateTemplates(System.Func`3[System.String,System.String,int],System.Action`1[Microsoft.Diagnostics.Tracing.TraceEvent]):this
       -1660 (-14.19% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - Microsoft.Diagnostics.Tracing.Parsers.AspNet.AspNetTraceEventParser:EnumerateTemplates(System.Func`3[System.String,System.String,int],System.Action`1[Microsoft.Diagnostics.Tracing.TraceEvent]):this
       -1360 (-4.23% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - Microsoft.Diagnostics.Tracing.Parsers.KernelTraceEventParser:EnumerateTemplates(System.Func`3[System.String,System.String,int],System.Action`1[Microsoft.Diagnostics.Tracing.TraceEvent]):this
        -611 (-2.88% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - Microsoft.Diagnostics.Tracing.Parsers.ClrTraceEventParser:EnumerateTemplates(System.Func`3[System.String,System.String,int],System.Action`1[Microsoft.Diagnostics.Tracing.TraceEvent]):this
        -292 (-21.73% of base) : System.Private.CoreLib.dasm - System.Reflection.RuntimeParameterInfo:GetDefaultValueInternal(bool):System.Object:this
        -170 (-4.44% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - Microsoft.Diagnostics.Tracing.Parsers.Clr.ClrRundownTraceEventParser:EnumerateTemplates(System.Func`3[System.String,System.String,int],System.Action`1[Microsoft.Diagnostics.Tracing.TraceEvent]):this
        -149 (-0.72% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - Microsoft.Diagnostics.Tracing.Parsers.ClrPrivateTraceEventParser:EnumerateTemplates(System.Func`3[System.String,System.String,int],System.Action`1[Microsoft.Diagnostics.Tracing.TraceEvent]):this
        -129 (-16.80% of base) : xunit.runner.utility.netcoreapp10.dasm - Xunit.DiaSession:GetNavigationData(System.String,System.String,System.String):Xunit.DiaNavigationData:this
        -127 (-8.61% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - Microsoft.Diagnostics.Tracing.Parsers.JSDumpHeapTraceEventParser:EnumerateTemplates(System.Func`3[System.String,System.String,int],System.Action`1[Microsoft.Diagnostics.Tracing.TraceEvent]):this
         -80 (-3.62% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - Microsoft.Diagnostics.Tracing.Parsers.SymbolTraceEventParser:EnumerateTemplates(System.Func`3[System.String,System.String,int],System.Action`1[Microsoft.Diagnostics.Tracing.TraceEvent]):this
         -63 (-2.88% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - Microsoft.Diagnostics.Tracing.Parsers.Kernel.HeapTraceProviderTraceEventParser:EnumerateTemplates(System.Func`3[System.String,System.String,int],System.Action`1[Microsoft.Diagnostics.Tracing.TraceEvent]):this
         -63 (-2.91% of base) : System.Private.DataContractSerialization.dasm - System.Runtime.Serialization.XmlReaderDelegator:ReadExtensionData(System.Type):System.Runtime.Serialization.IDataNode:this
         -61 (-16.62% of base) : Microsoft.CSharp.dasm - System.Runtime.InteropServices.ComEventsMethod:RemoveDelegate(System.Delegate,bool):this
         -48 (-2.81% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - Microsoft.Diagnostics.Tracing.Parsers.Kernel.ThreadPoolTraceEventParser:EnumerateTemplates(System.Func`3[System.String,System.String,int],System.Action`1[Microsoft.Diagnostics.Tracing.TraceEvent]):this
         -47 (-37.01% of base) : xunit.runner.utility.netcoreapp10.dasm - Xunit.DiaSession:.ctor(System.String):this
         -45 (-11.31% of base) : System.Runtime.Serialization.Formatters.dasm - System.Runtime.Serialization.Formatters.Binary.Converter:InitArrayTypeA()
         -45 (-11.31% of base) : System.Runtime.Serialization.Formatters.dasm - System.Runtime.Serialization.Formatters.Binary.Converter:InitTypeA()
         -42 (-4.40% of base) : Microsoft.CodeAnalysis.VisualBasic.dasm - Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxNode:GetSerializationData():System.Collections.Generic.IEnumerable`1[System.Object]
         -37 (-100.00% of base) : Microsoft.CSharp.dasm - System.Runtime.InteropServices.ComEventsMethod:PublishNewWrappers(DelegateWrapper[],DelegateWrapper[]):bool:this (1 base, 0 diff methods)
         -33 (-2.37% of base) : System.Private.DataContractSerialization.dasm - System.Runtime.Serialization.Json.XmlObjectSerializerReadContextComplexJson:ReadNumericalPrimitiveExtensionDataValue(System.Runtime.Serialization.XmlReaderDelegator):System.Runtime.Serialization.IDataNode
         -30 (-2.63% of base) : Microsoft.CodeAnalysis.dasm - FileStream:.cctor()
         -27 (-0.84% of base) : System.Data.OleDb.dasm - System.Data.OleDb.OleDbMetaDataFactory:.ctor(System.IO.Stream,System.String,System.String,System.Data.OleDb.SchemaSupport[]):this
         -23 (-88.46% of base) : System.DirectoryServices.AccountManagement.dasm - System.DirectoryServices.AccountManagement.GlobalConfig:get_DebugLogFile():System.String
         -23 (-0.93% of base) : System.Private.Xml.dasm - System.Xml.Schema.XmlMiscConverter:ChangeType(System.Object,System.Type,System.Xml.IXmlNamespaceResolver):System.Object:this
         -22 (-88.00% of base) : System.DirectoryServices.AccountManagement.dasm - System.DirectoryServices.AccountManagement.GlobalConfig:get_DebugLevel():int
         -21 (-0.92% of base) : System.Data.Odbc.dasm - System.Data.Odbc.OdbcMetaDataFactory:.ctor(System.IO.Stream,System.String,System.String,System.Data.Odbc.OdbcConnection):this
         -21 (-2.32% of base) : System.Data.Odbc.dasm - System.Data.Odbc.OdbcMetaDataFactory:PrepareCollection(System.String,System.String[],System.Data.Common.DbConnection):System.Data.DataTable:this
         -19 (-6.51% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - Microsoft.Diagnostics.Tracing.Parsers.KernelTraceEventParser:add_DiskIOFlushBuffers(System.Action`1[Microsoft.Diagnostics.Tracing.Parsers.Kernel.DiskIOFlushBuffersTraceData]):this
         -19 (-6.51% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - Microsoft.Diagnostics.Tracing.Parsers.KernelTraceEventParser:add_DiskIOFlushInit(System.Action`1[Microsoft.Diagnostics.Tracing.Parsers.Kernel.DiskIOInitTraceData]):this
         -19 (-6.51% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - Microsoft.Diagnostics.Tracing.Parsers.KernelTraceEventParser:add_DiskIORead(System.Action`1[Microsoft.Diagnostics.Tracing.Parsers.Kernel.DiskIOTraceData]):this

Top method regressions (percentages):
         120 (800.00% of base) : Microsoft.CSharp.dasm - System.Runtime.InteropServices.ComEventsMethod:get_Empty():bool:this
         195 (330.51% of base) : Microsoft.CSharp.dasm - System.Runtime.InteropServices.ComEventsMethod:Invoke(System.Object[]):System.Object:this
         342 (170.15% of base) : Microsoft.CSharp.dasm - System.Runtime.InteropServices.ComEventsMethod:RemoveDelegates(System.Func`2[System.Delegate,bool]):this
         228 (140.74% of base) : Microsoft.CodeAnalysis.CSharp.dasm - Microsoft.CodeAnalysis.CSharp.ErrorFacts:GetDescription(int):Microsoft.CodeAnalysis.LocalizableResourceString
         228 (140.74% of base) : Microsoft.CodeAnalysis.CSharp.dasm - Microsoft.CodeAnalysis.CSharp.ErrorFacts:GetTitle(int):Microsoft.CodeAnalysis.LocalizableResourceString
         425 (123.19% of base) : System.Private.Uri.dasm - System.UriBuilder:.ctor(System.String):this
          53 (73.61% of base) : Microsoft.Extensions.Configuration.Abstractions.dasm - Microsoft.Extensions.Configuration.ConfigurationPath:Combine(System.String[]):System.String
          32 (50.79% of base) : Microsoft.CSharp.dasm - System.Runtime.InteropServices.ComEventsMethod:.ctor(int):this
          79 (46.75% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.JsonTextWriter:DoWriteNullAsync(System.Threading.CancellationToken):System.Threading.Tasks.Task:this
          78 (39.59% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.JsonTextWriter:DoWriteValueAsync(System.Nullable`1[System.DateTime],System.Threading.CancellationToken):System.Threading.Tasks.Task:this
          78 (39.59% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.JsonTextWriter:DoWriteValueAsync(System.Nullable`1[System.TimeSpan],System.Threading.CancellationToken):System.Threading.Tasks.Task:this
         195 (39.47% of base) : xunit.execution.dotnet.dasm - Xunit.ExecutionHelper:get_PlatformSuffix():System.String
          81 (38.21% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.JsonTextWriter:DoWriteValueAsync(System.Nullable`1[System.Guid],System.Threading.CancellationToken):System.Threading.Tasks.Task:this
           8 (38.10% of base) : System.Private.CoreLib.dasm - System.Diagnostics.Stopwatch:GetElapsedTime(long,long):System.TimeSpan
          78 (36.79% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.JsonTextWriter:DoWriteValueAsync(System.Nullable`1[System.DateTimeOffset],System.Threading.CancellationToken):System.Threading.Tasks.Task:this
         114 (31.40% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.JsonTextWriter:WriteValueAsync(System.Nullable`1[System.Guid],System.Threading.CancellationToken):System.Threading.Tasks.Task:this
         114 (30.89% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.JsonTextWriter:WriteValueAsync(System.Nullable`1[System.DateTimeOffset],System.Threading.CancellationToken):System.Threading.Tasks.Task:this
          78 (30.23% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.JsonTextWriter:WriteNullAsync(System.Threading.CancellationToken):System.Threading.Tasks.Task:this
          32 (28.83% of base) : Microsoft.CSharp.dasm - System.Runtime.InteropServices.ComEventsSink:AddMethod(int):System.Runtime.InteropServices.ComEventsMethod:this
         242 (27.66% of base) : System.IO.Packaging.dasm - System.IO.Packaging.PackUriHelper:Create(System.Uri,System.Uri,System.String):System.Uri
          78 (25.41% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.JsonTextWriter:WriteValueAsync(System.Nullable`1[System.DateTime],System.Threading.CancellationToken):System.Threading.Tasks.Task:this
          78 (25.41% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.JsonTextWriter:WriteValueAsync(System.Nullable`1[System.TimeSpan],System.Threading.CancellationToken):System.Threading.Tasks.Task:this
         107 (24.65% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.JsonTextWriter:DoWriteValueAsync(System.Nullable`1[System.Decimal],System.Threading.CancellationToken):System.Threading.Tasks.Task:this
          84 (24.56% of base) : Microsoft.CSharp.dasm - System.Runtime.InteropServices.ComEventsMethod:AddDelegate(System.Delegate,bool):this
          90 (24.52% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.JsonTextWriter:DoWriteValueAsync(System.Nullable`1[bool],System.Threading.CancellationToken):System.Threading.Tasks.Task:this
         114 (23.27% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.JsonTextWriter:DoWriteValueAsync(System.Nullable`1[ulong],System.Threading.CancellationToken):System.Threading.Tasks.Task:this
         134 (22.22% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.JsonTextWriter:WriteValueAsync(System.Nullable`1[System.Decimal],System.Threading.CancellationToken):System.Threading.Tasks.Task:this
         183 (22.10% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.JsonTextWriter:DoWriteValueAsync(System.Nullable`1[ushort],System.Threading.CancellationToken):System.Threading.Tasks.Task:this (2 methods)
          88 (20.66% of base) : Microsoft.Diagnostics.Tools.RuntimeClient.dasm - Microsoft.Diagnostics.Tools.RuntimeClient.EventPipeClient:ListAvailablePorts():System.Collections.Generic.IEnumerable`1[int]
         123 (20.60% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.JsonTextWriter:WriteValueAsync(System.Nullable`1[ulong],System.Threading.CancellationToken):System.Threading.Tasks.Task:this

Top method improvements (percentages):
          -1 (-100.00% of base) : Microsoft.CSharp.dasm - <>c__DisplayClass12_0:.ctor():this (1 base, 0 diff methods)
         -16 (-100.00% of base) : Microsoft.CSharp.dasm - <>c__DisplayClass12_0:<RemoveDelegates>b__0(DelegateWrapper):bool:this (1 base, 0 diff methods)
         -37 (-100.00% of base) : Microsoft.CSharp.dasm - System.Runtime.InteropServices.ComEventsMethod:PublishNewWrappers(DelegateWrapper[],DelegateWrapper[]):bool:this (1 base, 0 diff methods)
         -23 (-88.46% of base) : System.DirectoryServices.AccountManagement.dasm - System.DirectoryServices.AccountManagement.GlobalConfig:get_DebugLogFile():System.String
         -22 (-88.00% of base) : System.DirectoryServices.AccountManagement.dasm - System.DirectoryServices.AccountManagement.GlobalConfig:get_DebugLevel():int
         -47 (-37.01% of base) : xunit.runner.utility.netcoreapp10.dasm - Xunit.DiaSession:.ctor(System.String):this
          -9 (-31.03% of base) : System.Private.CoreLib.dasm - System.RuntimeType:GetCustomAttributes(bool):System.Object[]:this
        -292 (-21.73% of base) : System.Private.CoreLib.dasm - System.Reflection.RuntimeParameterInfo:GetDefaultValueInternal(bool):System.Object:this
          -3 (-21.43% of base) : Microsoft.CodeAnalysis.dasm - HexPropertyMetadataNamedArgument:get_ArgumentName():System.String:this
          -3 (-21.43% of base) : xunit.console.dasm - Internal.Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment:get_OperatingSystem():System.String
          -3 (-21.43% of base) : xunit.console.dasm - Internal.Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment:get_RuntimeArchitecture():System.String
          -3 (-21.43% of base) : Microsoft.CodeAnalysis.dasm - Microsoft.CodeAnalysis.CodeGen.PermissionSetFileReadException:get_PropertyName():System.String:this
          -3 (-21.43% of base) : Microsoft.Diagnostics.Tools.RuntimeClient.dasm - Microsoft.Diagnostics.Tools.RuntimeClient.DiagnosticsIpc.IpcClient:get_DiagnosticsPortPattern():System.String
          -3 (-21.43% of base) : Microsoft.Diagnostics.Tools.RuntimeClient.dasm - Microsoft.Diagnostics.Tools.RuntimeClient.DiagnosticsIpc.IpcClient:get_IpcRootPath():System.String
          -3 (-21.43% of base) : Microsoft.Diagnostics.Tools.RuntimeClient.dasm - Microsoft.Diagnostics.Tools.RuntimeClient.EventPipeClient:get_DiagnosticsPortPattern():System.String
          -3 (-21.43% of base) : Microsoft.Diagnostics.Tools.RuntimeClient.dasm - Microsoft.Diagnostics.Tools.RuntimeClient.EventPipeClient:get_IpcRootPath():System.String
          -3 (-21.43% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - Microsoft.Diagnostics.Tracing.AutomatedAnalysis.StackTypes:get_CPU():System.String
          -3 (-21.43% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - Microsoft.Diagnostics.Tracing.Parsers.ApplicationServerTraceEventParser:GetProviderName():System.String:this
          -3 (-21.43% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - Microsoft.Diagnostics.Tracing.Parsers.AspNet.AspNetTraceEventParser:GetProviderName():System.String:this
          -3 (-21.43% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - Microsoft.Diagnostics.Tracing.Parsers.Clr.ClrRundownTraceEventParser:GetProviderName():System.String:this
          -3 (-21.43% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - Microsoft.Diagnostics.Tracing.Parsers.Clr.ClrStressTraceEventParser:GetProviderName():System.String:this
          -3 (-21.43% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - Microsoft.Diagnostics.Tracing.Parsers.ClrPrivateTraceEventParser:GetProviderName():System.String:this
          -3 (-21.43% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - Microsoft.Diagnostics.Tracing.Parsers.ClrTraceEventParser:GetProviderName():System.String:this
          -3 (-21.43% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - Microsoft.Diagnostics.Tracing.Parsers.FrameworkEventSourceTraceEventParser:GetProviderName():System.String:this
          -3 (-21.43% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - Microsoft.Diagnostics.Tracing.Parsers.JScriptTraceEventParser:GetProviderName():System.String:this
          -3 (-21.43% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - Microsoft.Diagnostics.Tracing.Parsers.JSDumpHeapTraceEventParser:GetProviderName():System.String:this
          -3 (-21.43% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - Microsoft.Diagnostics.Tracing.Parsers.Kernel.CritSecTraceProviderTraceEventParser:GetProviderName():System.String:this
          -3 (-21.43% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - Microsoft.Diagnostics.Tracing.Parsers.Kernel.HeapTraceProviderTraceEventParser:GetProviderName():System.String:this
          -3 (-21.43% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - Microsoft.Diagnostics.Tracing.Parsers.Kernel.ThreadPoolTraceEventParser:GetProviderName():System.String:this
          -3 (-21.43% of base) : Microsoft.Diagnostics.Tracing.TraceEvent.dasm - Microsoft.Diagnostics.Tracing.Parsers.KernelTraceEventParser:GetProviderName():System.String:this

2259 total methods with Code Size differences (2153 improved, 106 regressed), 389393 unchanged.

Regressions seem to be CSE related e.g. https://www.diffchecker.com/E3PieJCB + changed inlining decisions due to changes in gtGetClassHandle

PR is verbose due to JIT-EE update, the actual changes are fairly small.

@dotnet-issue-labeler dotnet-issue-labeler bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Sep 24, 2022
@ghost ghost assigned EgorBo Sep 24, 2022
@ghost
Copy link

ghost commented Sep 24, 2022

Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch
See info in area-owners.md if you want to be subscribed.

Issue Details

JIT is able to fold static readonly fields to constants for primitive types. This PR extends that on frozen objects, example:

static string MyStr { get; } = "Hello";
static readonly Type s_type = typeof(int);

static void Foo()
{
    Console.WriteLine(MyStr);
    Console.WriteLine(s_type);
}

Tier1 codegen diff for Foo:

; Assembly listing for method Foo()
; Tier-1 compilation
G_M14074_IG01:              
       4883EC28             sub      rsp, 40
G_M14074_IG02:              
-      48B9C01EC0CB9E020000 mov      rcx, 0x29ECBC01EC0      ; const ptr
-      488B09               mov      rcx, gword ptr [rcx]
+      48B9B017954DDA010000 mov      rcx, 0x1DA4D9517B0      ; 'Hello'
       FF15B9073800         call     [System.Console:WriteLine(System.String)]
-      48B9C81EC0CB9E020000 mov      rcx, 0x29ECBC01EC8      ; const ptr
-      488B09               mov      rcx, gword ptr [rcx]
+      48B9D017954DDA010000 mov      rcx, 0x1DA4D9517D0      ; 'System.Int32'
       FF158E073800         call     [System.Console:WriteLine(System.Object)]
       90                   nop      
G_M14074_IG03:              
       4883C428             add      rsp, 40
       C3                   ret      

Motivation

  1. static readonly Type .* = typeof pattern found 211 matches in dotnet/runtime (tests are not included)
    Example: https://github.com/dotnet/runtime/blob/main/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.Cache.cs#L15-L18
  2. It's needed for future ideas, e.g. Array.Empty<T> as was suggested by @jkotas

Jit-diff

PR is verbose due to JIT-EE update, the actual changes are fairly small.

Author: EgorBo
Assignees: EgorBo
Labels:

area-CodeGen-coreclr

Milestone: -

@jkotas
Copy link
Member

jkotas commented Sep 24, 2022

BTW: Storing frozen objects does not require write barrier. For example:

string[] a = new string[1];
a[0] = "Hello"; // no GC write barrier needed if "Hello" is frozen object

Does this optimization happen already? If not, we just need to skip the write barrier for frozen icon handles, similar how we skip them for nulls.

@EgorBo
Copy link
Member Author

EgorBo commented Sep 24, 2022

BTW: Storing frozen objects does not require write barrier. For example:

string[] a = new string[1];
a[0] = "Hello"; // no GC write barrier needed if "Hello" is frozen object

Does this optimization happen already? If not, we just need to skip the write barrier for frozen icon handles, similar how we skip them for nulls.

Good point! Do you mind if I do it separately, I also wanted to e.g. check if it's worth optimizing pinning against frozen objects, etc. Separate commits also help to triage perf improvements/regressions

@jkotas
Copy link
Member

jkotas commented Sep 24, 2022

Good point! Do you mind if I do it separately

Yes, it is fine to do it separately.

Copy link
Member

@jkotas jkotas left a comment

Choose a reason for hiding this comment

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

LGTM otherwise. Thank you!

Co-authored-by: Jan Kotas <jkotas@microsoft.com>
…pl.RyuJit.cs

Co-authored-by: Jan Kotas <jkotas@microsoft.com>
@EgorBo
Copy link
Member Author

EgorBo commented Oct 8, 2022

Looks like IsThreadStatic check should be preserved on VM side, jit doesn't know that info in advance it seems, there is CORINFO_FIELD_STATIC_TLS but that, presumably, for native TLS

@jkotas
Copy link
Member

jkotas commented Oct 8, 2022

Looks like IsThreadStatic check should be preserved on VM side, jit doesn't know that info in advance it seems, there is CORINFO_FIELD_STATIC_TLS but that, presumably, for native TLS

Sounds reasonable.

@EgorBo EgorBo closed this Oct 9, 2022
@EgorBo EgorBo reopened this Oct 9, 2022
@EgorBo
Copy link
Member Author

EgorBo commented Oct 10, 2022

CI is green except SPMI jobs (JIT-EE change), going to merge

@EgorBo EgorBo merged commit 12caa8f into dotnet:main Oct 10, 2022
@ghost ghost locked as resolved and limited conversation to collaborators Nov 10, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants