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

Adding new ReadyToRun helper for static cctor #76898

Merged
merged 40 commits into from
Dec 13, 2022

Conversation

BrianBohe
Copy link
Member

As described in issue #69635, when accessing a static field, the static constructor of the class is called. Then the helper CORINFO_HELP_READYTORUN_STATIC_BASE was printed twice during the same block, the first for the constructor and the second for the getter, as if they were two calls for the same method.

This PR introduced a new helper name to differentiate both scenarios.

@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 Oct 11, 2022
@ghost ghost assigned BrianBohe Oct 11, 2022
@ghost
Copy link

ghost commented Oct 11, 2022

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

Issue Details

As described in issue #69635, when accessing a static field, the static constructor of the class is called. Then the helper CORINFO_HELP_READYTORUN_STATIC_BASE was printed twice during the same block, the first for the constructor and the second for the getter, as if they were two calls for the same method.

This PR introduced a new helper name to differentiate both scenarios.

Author: BrianBohe
Assignees: -
Labels:

area-CodeGen-coreclr

Milestone: -

@EgorBo
Copy link
Member

EgorBo commented Oct 11, 2022

This PR introduced a new helper name to differentiate both scenarios.

Is it possible to unite them to have just a single helper call in that case to access a field and run cctor if needed?
Moreover, I assume CORINFO_HELP_READYTORUN_STATIC_BASE already does that? So you basically just need to eliminate one of them, am I right?

@jakobbotsch
Copy link
Member

Is it possible to unite them to have just a single helper call in that case to access a field and run cctor if needed?

@trylek had some ideas in #69635 (comment) but it didn't look too straightforward.

@jakobbotsch
Copy link
Member

jakobbotsch commented Oct 11, 2022

I don't mind taking a change like this the next time we are taking a JIT-EE GUID change anyway, but I don't think we need to churn things to take this alone. In fact I think taking it would be nice, it is very confusing right now that the CCTOR_TRIGGER R2R helper is called the same thing to the JIT as the other ones, yet has a different calling convention (doesn't return a value, if I read prestub.cpp right).

@EgorBo
Copy link
Member

EgorBo commented Oct 11, 2022

@BrianBohe while you're there could you also remove CORINFO_HELP_STRCNS_CURRENT_MODULE ? it seems unused

@jkotas
Copy link
Member

jkotas commented Oct 11, 2022

@trylek had some ideas in #69635 (comment) but it didn't look too straightforward.

Why is that hard? It looks like a pretty straightforward JIT optimization to me.

@jakobbotsch
Copy link
Member

jakobbotsch commented Oct 11, 2022

Why is that hard? It looks like a pretty straightforward JIT optimization to me.

Do you mean eliding CCTOR_TRIGGER for an empty static constructor, or folding CCTOR_TRIGGER followed by STATIC_BASE call? Maybe I misunderstood, but it sounded like the CCTOR_TRIGGER was necessary.

If the STATIC_BASE call subsumes the CCTOR_TRIGGER, then I agree some pattern matching can do it in some (many) simple cases like in the issue. Today the JIT is also able to remove multiple STATIC_BASE calls across multiple basic blocks via CSE, which would be more tricky to extend to CCTOR_TRIGGER.

I guess it would also need a new JIT-EE function since these helper functions do not carry any information except in the entry points about the class they are accessing.

One thing I'm confused about from looking at CCTOR_TRIGGER in prestub.cpp is that it seems to be always turned into trivial ret instruction. Do these helpers do anything at runtime or only during fixup? If the latter, does CCTOR_TRIGGER need to exist in the jitted code at all? Can crossgen2 just emit the fixup without JIT needing any runtime code?

@jkotas
Copy link
Member

jkotas commented Oct 11, 2022

folding CCTOR_TRIGGER followed by STATIC_BASE call? Maybe I misunderstood, but it sounded like the CCTOR_TRIGGER was necessary.

I meant folding CCTOR_TRIGGER followed by STATIC_BASE call. CCTOR_TRIGGER is unnecessary in this case. The STATIC_BASE calls always trigger the cctor if necessary.

I guess it would also need a new JIT-EE function since these helper functions do not carry any information except in the entry points about the class they are accessing.

You should be able to track the information in the call node, similar to how class or method handle is tracked for calls. We do not have JIT/EE interface method to give you method handle from resolved entrypoint for these cases either.

@jkotas
Copy link
Member

jkotas commented Oct 11, 2022

One thing I'm confused about from looking at CCTOR_TRIGGER in prestub.cpp is that it seems to be always turned into trivial ret instruction.

Yes, the cctor trigger is optimized to just return once the cctor runs. An alternative option would be to emit conditional check in the JITed code for cctor triggers. It would have better perf, but also produce larger code size.

@jakobbotsch
Copy link
Member

jakobbotsch commented Oct 11, 2022

You should be able to track the information in the call node, similar to how class or method handle is tracked for calls. We do not have JIT/EE interface method to give you method handle from resolved entrypoint for these cases either.

Hopefully we could find a place to put it. Increasing the size of GenTreeCall is quite expensive (increases the size of all large nodes in the JIT). (Looks like we have GenTreeCall::compileTimeHelperArgumentHandle already for the purpose).

Yes, the cctor trigger is optimized to just return once the cctor runs. An alternative option would be to emit conditional check in the JITed code for cctor triggers. It would have better perf, but also produce larger code size.

Ah ok, I understand now. For some reason I was under the impression that one stage of these fixups was running eagerly (during external fixup), but it is not the case.

@EgorBo
Copy link
Member

EgorBo commented Oct 11, 2022

An alternative option would be to emit conditional check in the JITed code for cctor triggers. It would have better perf, but also produce larger code size.

Related prototype: #47901 (for jit) - we decided to give up on it since larger code size wasn't justified for JIT where we usually get rid of the checks in tier1 anyway.

@@ -917,7 +917,7 @@ GenTreeCall* Compiler::fgGetSharedCCtor(CORINFO_CLASS_HANDLE cls)
memset(&resolvedToken, 0, sizeof(resolvedToken));
resolvedToken.hClass = cls;

return impReadyToRunHelperToTree(&resolvedToken, CORINFO_HELP_READYTORUN_STATIC_BASE, TYP_BYREF);
return impReadyToRunHelperToTree(&resolvedToken, CORINFO_HELP_READYTORUN_CCTOR_TRIGGER, TYP_BYREF);
Copy link
Member

Choose a reason for hiding this comment

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

While you are here, can you change this to return TYP_VOID? If I'm not mistaken the effect of the TYP_BYREF here is that we are reporting random garbage value to the GC as a byref.

@jkotas
Copy link
Member

jkotas commented Oct 12, 2022

The builds and tests are failing with:

---> System.NotImplementedException: ReadyToRun: CORINFO_HELP_READYTORUN_CCTOR_TRIGGER
   at Internal.JitInterface.CorInfoImpl.getReadyToRunHelper(CORINFO_RESOLVED_TOKEN&, CORINFO_LOOKUP_KIND&, CorInfoHelpFunc, CORINFO_CONST_LOOKUP&) in /_/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs:line 349

I do not think it is worth it to plumb this new helper through the whole system. It is nearly identical to the static base helper and frequently used together.

You can keep the new helper ID internal to the JIT if you really want to have it distinct from the static base helper. It may be better to focus on optimizing out the redundant static base helper calls instead.

@jkotas
Copy link
Member

jkotas commented Oct 12, 2022

Scratch my previous comment. The cctor helper partially exists already, it is just not used in all the right places. It makes sense to fix that.

@@ -116,6 +116,7 @@ ValueNumFuncDef(GetsharedNongcstaticBase, 2, false, true, true)
ValueNumFuncDef(GetsharedGcstaticBaseNoctor, 1, false, true, true)
ValueNumFuncDef(GetsharedNongcstaticBaseNoctor, 1, false, true, true)
ValueNumFuncDef(ReadyToRunStaticBase, 1, false, true, true)
ValueNumFuncDef(ReadyToRunCctorTrigger, 1, false, true, true)
Copy link
Member

Choose a reason for hiding this comment

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

I don't think you should end up needing this. Now that this is TYP_VOID it is going to get void value number.

Copy link
Member

Choose a reason for hiding this comment

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

I still think this (and the ReadyToRunStaticBase above it) can be removed.

@jakobbotsch
Copy link
Member

jakobbotsch commented Oct 12, 2022

Looking at NAOT it does not implement/use the CctorTrigger variant of the R2R helper.

That probably means we need to fix JIT to keep creating the STATIC_BASE helper for NAOT, otherwise we may regress NAOT by no longer being able to CSE the STATIC_BASE inserted cctor trigger with STATIC_BASE helpers for fields (this may also be what @am11 was seeing in #69635 (comment)).

Another question is if we should just move crossgen2 to be on the same plan as NAOT, i.e. get rid of CctorTrigger and just insert the GetNonGCStaticBase helper. It will allow JIT to CSE these automatically in more cases. Thoughts @jkotas?

The optimization might still be worth it even with this CSE'ing. It will allow us to drop the redundant GetNonGCStaticBase when it is succeeded by GetGCStaticBase, GetThreadStaticBase or GetThreadNonGcStaticBase.

@EgorBo
Copy link
Member

EgorBo commented Oct 12, 2022

Unrelated: are static cctor calls even exist for the snippet in the issue

static class Foo
{
    public static int Bar { get; } = 1;
    static Foo() { } // even the empty cctor will force it to emit the helper
                     // call twice in each static property's getter.
}

? I expect get_Bar to be just return 1 🙂 after #76112

@jakobbotsch
Copy link
Member

Here's a difference between NAOT and crossgen2 due to this CctorTrigger difference:

image

Notice we do not CSE the cctor trigger in the R2R code.

@jakobbotsch
Copy link
Member

jakobbotsch commented Oct 12, 2022

Since we only insert the cctor trigger in morph it would be pretty easy to pick the "best" STATIC_BASE variant as the CCTOR_TRIGGER and let CSE clean up later. That is, if we imported a GC field then insert the GC variant, if we imported a non-GC field, pick the non-GC variant, and otherwise stay with the CCTOR_TRIGGER helper. I think it would give us the best of all worlds.

@jkotas
Copy link
Member

jkotas commented Oct 12, 2022

Another question is if we should just move crossgen2 to be on the same plan as NAOT

Yes, having as few differences as possible between crossgen2 and NAOT is goodness.

@jkotas
Copy link
Member

jkotas commented Oct 12, 2022

I guess the reason why somebody introduced CCTOR_TRIGGER for R2R was that it can be just ret in steady state. STATIC_BASE helper needs to return the address in steady state so it is one (on x64) or more (on arm64) extra instructions.

@BrianBohe BrianBohe force-pushed the issue-69635 branch 2 times, most recently from ad5b34c to 452183d Compare October 25, 2022 21:31
@BrianBohe BrianBohe closed this Oct 25, 2022
@BrianBohe BrianBohe marked this pull request as ready for review November 23, 2022 23:25
@BrianBohe
Copy link
Member Author

jit-diff diff --pmi throws no difference:


Summary of Code Size diffs:
(Lower is better)

Total bytes of base: 64260871
Total bytes of diff: 64260871
Total bytes of delta: 0 (0.00 % of base)

0 total files with Code Size differences (0 improved, 0 regressed), 274 unchanged.

0 total methods with Code Size differences (0 improved, 0 regressed), 392186 unchanged.

jit-diff diff throws an overall improvement:

Found 455 files with textual diffs.

Summary of Code Size diffs:
(Lower is better)

Total bytes of base: 37553435
Total bytes of diff: 37552357
Total bytes of delta: -1078 (-0.00 % of base)
Total relative delta: NaN
    diff is an improvement.
    relative diff is a regression.


Top file regressions (bytes):
          13 : Microsoft.CodeAnalysis.CSharp.dasm (0.00% of base)

Top file improvements (bytes):
        -302 : System.Private.Xml.dasm (-0.01% of base)
        -138 : Microsoft.CodeAnalysis.VisualBasic.dasm (-0.00% of base)
        -114 : System.Net.Quic.dasm (-0.16% of base)
         -86 : System.IO.Packaging.dasm (-0.12% of base)
         -82 : Newtonsoft.Json.dasm (-0.01% of base)
         -54 : Microsoft.CodeAnalysis.dasm (-0.00% of base)
         -54 : System.Security.Cryptography.dasm (-0.01% of base)
         -38 : Newtonsoft.Json.Bson.dasm (-0.05% of base)
         -33 : System.CodeDom.dasm (-0.02% of base)
         -30 : System.Drawing.Common.dasm (-0.01% of base)
         -26 : System.Private.DataContractSerialization.dasm (-0.00% of base)
         -24 : System.DirectoryServices.AccountManagement.dasm (-0.01% of base)
         -24 : System.Runtime.Caching.dasm (-0.04% of base)
         -24 : System.Net.HttpListener.dasm (-0.01% of base)
         -23 : Microsoft.VisualBasic.Core.dasm (-0.01% of base)
         -12 : System.Net.Mail.dasm (-0.01% of base)
          -7 : xunit.runner.utility.netcoreapp10.dasm (-0.01% of base)
          -6 : System.Management.dasm (-0.00% of base)
          -6 : System.Security.Cryptography.Pkcs.dasm (-0.00% of base)
          -5 : FSharp.Core.dasm (-0.00% of base)

22 total files with Code Size differences (21 improved, 1 regressed), 252 unchanged.

Top method regressions (bytes):
          12 ( 5.19% of base) : Microsoft.CodeAnalysis.CSharp.dasm - Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.SyntaxToken:Create(ushort):Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.SyntaxToken
          12 ( 2.19% of base) : System.Private.Xml.dasm - System.Xml.Serialization.TypeScope:AddNonXsdPrimitive(System.Type,System.String,System.String,System.String,System.Xml.XmlQualifiedName,System.Xml.Schema.XmlSchemaFacet[],int)
          10 ( 2.30% of base) : Microsoft.CodeAnalysis.CSharp.dasm - Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.SyntaxToken:Create(ushort,Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.CSharpSyntaxNode,Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.CSharpSyntaxNode):Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.SyntaxToken
           8 ( 1.71% of base) : Newtonsoft.Json.Bson.dasm - Newtonsoft.Json.Bson.Utilities.DateTimeParser:ParseTime(byref):bool:this
           8 ( 1.70% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.Utilities.DateTimeParser:ParseTime(byref):bool:this
           5 ( 2.78% of base) : Newtonsoft.Json.Bson.dasm - Newtonsoft.Json.Bson.Utilities.DateTimeParser:Parse(System.String,int,int):bool:this
           5 ( 2.78% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.Utilities.DateTimeParser:Parse(ushort[],int,int):bool:this
           5 ( 1.39% of base) : System.Net.HttpListener.dasm - System.Net.WebSockets.WebSocketProtocolComponent:WebSocketCreateServerHandle(Property[],int):System.Net.WebSockets.SafeWebSocketHandle
           3 ( 0.53% of base) : System.DirectoryServices.AccountManagement.dasm - System.DirectoryServices.AccountManagement.ADStoreCtx:ScanACLForChangePasswordRight(System.DirectoryServices.ActiveDirectorySecurity,byref,byref,byref,byref)
           3 ( 1.07% of base) : System.Drawing.Common.dasm - System.Drawing.ToolboxBitmapAttribute:GetBitmapFromResource(System.Type,System.String,bool,bool):System.Drawing.Image
           2 ( 1.21% of base) : Microsoft.VisualBasic.Core.dasm - Microsoft.VisualBasic.CompilerServices.LikeOperator:CanCharExpand(ushort,ubyte[],System.Globalization.CompareInfo,int):int
           1 ( 0.81% of base) : FSharp.Core.dasm - Microsoft.FSharp.Quotations.FSharpVar:.ctor(System.String,System.Type,Microsoft.FSharp.Core.FSharpOption`1[bool]):this
           1 ( 0.28% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.Utilities.DateTimeUtils:GetDateValues(System.DateTime,byref,byref,byref)
           1 ( 0.19% of base) : System.IO.Packaging.dasm - System.IO.Packaging.PackUriHelper:Create(System.Uri,System.Uri,System.String):System.Uri

Top method improvements (bytes):
         -78 (-8.67% of base) : Microsoft.CodeAnalysis.VisualBasic.dasm - ConversionEasyOut:ClassifyPredefinedConversion(Microsoft.CodeAnalysis.VisualBasic.Symbols.TypeSymbol,Microsoft.CodeAnalysis.VisualBasic.Symbols.TypeSymbol):System.Nullable`1[int]
         -46 (-2.19% of base) : System.Net.Quic.dasm - System.Net.Quic.MsQuicConfiguration:Create(System.Net.Quic.QuicConnectionOptions,int,System.Security.Cryptography.X509Certificates.X509Certificate,System.Security.Cryptography.X509Certificates.X509Certificate[],System.Collections.Generic.List`1[System.Net.Security.SslApplicationProtocol],System.Net.Security.CipherSuitesPolicy,int):System.Net.Quic.MsQuicSafeHandle
         -39 (-9.42% of base) : System.Private.Xml.dasm - ItemType:Create(int,System.Xml.Xsl.XmlQualifiedNameTest,System.Xml.Schema.XmlSchemaType,bool):System.Xml.Xsl.XmlQueryType
         -24 (-3.10% of base) : System.Private.Xml.dasm - System.Xml.Schema.XmlSchemaValidator:BuildXsiAttributes()
         -23 (-0.44% of base) : System.Private.Xml.dasm - System.Xml.Schema.XdrBuilder:.cctor()
         -18 (-20.22% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.Utilities.JavaScriptUtils:GetCharEscapeFlags(int,ushort):bool[]
         -16 (-2.88% of base) : System.Private.Xml.dasm - System.Xml.Schema.XmlSchemaValidator:.cctor()
         -16 (-9.30% of base) : System.Private.Xml.dasm - System.Xml.Schema.XsdValidator:.cctor()
         -15 (-9.26% of base) : System.CodeDom.dasm - System.CodeDom.Compiler.CodeDomProvider:AddCompilerInfo(System.CodeDom.Compiler.CompilerInfo)
         -15 (-3.37% of base) : System.Private.Xml.dasm - System.Xml.Schema.DatatypeImplementation:FinishBuiltinType(System.Xml.Schema.XmlSchemaSimpleType,System.Xml.Schema.XmlSchemaSimpleType)
         -13 (-4.22% of base) : System.IO.Packaging.dasm - System.IO.Packaging.PackUriHelper:ComparePackUri(System.Uri,System.Uri):int
         -12 (-4.90% of base) : System.Private.Xml.dasm - ChoiceType:Create(int):System.Xml.Xsl.XmlQueryType
         -12 (-13.04% of base) : System.Private.Xml.dasm - ItemType:Create(int,bool):System.Xml.Xsl.XmlQueryType
         -12 (-7.55% of base) : System.Private.Xml.dasm - ItemType:Create(System.IO.BinaryReader):System.Xml.Xsl.XmlQueryType
         -12 (-0.73% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.Bson.BsonBinaryWriter:WriteTokenInternal(Newtonsoft.Json.Bson.BsonToken):this
         -12 (-2.81% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.Converters.JavaScriptDateTimeConverter:WriteJson(Newtonsoft.Json.JsonWriter,System.Object,Newtonsoft.Json.JsonSerializer):this
         -12 (-4.15% of base) : System.Security.Cryptography.dasm - System.Security.Cryptography.OidLookup:ToFriendlyName(System.String,int,bool):System.String
         -12 (-4.46% of base) : System.Security.Cryptography.dasm - System.Security.Cryptography.OidLookup:ToOid(System.String,int,bool):System.String
         -11 (-0.79% of base) : System.Private.Xml.dasm - ItemType:.cctor()
         -10 (-1.36% of base) : Microsoft.CodeAnalysis.VisualBasic.dasm - Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax.ExpressionEvaluator:EvaluateTernaryIfExpression(Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax.TernaryConditionalExpressionSyntax):Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax.CConst:this

Top method regressions (percentages):
          12 ( 5.19% of base) : Microsoft.CodeAnalysis.CSharp.dasm - Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.SyntaxToken:Create(ushort):Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.SyntaxToken
           5 ( 2.78% of base) : Newtonsoft.Json.Bson.dasm - Newtonsoft.Json.Bson.Utilities.DateTimeParser:Parse(System.String,int,int):bool:this
           5 ( 2.78% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.Utilities.DateTimeParser:Parse(ushort[],int,int):bool:this
          10 ( 2.30% of base) : Microsoft.CodeAnalysis.CSharp.dasm - Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.SyntaxToken:Create(ushort,Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.CSharpSyntaxNode,Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.CSharpSyntaxNode):Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax.SyntaxToken
          12 ( 2.19% of base) : System.Private.Xml.dasm - System.Xml.Serialization.TypeScope:AddNonXsdPrimitive(System.Type,System.String,System.String,System.String,System.Xml.XmlQualifiedName,System.Xml.Schema.XmlSchemaFacet[],int)
           8 ( 1.71% of base) : Newtonsoft.Json.Bson.dasm - Newtonsoft.Json.Bson.Utilities.DateTimeParser:ParseTime(byref):bool:this
           8 ( 1.70% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.Utilities.DateTimeParser:ParseTime(byref):bool:this
           5 ( 1.39% of base) : System.Net.HttpListener.dasm - System.Net.WebSockets.WebSocketProtocolComponent:WebSocketCreateServerHandle(Property[],int):System.Net.WebSockets.SafeWebSocketHandle
           2 ( 1.21% of base) : Microsoft.VisualBasic.Core.dasm - Microsoft.VisualBasic.CompilerServices.LikeOperator:CanCharExpand(ushort,ubyte[],System.Globalization.CompareInfo,int):int
           3 ( 1.07% of base) : System.Drawing.Common.dasm - System.Drawing.ToolboxBitmapAttribute:GetBitmapFromResource(System.Type,System.String,bool,bool):System.Drawing.Image
           1 ( 0.81% of base) : FSharp.Core.dasm - Microsoft.FSharp.Quotations.FSharpVar:.ctor(System.String,System.Type,Microsoft.FSharp.Core.FSharpOption`1[bool]):this
           3 ( 0.53% of base) : System.DirectoryServices.AccountManagement.dasm - System.DirectoryServices.AccountManagement.ADStoreCtx:ScanACLForChangePasswordRight(System.DirectoryServices.ActiveDirectorySecurity,byref,byref,byref,byref)
           1 ( 0.28% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.Utilities.DateTimeUtils:GetDateValues(System.DateTime,byref,byref,byref)
           1 ( 0.19% of base) : System.IO.Packaging.dasm - System.IO.Packaging.PackUriHelper:Create(System.Uri,System.Uri,System.String):System.Uri

Top method improvements (percentages):
          -6 (-24.00% of base) : System.IO.Packaging.dasm - System.IO.Packaging.PackageXmlStringTable:get_NameTable():System.Xml.NameTable
          -6 (-24.00% of base) : System.IO.Packaging.dasm - System.IO.Packaging.PackUriHelper:get_PackageRootUri():System.Uri
          -6 (-21.43% of base) : System.Drawing.Common.dasm - System.Drawing.BufferedGraphicsManager:get_Current():System.Drawing.BufferedGraphicsContext
          -6 (-21.43% of base) : System.Net.Quic.dasm - System.Net.Quic.MsQuicApi:get_IsQuicSupported():bool
          -6 (-21.43% of base) : System.Net.Quic.dasm - System.Net.Quic.MsQuicApi:get_Tls13ClientMayBeDisabled():bool
          -6 (-21.43% of base) : System.Net.Quic.dasm - System.Net.Quic.MsQuicApi:get_Tls13ServerMayBeDisabled():bool
          -6 (-21.43% of base) : System.Net.Quic.dasm - System.Net.Quic.MsQuicApi:get_UsesSChannelBackend():bool
          -6 (-21.43% of base) : System.Net.Quic.dasm - System.Net.Quic.QuicConnection:get_IsSupported():bool
          -6 (-21.43% of base) : System.Net.Quic.dasm - System.Net.Quic.QuicListener:get_IsSupported():bool
          -6 (-21.43% of base) : System.Runtime.Caching.dasm - System.Runtime.Caching.MemoryMonitor:get_TotalPhysical():long
          -6 (-21.43% of base) : System.Runtime.Caching.dasm - System.Runtime.Caching.MemoryMonitor:get_TotalVirtual():long
          -6 (-21.43% of base) : System.Private.Xml.dasm - System.Xml.Schema.DatatypeImplementation:get_AnyAtomicType():System.Xml.Schema.XmlSchemaSimpleType
          -6 (-21.43% of base) : System.Private.Xml.dasm - System.Xml.Schema.DatatypeImplementation:get_AnySimpleType():System.Xml.Schema.XmlSchemaSimpleType
          -6 (-21.43% of base) : System.Private.Xml.dasm - System.Xml.Schema.DatatypeImplementation:get_UntypedAtomicType():System.Xml.Schema.XmlSchemaSimpleType
          -6 (-21.43% of base) : System.Private.Xml.dasm - System.Xml.Schema.DatatypeImplementation:GetBuiltInTypes():System.Xml.Schema.XmlSchemaSimpleType[]
          -6 (-21.43% of base) : System.Private.Xml.dasm - System.Xml.Serialization.TypeScope:get_PrimtiveTypes():System.Collections.Hashtable
         -18 (-20.22% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.Utilities.JavaScriptUtils:GetCharEscapeFlags(int,ushort):bool[]
          -6 (-17.14% of base) : System.Drawing.Common.dasm - Gdip:get_Initialized():bool
          -6 (-17.14% of base) : System.Net.HttpListener.dasm - System.Net.HttpListenerRequest:get_SupportsWebSockets():bool
          -6 (-17.14% of base) : System.Net.HttpListener.dasm - System.Net.WebSockets.WebSocketProtocolComponent:get_IsSupported():bool

174 total methods with Code Size differences (160 improved, 14 regressed), 272590 unchanged.

@BrianBohe
Copy link
Member Author

Now one of the helpers calls is being repeated at the beginning of the code and its result is being stored in a register. Then one register is being locked since the beginning of the code, triggering a cascade regression in a few examples, and improvements in others.

@BrianBohe
Copy link
Member Author

If we duplicate the first helper call we may avoid the regression cases (holding a register for a long time). Do we know at import phase if the relative order of the helper calls will persist?

@jakobbotsch
Copy link
Member

Now one of the helpers calls is being repeated at the beginning of the code and its result is being stored in a register. Then one register is being locked since the beginning of the code, triggering a cascade regression in a few examples, and improvements in others.

Can you show the diff?

I wouldn't be worried about a few minor regressions, especially if it is just due to slightly worse register allocation. That is unavoidable in many cases when making changes early in the JIT phases, and trying to compensate for it upstream makes things very entangled.

It is initialized with CORINFO_HELP_UNDEF and set to R2R GC or NON
GC Static Base later.
@BrianBohe BrianBohe linked an issue Dec 10, 2022 that may be closed by this pull request
@BrianBohe
Copy link
Member Author

New diff looks like this:

Found 455 files with textual diffs.

Summary of Code Size diffs:
(Lower is better)

Total bytes of base: 36015141
Total bytes of diff: 36014151
Total bytes of delta: -990 (-0.00 % of base)
Total relative delta: NaN
    diff is an improvement.
    relative diff is a regression.


Top file improvements (bytes):
        -248 : System.Private.Xml.dasm (-0.01% of base)
        -138 : Microsoft.CodeAnalysis.VisualBasic.dasm (-0.00% of base)
        -102 : Newtonsoft.Json.dasm (-0.02% of base)
         -76 : System.Net.Quic.dasm (-0.12% of base)
         -72 : System.IO.Packaging.dasm (-0.11% of base)
         -57 : Newtonsoft.Json.Bson.dasm (-0.08% of base)
         -54 : System.Security.Cryptography.dasm (-0.01% of base)
         -54 : Microsoft.CodeAnalysis.dasm (-0.00% of base)
         -33 : System.CodeDom.dasm (-0.02% of base)
         -24 : System.DirectoryServices.AccountManagement.dasm (-0.01% of base)
         -24 : System.Drawing.Common.dasm (-0.01% of base)
         -21 : Microsoft.CodeAnalysis.CSharp.dasm (-0.00% of base)
         -16 : Microsoft.VisualBasic.Core.dasm (-0.00% of base)
         -12 : System.Net.Mail.dasm (-0.01% of base)
         -12 : System.Net.HttpListener.dasm (-0.01% of base)
         -12 : System.Runtime.Caching.dasm (-0.02% of base)
          -8 : System.Private.DataContractSerialization.dasm (-0.00% of base)
          -7 : xunit.runner.utility.netcoreapp10.dasm (-0.01% of base)
          -6 : System.Management.dasm (-0.00% of base)
          -6 : System.Security.Cryptography.Pkcs.dasm (-0.00% of base)

22 total files with Code Size differences (22 improved, 0 regressed), 252 unchanged.

Top method regressions (bytes):
          12 ( 2.19% of base) : System.Private.Xml.dasm - System.Xml.Serialization.TypeScope:AddNonXsdPrimitive(System.Type,System.String,System.String,System.String,System.Xml.XmlQualifiedName,System.Xml.Schema.XmlSchemaFacet[],int)
           5 ( 2.78% of base) : Newtonsoft.Json.Bson.dasm - Newtonsoft.Json.Bson.Utilities.DateTimeParser:Parse(System.String,int,int):bool:this
           5 ( 2.78% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.Utilities.DateTimeParser:Parse(ushort[],int,int):bool:this
           5 ( 1.39% of base) : System.Net.HttpListener.dasm - System.Net.WebSockets.WebSocketProtocolComponent:WebSocketCreateServerHandle(Interop+WebSocket+Property[],int):System.Net.WebSockets.SafeWebSocketHandle
           3 ( 0.53% of base) : System.DirectoryServices.AccountManagement.dasm - System.DirectoryServices.AccountManagement.ADStoreCtx:ScanACLForChangePasswordRight(System.DirectoryServices.ActiveDirectorySecurity,byref,byref,byref,byref)
           3 ( 1.07% of base) : System.Drawing.Common.dasm - System.Drawing.ToolboxBitmapAttribute:GetBitmapFromResource(System.Type,System.String,bool,bool):System.Drawing.Image
           2 ( 1.21% of base) : Microsoft.VisualBasic.Core.dasm - Microsoft.VisualBasic.CompilerServices.LikeOperator:CanCharExpand(ushort,ubyte[],System.Globalization.CompareInfo,int):int
           1 ( 0.81% of base) : FSharp.Core.dasm - Microsoft.FSharp.Quotations.FSharpVar:.ctor(System.String,System.Type,Microsoft.FSharp.Core.FSharpOption`1[bool]):this
           1 ( 0.19% of base) : System.IO.Packaging.dasm - System.IO.Packaging.PackUriHelper:Create(System.Uri,System.Uri,System.String):System.Uri

Top method improvements (bytes):
         -78 (-8.67% of base) : Microsoft.CodeAnalysis.VisualBasic.dasm - Microsoft.CodeAnalysis.VisualBasic.Conversions+ConversionEasyOut:ClassifyPredefinedConversion(Microsoft.CodeAnalysis.VisualBasic.Symbols.TypeSymbol,Microsoft.CodeAnalysis.VisualBasic.Symbols.TypeSymbol):System.Nullable`1[int]
         -46 (-2.21% of base) : System.Net.Quic.dasm - System.Net.Quic.MsQuicConfiguration:Create(System.Net.Quic.QuicConnectionOptions,int,System.Security.Cryptography.X509Certificates.X509Certificate,System.Security.Cryptography.X509Certificates.X509Certificate[],System.Collections.Generic.List`1[System.Net.Security.SslApplicationProtocol],System.Net.Security.CipherSuitesPolicy,int):System.Net.Quic.MsQuicSafeHandle
         -39 (-9.42% of base) : System.Private.Xml.dasm - System.Xml.Xsl.XmlQueryTypeFactory+ItemType:Create(int,System.Xml.Xsl.XmlQualifiedNameTest,System.Xml.Schema.XmlSchemaType,bool):System.Xml.Xsl.XmlQueryType
         -24 (-3.10% of base) : System.Private.Xml.dasm - System.Xml.Schema.XmlSchemaValidator:BuildXsiAttributes()
         -23 (-0.44% of base) : System.Private.Xml.dasm - System.Xml.Schema.XdrBuilder:.cctor()
         -18 (-20.22% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.Utilities.JavaScriptUtils:GetCharEscapeFlags(int,ushort):bool[]
         -16 (-2.88% of base) : System.Private.Xml.dasm - System.Xml.Schema.XmlSchemaValidator:.cctor()
         -16 (-9.30% of base) : System.Private.Xml.dasm - System.Xml.Schema.XsdValidator:.cctor()
         -15 (-9.26% of base) : System.CodeDom.dasm - System.CodeDom.Compiler.CodeDomProvider:AddCompilerInfo(System.CodeDom.Compiler.CompilerInfo)
         -15 (-3.37% of base) : System.Private.Xml.dasm - System.Xml.Schema.DatatypeImplementation:FinishBuiltinType(System.Xml.Schema.XmlSchemaSimpleType,System.Xml.Schema.XmlSchemaSimpleType)
         -13 (-4.22% of base) : System.IO.Packaging.dasm - System.IO.Packaging.PackUriHelper:ComparePackUri(System.Uri,System.Uri):int
         -12 (-0.73% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.Bson.BsonBinaryWriter:WriteTokenInternal(Newtonsoft.Json.Bson.BsonToken):this
         -12 (-2.81% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.Converters.JavaScriptDateTimeConverter:WriteJson(Newtonsoft.Json.JsonWriter,System.Object,Newtonsoft.Json.JsonSerializer):this
         -12 (-4.15% of base) : System.Security.Cryptography.dasm - System.Security.Cryptography.OidLookup:ToFriendlyName(System.String,int,bool):System.String
         -12 (-4.46% of base) : System.Security.Cryptography.dasm - System.Security.Cryptography.OidLookup:ToOid(System.String,int,bool):System.String
         -12 (-4.90% of base) : System.Private.Xml.dasm - System.Xml.Xsl.XmlQueryTypeFactory+ChoiceType:Create(int):System.Xml.Xsl.XmlQueryType
         -12 (-7.55% of base) : System.Private.Xml.dasm - System.Xml.Xsl.XmlQueryTypeFactory+ItemType:Create(System.IO.BinaryReader):System.Xml.Xsl.XmlQueryType
         -11 (-2.35% of base) : Newtonsoft.Json.Bson.dasm - Newtonsoft.Json.Bson.Utilities.DateTimeParser:ParseTime(byref):bool:this
         -11 (-2.34% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.Utilities.DateTimeParser:ParseTime(byref):bool:this
         -11 (-0.79% of base) : System.Private.Xml.dasm - System.Xml.Xsl.XmlQueryTypeFactory+ItemType:.cctor()

Top method regressions (percentages):
           5 ( 2.78% of base) : Newtonsoft.Json.Bson.dasm - Newtonsoft.Json.Bson.Utilities.DateTimeParser:Parse(System.String,int,int):bool:this
           5 ( 2.78% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.Utilities.DateTimeParser:Parse(ushort[],int,int):bool:this
          12 ( 2.19% of base) : System.Private.Xml.dasm - System.Xml.Serialization.TypeScope:AddNonXsdPrimitive(System.Type,System.String,System.String,System.String,System.Xml.XmlQualifiedName,System.Xml.Schema.XmlSchemaFacet[],int)
           5 ( 1.39% of base) : System.Net.HttpListener.dasm - System.Net.WebSockets.WebSocketProtocolComponent:WebSocketCreateServerHandle(Interop+WebSocket+Property[],int):System.Net.WebSockets.SafeWebSocketHandle
           2 ( 1.21% of base) : Microsoft.VisualBasic.Core.dasm - Microsoft.VisualBasic.CompilerServices.LikeOperator:CanCharExpand(ushort,ubyte[],System.Globalization.CompareInfo,int):int
           3 ( 1.07% of base) : System.Drawing.Common.dasm - System.Drawing.ToolboxBitmapAttribute:GetBitmapFromResource(System.Type,System.String,bool,bool):System.Drawing.Image
           1 ( 0.81% of base) : FSharp.Core.dasm - Microsoft.FSharp.Quotations.FSharpVar:.ctor(System.String,System.Type,Microsoft.FSharp.Core.FSharpOption`1[bool]):this
           3 ( 0.53% of base) : System.DirectoryServices.AccountManagement.dasm - System.DirectoryServices.AccountManagement.ADStoreCtx:ScanACLForChangePasswordRight(System.DirectoryServices.ActiveDirectorySecurity,byref,byref,byref,byref)
           1 ( 0.19% of base) : System.IO.Packaging.dasm - System.IO.Packaging.PackUriHelper:Create(System.Uri,System.Uri,System.String):System.Uri

Top method improvements (percentages):
          -6 (-21.43% of base) : System.Drawing.Common.dasm - System.Drawing.BufferedGraphicsManager:get_Current():System.Drawing.BufferedGraphicsContext
          -6 (-21.43% of base) : System.Net.Quic.dasm - System.Net.Quic.QuicConnection:get_IsSupported():bool
          -6 (-21.43% of base) : System.Net.Quic.dasm - System.Net.Quic.QuicListener:get_IsSupported():bool
          -6 (-21.43% of base) : System.Private.Xml.dasm - System.Xml.Schema.DatatypeImplementation:get_AnySimpleType():System.Xml.Schema.XmlSchemaSimpleType
         -18 (-20.22% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.Utilities.JavaScriptUtils:GetCharEscapeFlags(int,ushort):bool[]
          -6 (-16.67% of base) : System.CodeDom.dasm - System.CodeDom.Compiler.CodeDomProvider:GetAllCompilerInfo():System.CodeDom.Compiler.CompilerInfo[]
          -6 (-11.54% of base) : Microsoft.CodeAnalysis.dasm - Microsoft.CodeAnalysis.SpecialTypes:GetTypeFromMetadataName(int):byte
          -6 (-11.11% of base) : Microsoft.CodeAnalysis.dasm - Microsoft.CodeAnalysis.SpecialTypes:GetTypeCode(byte):int
          -6 (-11.11% of base) : Microsoft.CodeAnalysis.dasm - Microsoft.CodeAnalysis.WellKnownTypes:GetMetadataName(int):System.String
          -6 (-10.91% of base) : Microsoft.CodeAnalysis.dasm - Microsoft.CodeAnalysis.SpecialTypes:GetMetadataName(byte):System.String
          -6 (-10.34% of base) : System.Security.Cryptography.dasm - System.Security.Cryptography.XmlKeyHelper+ParseState+Functions:GetValue(System.Object):System.String
          -6 (-10.34% of base) : System.Private.Xml.dasm - System.Xml.Schema.DatatypeImplementation:GetSimpleTypeFromXsdType(System.Xml.XmlQualifiedName):System.Xml.Schema.XmlSchemaSimpleType
          -6 (-10.34% of base) : System.Private.Xml.dasm - System.Xml.Serialization.TypeScope:GetTypeDesc(System.Xml.Schema.XmlSchemaSimpleType):System.Xml.Serialization.TypeDesc
         -39 (-9.42% of base) : System.Private.Xml.dasm - System.Xml.Xsl.XmlQueryTypeFactory+ItemType:Create(int,System.Xml.Xsl.XmlQualifiedNameTest,System.Xml.Schema.XmlSchemaType,bool):System.Xml.Xsl.XmlQueryType
         -16 (-9.30% of base) : System.Private.Xml.dasm - System.Xml.Schema.XsdValidator:.cctor()
         -15 (-9.26% of base) : System.CodeDom.dasm - System.CodeDom.Compiler.CodeDomProvider:AddCompilerInfo(System.CodeDom.Compiler.CompilerInfo)
          -6 (-9.09% of base) : Newtonsoft.Json.Bson.dasm - Newtonsoft.Json.Bson.Utilities.DateTimeUtils:UniversialTicksToJavaScriptTicks(long):long
          -6 (-9.09% of base) : Newtonsoft.Json.dasm - Newtonsoft.Json.Utilities.DateTimeUtils:UniversalTicksToJavaScriptTicks(long):long
          -6 (-9.09% of base) : System.DirectoryServices.AccountManagement.dasm - System.DirectoryServices.AccountManagement.FilterFactory:CreateFilter(System.String):System.Object
          -6 (-8.70% of base) : Newtonsoft.Json.Bson.dasm - Newtonsoft.Json.Bson.Utilities.DateTimeUtils:ConvertJavaScriptTicksToDateTime(long):System.DateTime

144 total methods with Code Size differences (135 improved, 9 regressed), 248979 unchanged.

--------------------------------------------------------------------------------

@BrianBohe
Copy link
Member Author

BrianBohe commented Dec 10, 2022

Regressions looks to be cases in which CSE didn't took place or took place, but the assignation of registers end up adding a few bytes more, something very similar to what I found previously. Here for example, in diff (right), r14 is used to store the result of the helper call, where previously on base (left) rax was used directly.

image

@BrianBohe
Copy link
Member Author

Here is another example, in this scenario base (left) is not storing the return from its first call to CORINFO_HELP_READYTORUN_GCSTATIC_BASE, and diff (right) stores it but not CSE the call with its next appearance. Diff does CSE but with another helper call that is further below in another instruction group.

image

@jakobbotsch
Copy link
Member

If CSE decides to introduce long lived temporaries that aren't profitable it's a CSE problem. I don't think you need to be concerned that this change provokes that for a small number of cases.

Here is another example, in this scenario base (left) is not storing the return from its first call to CORINFO_HELP_READYTORUN_GCSTATIC_BASE, and diff (right) stores it but not CSE the call with its next appearance. Diff does CSE but with another helper call that is further below in another instruction group.

It sounds likely that the second CORINFO_HELP_READYTORUN_GCSTATIC_BASE call, the one that does not get CSE'd, is for a different class than the first and third ones (i.e. it has a different R2R entry point). Can you double check?

@BrianBohe
Copy link
Member Author

BrianBohe commented Dec 13, 2022

It sounds likely that the second CORINFO_HELP_READYTORUN_GCSTATIC_BASE call, the one that does not get CSE'd, is for a different class than the first and third ones (i.e. it has a different R2R entry point). Can you double check?

You are right, they are from different classes:

4be54: ff 15 a6 42 2b 00        call    qword ptr [0x300100]  // System.Xml.Serialization.TypeScope (STATIC_BASE_GC)
4be5a: 4c 8b f8                 mov     r15, rax
4be5d: ff 15 ad 42 2b 00        call    qword ptr [0x300110]  // System.Xml.Schema.XmlSchemaSimpleType (NEW_OBJECT)
4be63: 4c 8b e0                 mov     r12, rax
4be66: 41 c7 44 24 78 00 01 00 00  mov  dword ptr [r12 + 120], 256
4be6f: ff 15 83 42 2b 00        call    qword ptr [0x3000f8]  // System.Xml.XmlQualifiedName (STATIC_BASE_GC)

@BrianBohe BrianBohe merged commit c635ae2 into dotnet:main Dec 13, 2022
@ghost ghost locked as resolved and limited conversation to collaborators Jan 12, 2023
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.

jit disasm is confusing for R2R helpers with different entry points
5 participants