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

Allow jit to examine type of initonly static ref typed fields #20886

Merged

Conversation

AndyAyersMS
Copy link
Member

The jit incorporates the value of integer and float typed initonly static
fields into its codegen, if the class initializer has already run.

The jit can't incorporate the values of ref typed initonly static fields,
but the types of those values can't change, and the jit can use this knowledge
to enable type based optimizations like devirtualization.

In particular for static fields initialized by complex class factory logic the
jit can now see the end result of that logic instead of having to try and deduce
the type of object that will initialize or did initialize the field.

Examples of this factory pattern in include EqualityComparer<T>.Default and
Comparer<T>.Default. The former is already optimized in some cases by via
special-purpose modelling in the framework, jit, and runtime (see #14125) but
the latter is not. With this change calls through Comparer<T>.Default may now
also devirtualize (though won't yet inline as the devirtualization happens
late).

Addresses #4108.

{
MethodTable* pEnclosingMT = field->GetEnclosingMethodTable();

// We must not call here for statics of collectible types.
Copy link
Member

Choose a reason for hiding this comment

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

Why not? What guarantees it that you won't come here for collectible types?

Copy link
Member Author

Choose a reason for hiding this comment

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

Not sure. This follows what we do with getFieldAddress so there is evidently some constraint on how the jit models more complex field accesses that leads us to never call back for these cases.

But probably better not to rely on this and just add the disqualifying check here.

Copy link
Member

Choose a reason for hiding this comment

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

I do not think there needs to be disqualifying check on this path. This path should work just fine for fields on collectible types too.

The reason why getFieldAddress does not return addresses for collectible types is that the storage is not pinned for them, and so you cannot burn the address into the JITed code.

@@ -417,6 +417,9 @@ class ZapInfo

void * getFieldAddress(CORINFO_FIELD_HANDLE field,
void **ppIndirection);
CORINFO_CLASS_HANDLE getStaticFieldCurrentClass(CORINFO_FIELD_HANDLE field,
bool* isInitO);
Copy link
Member

Choose a reason for hiding this comment

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

Typo

}
}

// If the field is init only, and the class is known, the class can't ever change.
Copy link
Member

Choose a reason for hiding this comment

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

This is not sufficient condition. The static constructor has to be finished running for this to be true. Consider this:

using System;

class Test
{
    static readonly object s;

    static Test()
    {
       s = new object();
       DoStuff();
       s = "Method";
    }

    static void DoStuff()
    {
        Console.WriteLine(s.GetType());
    }

    static void Main()
    {
    }
}

Copy link
Member Author

Choose a reason for hiding this comment

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

Good point -- is checking that pEnclosingMT->IsClassInited() sufficient?

Copy link
Member

Choose a reason for hiding this comment

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

Yes, it should be. You may want to do it inside the block above only when you are returning the classhandle (e.g. IsClassInited does not work when pEnclosingMT is shared generic).

Copy link
Member

Choose a reason for hiding this comment

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

Also, isInitOnly is not the best name of the flag once you fix this...

Copy link
Member Author

Choose a reason for hiding this comment

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

Suggestions?

Copy link
Member

Choose a reason for hiding this comment

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

bool *pIsFinal or bool *pIsImmutable?

Or flip this around and have bool *pIsSpeculative flag instead. When the EE returns *pIsSpeculative = true, it would mean that the answer is a guess that the JIT has to emit a test for. I think bool *pIsSpeculative would work well as a general pattern for other APIs (e.g. devirtualization). If the JIT is not interested in speculative answer, it would pass pIsSpeculative: NULL.

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks. isSpeculative is a great suggestion. That's what I'll go with.

@AndyAyersMS
Copy link
Member Author

AndyAyersMS commented Nov 8, 2018

@jkotas, @erozenfeld PTAL
cc @dotnet/jit-contrib @davidwrighton

Impact of this on codegen is a bit tricky to evaluate, but here's one take.

Jit-diffs (in the new --ccrtors mode) shows:

Total bytes of diff: 41126 (0.11% of base)
    diff is a regression.

Total byte diff includes 42793 bytes from reconciling methods
        Base had    0 unique methods,        0 unique bytes
        Diff had  155 unique methods,    42793 unique bytes

Top file regressions by size (bytes):
       45139 : System.Private.CoreLib.dasm (1.45% of base)
        1051 : Microsoft.CodeAnalysis.dasm (0.07% of base)
          70 : System.Private.DataContractSerialization.dasm (0.01% of base)

Top file improvements by size (bytes):
       -3646 : System.Private.Xml.dasm (-0.10% of base)
        -226 : Microsoft.CodeAnalysis.VisualBasic.dasm (0.00% of base)
        -158 : System.Data.Common.dasm (-0.01% of base)
        -148 : System.Net.Http.dasm (-0.03% of base)
        -134 : Newtonsoft.Json.dasm (-0.02% of base)

47 total files with size differences (44 improved, 3 regressed), 82 unchanged.

Top method regressions by size (bytes):
        2304 (     8 of base) : System.Private.CoreLib.dasm - System.Reflection.CustomAttribute:GetCustomAttributes(ref,int,int,ref,bool,byref):ref (0 base, 1 diff methods)
        1453 (     8 of base) : System.Private.CoreLib.dasm - System.AppDomain:Setup(ref):ref (0 base, 1 diff methods)
        1301 (     8 of base) : System.Private.CoreLib.dasm - System.RuntimeType:GetMethodBase(ref,long):ref (0 base, 1 diff methods)
        1173 (     8 of base) : System.Private.CoreLib.dasm - System.Reflection.CustomAttribute:FilterCustomAttributeRecord(struct,struct,byref,ref,struct,ref,bool,ref,byref,byref,byref,byref,byref):bool (0 base, 1 diff methods)
        1157 (     8 of base) : System.Private.CoreLib.dasm - System.MulticastDelegate:CombineImpl(ref):ref:this (0 base, 1 diff methods)

Top method improvements by size (bytes):
        -505 (-6.88% of base) : System.Private.Xml.dasm - XmlUntypedConverter:ChangeType(ref,ref,ref):ref:this (2 methods)
        -298 (-10.40% of base) : System.Private.Xml.dasm - XmlNumeric10Converter:ChangeType(ref,ref,ref):ref:this (2 methods)
        -273 (-16.99% of base) : System.Private.Xml.dasm - XmlListConverter:ChangeListType(ref,ref,ref):ref:this
        -224 (-5.77% of base) : System.Private.Xml.dasm - XmlAnyConverter:ChangeType(ref,ref,ref):ref:this (2 methods)
        -209 (-39.51% of base) : System.Private.Xml.dasm - XmlUntypedConverter:SupportsType(ref):bool:this

Top method regressions by size (percentage):
         232 (     8 of base) : System.Private.CoreLib.dasm - System.AppDomain:SetupDomain(bool,ref,ref,ref,ref):this
         197 (     8 of base) : System.Private.CoreLib.dasm - System.AppDomain:SetupFusionStore(ref,ref):this (0 base, 1 diff methods)
         330 (     8 of base) : System.Private.CoreLib.dasm - System.AppDomainSetup:SetupDefaults(ref,bool):this (0 base, 1 diff methods)
          26 (     8 of base) : System.Private.CoreLib.dasm - System.String:LastIndexOfAny(ref):int:this (0 base, 1 diff methods)
         391 (     8 of base) : System.Private.CoreLib.dasm - System.String:LastIndexOfAny(ref,int,int):int:this (0 base, 1 diff methods)

Top method improvements by size (percentage):
         -25 (-50.00% of base) : Microsoft.CodeAnalysis.dasm - <>c__DisplayClass13_0:<ClearAnalysisScopeState>b__0(ref):bool:this
        -209 (-39.51% of base) : System.Private.Xml.dasm - XmlUntypedConverter:SupportsType(ref):bool:this
         -53 (-37.59% of base) : System.Private.Xml.dasm - XmlListConverter:IsListType(ref):bool:this
         -54 (-35.53% of base) : System.Private.Xml.dasm - XmlMiscConverter:ChangeTypeWildcardSource(ref,ref,ref):ref:this
         -54 (-35.53% of base) : System.Private.Xml.dasm - XmlAnyConverter:ChangeTypeWildcardSource(ref,ref,ref):ref:this

603 total methods with size differences (403 improved, 200 regressed), 190032 unchanged.

Most of the regressions here are "reconciliation" issues in corelib, where we see different methods get jitted. Almost certainly a testing artifact, though something I need to look at more closely.

Improvements are seen for fields like current culture and text encoder, some specialized type comparisons, and number of cases where we already were devirtualizing but can now also omit a null check.

Also makes some headway into the patterns for the test case in #17273 that we don't optimize very well; eg in Common() we now can do a late devirtualization.

;; before
       mov      rax, qword ptr [rcx]
       mov      rax, qword ptr [rax+64]
       call     qword ptr [rax+32]EqualityComparer`1:Equals(ref,ref):bool:this

;; after
       call     GenericEqualityComparer`1:Equals(ref,ref):bool:this

@AndyAyersMS
Copy link
Member Author

The (apparently new) dotnet-coreclr leg failed with:

2018-11-08T20:24:46.1616930Z ##[error]Microsoft.VisualStudio.Services.Common.VssUnauthorizedException: TF400813: The user 'xxx-some-guid-xxx' is not authorized to access this resource.
   at Microsoft.VisualStudio.Services.Common.VssHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)

@jkoritzinsky
Copy link
Member

Yeah that failure is a bug in Azure DevOps that causes the artifact publishing to fail on PR builds from forks. I think they've known about it for a few weeks now.

@AndyAyersMS
Copy link
Member Author

Ubuntu failures look like odd glitches:

error MSB3095: Invalid argument. Item "/mnt/j/workspace/dotnet_coreclr/master/x64_checked_ubuntu_innerloop_prtest/packages/microsoft.private.corefx.netcoreapp/4.6.0-preview.18553.3/ref/netcoreapp3.0/System.Net.HttpListener.xml" has attribute "CopyLocal" with value "{RawFileName}" that could not be converted to "bool". [/mnt/j/workspace/dotnet_coreclr/master/x64_checked_ubuntu_innerloop_prtest/tests/src/JIT/Directed/Misc/function_pointer/MutualThdRecur-fptr.ilproj]

Arm failure was a disconnect:

17:55:04 FATAL: command execution failed
17:55:04 java.nio.channels.ClosedChannelException
17:55:04 	at org.jenkinsci.remoting.protocol.impl.ChannelApplicationLayer.onReadClosed(ChannelApplicationLayer.java:208)
17:55:04 	at org.jenkinsci.remoting.protocol.ApplicationLayer.onRecvClosed(ApplicationLayer.java:222)

Am about to push an update so won't rerun.

@AndyAyersMS
Copy link
Member Author

@dotnet/jit-contrib one thing I should highlight.

Because the values returned by this new jit interface method can change over time (even in the non-speculative case), it creates a problem for SPMI. I haven't looked at SPMI in detail but my guess is that only the last of these answers will survive in the results cache. So the SPMI replay may not be completely faithful recreation of the jit-ee traffic.

I thought about caching the result of this query on the jit side on the root compiler, so that from the jit perspective the answer wouldn't change in the middle of jitting a method, but haven't done that. So it's possible in a method with a lot of field accesses that they get optimized differently if a jitting thread is racing with an initializing thread.

Note we have this situation already with int and float typed initonly statics.

@jkotas
Copy link
Member

jkotas commented Nov 9, 2018

Note we have this situation already with int and float typed initonly statics.

And number of other cases. For example, you can get different answers for whether the constructor triggers are required over time.

@AndyAyersMS
Copy link
Member Author

Reran PMI and suspect my previous results had version skew or something (hence all the unique to diff methods). New results:

Total bytes of diff: -4002 (-0.01% of base)
    diff is an improvement.

Top file regressions by size (bytes):
        1058 : Microsoft.CodeAnalysis.dasm (0.07% of base)
          70 : System.Private.DataContractSerialization.dasm (0.01% of base)

Top file improvements by size (bytes):
       -3605 : System.Private.Xml.dasm (-0.10% of base)
        -226 : Microsoft.CodeAnalysis.VisualBasic.dasm (0.00% of base)
        -158 : System.Data.Common.dasm (-0.01% of base)
        -148 : System.Net.Http.dasm (-0.03% of base)
        -134 : Newtonsoft.Json.dasm (-0.02% of base)

47 total files with size differences (45 improved, 2 regressed), 82 unchanged.

Top method regressions by size (bytes):
         588 (92.45% of base) : Microsoft.CodeAnalysis.dasm - OneToOneUnicodeComparer:Compare(ref,ref):int:this
         492 (88.97% of base) : Microsoft.CodeAnalysis.dasm - OneToOneUnicodeComparer:Equals(ref,ref):bool:this
         297 (85.59% of base) : Microsoft.CodeAnalysis.dasm - OneToOneUnicodeComparer:EndsWith(ref,ref):bool
         155 (116.54% of base) : Microsoft.CodeAnalysis.dasm - OneToOneUnicodeComparer:GetHashCode(ref):int:this
         146 (96.69% of base) : Microsoft.CodeAnalysis.dasm - CaseInsensitiveComparison:ToLower(ref)

Top method improvements by size (bytes):
        -505 (-6.79% of base) : System.Private.Xml.dasm - XmlUntypedConverter:ChangeType(ref,ref,ref):ref:this (2 methods)
        -298 (-10.37% of base) : System.Private.Xml.dasm - XmlNumeric10Converter:ChangeType(ref,ref,ref):ref:this (2 methods)
        -273 (-16.99% of base) : System.Private.Xml.dasm - XmlListConverter:ChangeListType(ref,ref,ref):ref:this
        -227 (-5.83% of base) : System.Private.Xml.dasm - XmlAnyConverter:ChangeType(ref,ref,ref):ref:this (2 methods)
        -209 (-39.51% of base) : System.Private.Xml.dasm - XmlUntypedConverter:SupportsType(ref):bool:this

Top method regressions by size (percentage):
         155 (116.54% of base) : Microsoft.CodeAnalysis.dasm - OneToOneUnicodeComparer:GetHashCode(ref):int:this
         146 (96.69% of base) : Microsoft.CodeAnalysis.dasm - CaseInsensitiveComparison:ToLower(ref)
         588 (92.45% of base) : Microsoft.CodeAnalysis.dasm - OneToOneUnicodeComparer:Compare(ref,ref):int:this
         492 (88.97% of base) : Microsoft.CodeAnalysis.dasm - OneToOneUnicodeComparer:Equals(ref,ref):bool:this
         297 (85.59% of base) : Microsoft.CodeAnalysis.dasm - OneToOneUnicodeComparer:EndsWith(ref,ref):bool

Top method improvements by size (percentage):
         -25 (-50.00% of base) : Microsoft.CodeAnalysis.dasm - <>c__DisplayClass13_0:<ClearAnalysisScopeState>b__0(ref):bool:this
        -126 (-50.00% of base) : System.Private.CoreLib.dasm - AppDomain:SetupFusionStore(ref,ref):this (2 base, 1 diff methods)
        -209 (-39.51% of base) : System.Private.Xml.dasm - XmlUntypedConverter:SupportsType(ref):bool:this
         -53 (-37.59% of base) : System.Private.Xml.dasm - XmlListConverter:IsListType(ref):bool:this
         -54 (-35.53% of base) : System.Private.Xml.dasm - XmlMiscConverter:ChangeTypeWildcardSource(ref,ref,ref):ref:this

435 total methods with size differences (403 improved, 32 regressed), 190200 unchanged.

// Figure out what to report back.
DWORD fieldAttribs = field->GetAttributes();
bool isInitOnly = !!IsFdInitOnly(fieldAttribs);
bool isResultImmutable = isInitOnly && isClassInitialized;
Copy link
Member

Choose a reason for hiding this comment

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

bool isResultImmutable = isClassInitialized && IsFdInitOnly(field->GetAttributes()) to make this cheaper when the class is not initialized? field->GetAttributes() is not super cheap.

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, at least for the JIT side of the changes.

{
objClass = fieldClass;
}
objClass = gtGetFieldClassHandle(fieldHnd, isExact, isNonNull);

Choose a reason for hiding this comment

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

I realize that the isExact variable was pre-existing, but it tripped me up here, because it appears to be a bool rather than a bool *. Might be nice to rename it something like pIsExact.

Copy link
Member Author

Choose a reason for hiding this comment

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

Seems reasonable.

Also I was somewhat surprised to see in an earlier iteration that VC++ allows isExact = false to compile. Clang doesn't though....

{
MethodTable *pObjMT = fieldObj->GetMethodTable();

// TODO: Check if the jit is allowed to embed this handle in jitted code.

Choose a reason for hiding this comment

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

What is the remaining issue beyond the things already checked for?

Copy link
Member Author

Choose a reason for hiding this comment

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

My understanding is that for collectible classes the runtime data structures may get recycled when the class is collected, so we need to be careful about making direct references to them from jitted code.

@jkotas
Copy link
Member

jkotas commented Nov 9, 2018

@AndyAyersMS Are you also going to look into adding the checks into reflection to prevent it from mutating static readonly fields that we have discussed offline? (I can help you with that if needed.)

@AndyAyersMS
Copy link
Member Author

@jkotas yes I'll add the checks. Pointers welcome.

Copy link
Member

@erozenfeld erozenfeld left a comment

Choose a reason for hiding this comment

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

LGTM

@jkotas
Copy link
Member

jkotas commented Nov 10, 2018

@jkotas yes I'll add the checks. Pointers welcome.

RuntimeFieldHandle::SetValue and RuntimeFieldHandle::SetValueDirect should be the two methods to add it to.

@AndyAyersMS
Copy link
Member Author

@jkotas tale a look at the most recent commit; hopefully this is in the right neighborhood. It seems to work on my simple test case for SetValue -- calling SetValueDirect looks like it will require an IL test as C# won't let me build a TypedReference to a readonly static outside the class initializer.

@@ -337,6 +337,15 @@ FCIMPL7(void, RuntimeFieldHandle::SetValue, ReflectFieldObject *pFieldUNSAFE, Ob

FieldDesc* pFieldDesc = gc.refField->GetField();

// Verify we're not trying to set the value of a static initonly field
Copy link
Member

Choose a reason for hiding this comment

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

When you have a choice, it is better to have code inside HELPER_METHOD_FRAME_BEGIN_PROTECT. It reduces chances of GC holes. (You will need to use COMPlusThrow once you do that.)

@@ -1714,6 +1723,11 @@ FCIMPL5(void, RuntimeFieldHandle::SetValueDirect, ReflectFieldObject *pFieldUNSA
if (IsFdLiteral(attr))
Copy link
Member

Choose a reason for hiding this comment

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

You can delete this check while you are on it. The type loader will not create field descs for IsFdLiteral fields.

{
MethodTable* pEnclosingMT = pFieldDesc->GetEnclosingMethodTable();
if (pEnclosingMT->IsClassInited() && IsFdInitOnly(pFieldDesc->GetAttributes()))
FCThrowVoid(kFieldAccessException);
Copy link
Member

Choose a reason for hiding this comment

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

This needs good error message

@AndyAyersMS
Copy link
Member Author

Added initial cut at an error message string.

Test case now returns:

Begin initializing class X
Attempting to update S via SetValue, current value is '0', desired new value is '1'
Updated S to '1'
Attempting to update S via SetValueDirect, current value is '1', desired new value is '2'
Updated S to '2'
Done initializing class X
Attempting to update S via SetValue, current value is '2', desired new value is '3'
Caught expected exception
System.FieldAccessException: Cannot set initonly static field 'S' after type 'X' is initialized.
   at System.RuntimeFieldHandle.SetValue(RtFieldInfo field, Object obj, Object value, RuntimeType fieldType, FieldAttributes fieldAttr, RuntimeType declaringType, Boolean& domainInitialized)
   at System.Reflection.RtFieldInfo.InternalSetValue(Object obj, Object value, BindingFlags invokeAttr, Binder binder, CultureInfo culture, StackCrawlMark& stackMark)
   at System.Reflection.RtFieldInfo.SetValue(Object obj, Object value, BindingFlags invokeAttr, Binder binder, CultureInfo culture)
   at X.Set(String s, Boolean shouldThrow) in D:\bugs\4108\setfield.cs:line 30
S is '2' as expected
ERRORLEVEL=100

Should I add this test case somewhere under tests\src\reflection ?

@AndyAyersMS
Copy link
Member Author

CoreFx test is trying to set a static readonly field and failing...

System.Linq.Expressions.Tests.BindTests.StaticReadonlyField(useInterpreter: True)
...
System.FieldAccessException : Cannot set initonly static field 'StaticReadonlyStringField' after type 'PropertyAndFields' is initialized

@AndyAyersMS
Copy link
Member Author

The compiled version of the test didn't fail, so jitted code is able to alter the field still.

if (pEnclosingMT->IsClassInited() && IsFdInitOnly(pFieldDesc->GetAttributes()))
{
DefineFullyQualifiedNameForClassW();
StackSString ssFieldName(SString::Utf8, pFieldDesc->GetName());
Copy link
Member

Choose a reason for hiding this comment

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

This can be just SString. StackSString is an optimization - no need to optimize exception throwing paths.

@jkotas
Copy link
Member

jkotas commented Nov 10, 2018

CoreFx test is trying to set a static readonly field and failing...

This is bad test. I would disable it in https://github.com/dotnet/coreclr/blob/master/tests/CoreFX/CoreFX.issues.json and delete in CoreFX.

The compiled version of the test didn't fail, so jitted code is able to alter the field still.

The invalid expressions tends to have different behavior between interpretted and compiled mode.

It is close to impossible to make this check 100% bullet proof. There are many (unsafe) ways to get the readonly field and modify it. The reflection check should catch the most common cases.

Should I add this test case somewhere under tests\src\reflection ?

Yes. (Alternatively, it can be also in corefx repo. I think having low-level tests like this in CoreCLR is fine.)

calling SetValueDirect looks like it will require an IL test as C# won't let me build a TypedReference to a readonly static outside the class initializer.

I do not think you need to write it in IL. The error handling for SetValueDirect is bogus. The TypedReference can point to anything for static fields, it just cannot be null:

public class Program
{
    public static readonly string s = "Hello";
    static void Main()
    {
        int i = 0;
        typeof(Program).GetField("s").SetValueDirect(__makeref(i), "World");
        Console.WriteLine(s);
    }
}

@AndyAyersMS
Copy link
Member Author

@dotnet/dnceng ci.dot.net seems to be unreachable

@AndyAyersMS
Copy link
Member Author

CoreFX failure in NoPlaintextExportAllowsEncryptedPkcs8 seems unrelated. Need to make a few minor edits and rebase to get past a merge conflict so will see what happens when this gets rerun.

The jit incorporates the value of integer and float typed initonly static
fields into its codegen, if the class initializer has already run.

The jit can't incorporate the values of ref typed initonly static fields,
but the types of those values can't change, and the jit can use this knowledge
to enable type based optimizations like devirtualization.

In particular for static fields initialized by complex class factory logic the
jit can now see the end result of that logic instead of having to try and deduce
the type of object that will initialize or did initialize the field.

Examples of this factory pattern in include `EqualityComparer<T>.Default` and
`Comparer<T>.Default`. The former is already optimized in some cases by via
special-purpose modelling in the framework, jit, and runtime (see dotnet#14125) but
the latter is not. With this change calls through `Comparer<T>.Default` may now
also devirtualize (though won't yet inline as the devirtualization happens
late).

Addresses #4108.
Also remove assert for collectible classes and fix typo.
Flip the sense of the return flag for getStaticFieldCurrentClass and update
remainder of code accordingly.

Change jit to not ask for speculative results.
Rename bool out parameters to have `p` prefix for clarity.

Defer fetching field attributes if class is not initialized.
@AndyAyersMS AndyAyersMS force-pushed the GetExactTypeForRefClassReadonlyStatics branch from 31ed3ea to bf3c1a5 Compare November 12, 2018 15:51
@AndyAyersMS
Copy link
Member Author

I've removed the test that was setting a static readonly value via reflection in dotnet/corefx#33413 so the new exclusion won't be needed for long...

@AndyAyersMS AndyAyersMS merged commit c2abe89 into dotnet:master Nov 12, 2018
buybackoff added a commit to Spreads/Spreads that referenced this pull request Mar 10, 2019
…serializers.

Make serializers abstract classes instead of interfaces.

Now nested tuples perf is c.5-10x faster than JSON and profiler is almost clean.

# Bench

**`((((int, int), (int, int)), ((int, int), (int, int))), (((int, int), (int, int)), ((int, int), (int, int))))`**

Binary: 	3.86 Mops
JSON:  	0.85 Mops

**`(((int, int), (int, int)), ((int, int), (int, int)))`**
Binary: 	7.75 Mops
JSON:  	1.60 Mops

**`((int, int), (int, int))`**
Binary: 	20.0 Mops
JSON:  	2.60 Mops

**`(int, int)`**
Binary: 	138.0 Mops    * not nested same type is extremely fast
JSON:  	4.20 Mops

**`(int, long)`**
Binary: 	27.0 Mops
JSON:  	3.90 Mops

Utf8Json could use same technique for formatters, but that's a lot of stupid refactoring work and
should be done in a distant future at least after System.Text.Json is shipped.

dotnet/coreclr#20886
@richlander richlander added the breaking-change Issue or PR that represents a breaking API or functional change over a prerelease. label Mar 29, 2019
picenka21 pushed a commit to picenka21/runtime that referenced this pull request Feb 18, 2022
…/coreclr#20886)

The jit incorporates the value of integer and float typed initonly static
fields into its codegen, if the class initializer has already run.

The jit can't incorporate the values of ref typed initonly static fields,
but the types of those values can't change, and the jit can use this knowledge
to enable type based optimizations like devirtualization.

In particular for static fields initialized by complex class factory logic the
jit can now see the end result of that logic instead of having to try and deduce
the type of object that will initialize or did initialize the field.

Examples of this factory pattern in include `EqualityComparer<T>.Default` and
`Comparer<T>.Default`. The former is already optimized in some cases by via
special-purpose modelling in the framework, jit, and runtime (see dotnet/coreclr#14125) but
the latter is not. With this change calls through `Comparer<T>.Default` may now
also devirtualize (though won't yet inline as the devirtualization happens
late).

Also update the reflection code to throw an exception instead of changing the value
of a fully initialized static readonly field.

Closes dotnet/coreclr#4108.


Commit migrated from dotnet/coreclr@c2abe89
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
breaking-change Issue or PR that represents a breaking API or functional change over a prerelease.
Projects
None yet
6 participants