- Notifications
You must be signed in to change notification settings - Fork5.2k
Instantiating stub for devirtualized default interface method on a generic type#43668
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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Instantiating stub for devirtualized default interface method on a generic type#43668
Uh oh!
There was an error while loading.Please reload this page.
Conversation
MichalStrehovsky commentedOct 21, 2020
Let me know if you have trouble extracting the failing tests from the CI. E.g. here is one: I don't have pointers for you - I've decided the CoreCLR type system is not something I want to be involved in after finishing the default interface methods feature and haven't looked back since. |
Rattenkrieg commentedOct 21, 2020
Thank you! I've managed to dig through pipelines but I'm struggling to reproduce that failure. ... and it passes 🤯 I see there is setup being done duringpipeline builds:
I can't find that file in my repo/artifacts. Could there be some crucial setup steps like setting |
MichalStrehovsky commentedOct 21, 2020
:) Try COMPlus_TieredCompilation=0 |
Rattenkrieg commentedOct 22, 2020
Thank you@MichalStrehovsky ❤️ now I'm able to reproduce and have couple of failing tests under /regeressions dir. And all that issues have linked PR's submitted by you. Reading through related discussions I can feel your PTSD
|
Rattenkrieg left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
I'm done with general logic changes. The gist is:resolveVirtualMethodHelper now returns wrappedMethodDesc for devirtualized default interface method.
interfaceI<T>{stringDefaultTypeOf()=>typeof(T).Name;}classC:I<string>{}publicstaticclassProgram{[MethodImpl(MethodImplOptions.AggressiveOptimization)]staticintMain(){varc=newC();vardcs=((I<string>)c).DefaultTypeOf();if(dcs!="String")return200;return100;}}
For the program above we will getI'1[[System.String]][System.String].DefaultTypeOf frompDerivedMT->GetMethodDescForInterfaceMethod(TypeHandle(pOwnerMT), pBaseMD, FALSE); Which is already an instantiating stub!
Then we going to unwrap it intoI'1[[System.__Canon]][System.String].DefaultTypeOf, tell to caller that*requiresInstMethodTableArg = true and do our business inimpDevirtualizeCall:
Get the instantiation param from context:GenTree* instParam = gtNewIconEmbClsHndNode(exactClassHandle);. Context here isI<string> part of((I<string>)c).DefaultTypeOf();. Pass that param, so we will end with:
[000010] I-C-G------- * CALL nullcheck ref I`1[__Canon][System.__Canon].DefaultTypeOf (exactContextHnd=0x00007FF95AE1FE50) [000009] ------------ this in rcx +--* LCL_VAR ref V00 loc0 [000011] H----------- arg1 \--* CNS_INT(h) long 0x7ff95ae40020 class ^-- I`1[System.String] type handleand the following assembly would be
IN0002: 00000Fcall CORINFO_HELP_NEWSFASTIN0003:000014movrsi,raxIN0004:000017movrcx,rsiIN0005: 00001Acall System.Object:.ctor():thisIN0006: 00001Fmovrcx,rsiIN0007:000022movrdx,0x7FF95AE40020 # I`1[System.String] type handleIN0008: 00002Ccall I`1[__Canon][System.__Canon]:DefaultTypeOf():System.String:this
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
src/coreclr/src/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
src/coreclr/src/vm/method.hpp Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
I forgot to change wording before pushing.
I think we should rephrase that
The method is itself generic and is shared between generic instantiations but is not itself generic
src/coreclr/src/vm/jitinterface.cpp Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
I've delayed this check because it forbids several devirt oportunities:
interfaceI<T>{stringDefaultTypeOf()=>typeof(T).Name;}classC:I<string>{}publicstaticclassProgram{[MethodImpl(MethodImplOptions.AggressiveOptimization)]staticintMain(){varc=newC();vardcs=((I<string>)c).DefaultTypeOf();if(dcs!="String")return200;return100;}}
The check above makes us return nullptr for((I<string>)c).DefaultTypeOf(); Since it takes!pTargetMT->HasVariance() branch and we fail to comapreI<String> withI<__Canon> .
So this change should allow us to devirtualize cases likeclass C<T> : I<T> andclass C : I<string>.
By the way cases likeclass C : I<int> are fine since we comparing I<Int32> withI<Int32>
Rattenkrieg commentedOct 27, 2020
I wonder what could be the reason behind inliner failure |
Rattenkrieg commentedOct 27, 2020 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
BTW all failures in checked builds are happening here runtime/src/coreclr/src/vm/methodtable.cpp Line 2119 inf5c17f1
This is because of this reordering EDIT: It appears I've messed with conditionals. Fixed in61a7aab |
Uh oh!
There was an error while loading.Please reload this page.
davidwrighton commentedOct 28, 2020
Given the current state of what can be done with devirtualization in the JIT I worry that directly calling an instantiating stub may not provide much of a performance win. We'll need at least some level of performance benchmarking to indicate how much improvement this can provide. |
Rattenkrieg commentedOct 30, 2020 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
@davidwrighton I've named this PR in consistent way with the problem described in#9588 After spending some time on this issue it appears the title does not describe preciselly what have been done in this PR. Let me briefly explain my understaing of "instantiating/unboxing stub" first: this is purely VM concept, there is no common asm counterpart of this on the compiler side, like the ones we call "PrecodeFixup", "PreStub" etc. It is the duty of the one who generates the code to emit all necessary instructions once they have been told by VM side that I would really love to hear your criticism on stated above. For performance sake
interfaceI<T>{stringDefaultTypeOf()=>typeof(T).Name;}classC:I<string>{}publicstaticclassProgram{[MethodImpl(MethodImplOptions.AggressiveOptimization)]staticintMain(){I<string>c=newC();vardcs=c.DefaultTypeOf();if(dcs!="String")return200;return100;}} when jitted relevant parts of Main would be: 00007FFEFA6AC33Acall 00007FFEFA690088 # C.ctor()00007FFEFA6AC33Fmovrcx,rsi00007FFEFA6AC342movrdx,7FFEFAA10860h # I`1[[System.String]] type handle00007FFEFA6AC34Ccall 00007FFEFA6ABDB8 # I`1[__Canon][System.__Canon]:DefaultTypeOf()00007FFEFA6AC351movrcx,rax00007FFEFA6AC354movrdx,1DD3FD031A8h00007FFEFA6AC35Emovrdx,qword ptr[rdx]00007FFEFA6AC361call 00007FFEFA695978 # String equals00007FFEFA6AC366testeax,eax00007FFEFA6AC368je 00007FFEFA6AC37500007FFEFA6AC36Amoveax,0C8h00007FFEFA6AC36Faddrsp,20h00007FFEFA6AC373poprsi00007FFEFA6AC374ret
00007FFEFA6ABDB8call PrecodeFixupThunk (07FFF594EEAD0h) and after jitting of 00007FFEFA6ABDB8jmp 00007FFEFA6AC3D0 If we replace interface with class with virtual method like that: classI<T>{publicvirtualstringDefaultTypeOf()=>typeof(T).Name;} the only difference (in case of devirt) would be the absence of type handle store in rdx (we didn't tell compiler to emit it, because methodtable of 00007FFEFA68C36Fcall qword ptr[7FFEFA9F0730h] For me this is clearly better than VSD in non-devirtualized interface case and just one In given example we were not able to inline due to limitations mentioned in#38477 But if we try something like that: interfaceI<T>{TGetAt(inti,T[]tx)=>tx[i];}classC:I<string>{}publicstaticclassProgram{privatestaticstring[]tx=newstring[]{"test"};[MethodImpl(MethodImplOptions.AggressiveOptimization)]staticintMain(){I<string>c=newC();vardcs=c.GetAt(0,newstring[]{"test"});if(dcs!="test")return200;return100;}} we will get inlined code IN0001:000005movrcx,0x7FFEFA9F0858IN0002: 00000Fcall CORINFO_HELP_NEWSFASTIN0003:000014movrcx,raxIN0004:000017call System.Object:.ctor():thisIN0005: 00001Cmovrcx,0x7FFEFA9618E0IN0006:000026movedx,1IN0007: 00002Bcall CORINFO_HELP_NEWARR_1_OBJIN0008:000030learcx, bword ptr[rax+16]IN0009:000034movrdx,0x284C25431A8IN000a: 00003Emovrsi, gword ptr[rdx]IN000b:000041movrdx,rsiIN000c:000044call CORINFO_HELP_ASSIGN_REFIN000d:000049movrcx,rsiIN000e: 00004Cmovrdx,rsiIN000f: 00004Fcall System.String:op_Inequality(System.String,System.String):boolIN0010:000054testeax,eaxIN0011:000056je SHORT G_M24375_IG05 I'm totally ok if you still not convinced: would synthetic BDN harness be suffictient to demonstrate improvement or do I need to hack something like stuff describedhere then? |
Rattenkrieg commentedOct 31, 2020 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Some asm diff stats:
IN0008:000000subrsp,40IN0009:000004xorrax,raxIN000a:000006mov qword ptr[V00rsp+20H],raxG_M29659_IG02: ; offs=00000BH, size=0029H, bbWeight=1 PerfScore 6.75, gcrefRegs=00000000 {}, byrefRegs=00000000 {}, byrefIN0001:00000Bmovrcx,0x13F900031A8 #"IFrobber<T>:Frob"IN0002:000015movrcx, gword ptr[rcx]IN0003:000018call System.Console:WriteLine(System.String)IN0004: 00001Dmovrcx,0x13F900031B0 #"IRobber<T>:Frob"IN0005:000027movrcx, gword ptr[rcx]IN0006: 00002Acall System.Console:WriteLine(System.String)IN0007: 00002Fmoveax,100 #34+66G_M29659_IG03: ; offs=000034H, size=0005H, bbWeight=1 PerfScore 1.25, epilog, nogc, extendIN000b:000034addrsp,40IN000c:000038ret
IN0005:000000subrsp,56IN0006:000004xorrax,raxIN0007:000006mov qword ptr[V05rsp+28H],raxIN0008:00000Bmov qword ptr[V05+0x8rsp+30H],raxG_M27646_IG02: ; offs=000010H, size=0010H, bbWeight=1 PerfScore 2.00, gcrefRegs=00000000 {}, byrefRegs=00000000 {}, byrefIN0001:000010learcx, bword ptr[V05rsp+28H]IN0002:000015movedx,42IN0003: 00001Acall IM`1[Int32][System.Int32]:DefaultM(int):System.Threading.Tasks.ValueTaskIN0004: 00001FnopG_M27646_IG03: ; offs=000020H, size=0005H, bbWeight=1 PerfScore 1.25, epilog, nogc, extendIN0009:000020addrsp,56IN000a:000024ret EDIT: to have stats reported by jit-diff properly, it has to be patched to move coreclr binaries around, seedotnet/jitutils#296 |
davidwrighton commentedNov 1, 2020
@Rattenkrieg I would prefer some level of actual benchmarking, but the analysis you have provided is sufficient to convince me that there is value here, and this is worth implementing. I'm currently very focused on finishing up some details around crossgen2 compilation and that will occupy me for a few more days before I can deeply analyze the correctness of this change, and probably write a few extra test cases to cover possible problems I see. (Primarily around cases where the type implements multiple generic variants of the same interface.). In addition, I'd like to see@AndyAyersMS or someone he designates take a look at the jit/jitinterface api side of this change. With this work, we are very close to supporting devirtualization of generic virtual methods, which would also be interesting, and it might be best to make the jit interface api reflect that possibility as well. |
AndyAyersMS commentedNov 2, 2020
This is going to take a bit of time to sort through. But some quick initial thoughts:
|
Rattenkrieg commentedNov 3, 2020
@AndyAyersMS I'll check params passing solution for jit-diff -fAll these improvements are due to#43668 (comment) |
Rattenkrieg commentedNov 3, 2020
No rush needed! I've pushed some tests I used for debugging, most of them are primitive since I was primarily interested in straightforward asm for analysis. But there are few testing "cases where the type implements multiple generic variants of the same interface". I don't propose to merge those, just added it for reference. Now I'm going to work on benchmarks, mostly for methods were reported as changed by jit-diff. |
c751e71 tof145018CompareRattenkrieg commentedNov 4, 2020
Here are benchmarks made from pushed tests. Ran with resultsBenchmarkDotNet=v0.12.1.1448-nightly,OS=fedora 33Intel Core i7-8700 CPU 3.20GHz (Coffee Lake), 1 CPU, 12 logical and 6 physical cores.NETSDK=6.0.100-alpha.1.20553.9 [Host] : .NET 6.0.0 (6.0.20.55204), X64 RyuJIT Job-UOOGDK : .NET 6.0 (42.42.42.42424), X64 RyuJIT Job-ENJNBQ : .NET 6.0 (42.42.42.42424), X64 RyuJITPowerPlanMode=00000000-0000-0000-0000-000000000000Arguments=/p:DebugType=portableIterationTime=250.0000 msMaxIterationCount=20MinIterationCount=15WarmupCount=1
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Be advised, although I'm not 100% sure, same below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Yeah, it looks like the wrong map is getting used here. Suspect the keys are conformable and don't collide so we never noticed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Agree, thanks for catching this.
Rattenkrieg commentedNov 22, 2020
I don't know the reason behind timeouts in few tests, have everything passing on my machine. Besides that I think that I'm done with implementation. |
Rattenkrieg commentedNov 28, 2020 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Format is failing on CISame on my machineWindows |
5fe23e4 toe91c55bCompareRattenkrieg commentedDec 21, 2020
@davidwrighton@AndyAyersMS PTAL Initially I thought that my solution is incomplete wrt generic methods on non-generic interfaces but tests demonstrated that we never touch devirtualization for such cases. Consider: interfaceI{stringDefaultTypeOf<T>()=>typeof(T).Name;}classC:I{}publicstaticclassProgram{[MethodImpl(MethodImplOptions.AggressiveOptimization)]staticintMain(){Ic=newC();vardcs=c.DefaultTypeOf();if(dcs!="Object")return200;return100;}} produces IN0001:000008movrdi,0x12293E518IN0002:000012call CORINFO_HELP_NEWSFASTIN0003:000017movrbx,raxIN0004: 00001Amovrdi,rbxIN0005: 00001Dcall System.Object:.ctor():thisIN0006:000022movrdi,rbxIN0007:000025movrsi,0x12293E3C8IN0008: 00002Fmovrdx,0x12293E718IN0009:000039call CORINFO_HELP_VIRTUAL_FUNC_PTRIN000a: 00003Emovrdi,rbxIN000b:000041callraxIN000c:000043movrdi,raxIN000d:000046movrsi,0x19B2371F0IN000e:000050movrsi, gword ptr[rsi]IN000f:000053call System.String:op_Inequality(System.String,System.String):boolIN0010:000058testeax,eaxIN0011: 00005Aje SHORT G_M24375_IG05 If we remove DIM and let class implement interface asm stays the same. However if we remove interface, method inlines perfectly: classC{publicstringDefaultTypeOf<T>()=>typeof(T).Name;} IN0001:000004movrdi,0x11CFFE3F8IN0002: 00000Ecall CORINFO_HELP_NEWSFASTIN0003:000013movrdi,raxIN0004:000016call System.Object:.ctor():thisIN0005:00001Bmovrdi,0x11CDA4388IN0006:000025call CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEIN0007: 00002Amovrdi,raxIN0008: 00002Dmovrax,0x11CC5A5F0IN0009:000037call gword ptr[rax]System.RuntimeType:get_Name():System.String:thisIN000a:000039movrdi,raxIN000b: 00003Cmovrsi,0x19D8FD1F0IN000c:000046movrsi, gword ptr[rsi]IN000d:000049call System.String:op_Inequality(System.String,System.String):boolIN000e: 00004Etesteax,eaxIN000f:000050je SHORT G_M24375_IG05 But only for one generic param: classC{publicstringDefaultTypeOf<T1,T2>()=>typeof(T1).Name+typeof(T2).Name;} IN0001:000008movrdi,0x10C68E3F8IN0002:000012call CORINFO_HELP_NEWSFASTIN0003:000017movrbx,raxIN0004: 00001Amovrdi,rbxIN0005: 00001Dcall System.Object:.ctor():thisIN0006:000022movrdi,rbxIN0007:000025movrsi,0x10C68E608IN0008: 00002Fcall C:DefaultTypeOf():System.String:thisIN0009:000034movrdi,raxIN000a:000037movrsi,0x194F911F0IN000b:000041movrsi, gword ptr[rsi]IN000c:000044call System.String:op_Inequality(System.String,System.String):boolIN000d:000049testeax,eaxIN000e: 00004Bje SHORT G_M24375_IG05 Have we ever considered optimizing such case? |
AndyAyersMS commentedDec 21, 2020
I believe all generic virtual methods are handled via the I had been thinking it wouldn't be worth addressing until after the jit is able propagate methods into |
Rattenkrieg commentedFeb 2, 2021
Hey@davidwrighton is there still an interest from dotnet team/you in this PR? |
| publicstaticclassProgram | ||
| { | ||
| [MethodImpl(MethodImplOptions.AggressiveOptimization)] | ||
| staticintMain() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
We should find a way to test this without AggressiveOptimization. Currently that will disable the Crossgen compiler and so we won't test the behavior in ahead of time compile scenarios.
| @@ -0,0 +1,31 @@ | |||
| usingSystem; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
u [](start = 0, length = 1)
All of the test files need the copyright attribution comment.
| MethodDesc*GetMethodDescForInterfaceMethod(TypeHandleownerType,MethodDesc*pInterfaceMD,BOOLthrowOnConflict); | ||
| MethodDesc*GetMethodDescForInterfaceMethod(MethodDesc*pInterfaceMD,BOOLthrowOnConflict);// You can only use this one for non-generic interfaces | ||
| // ^-- I don't believe this is correct statement, we have PRECONDITION(!pInterfaceMD->HasClassOrMethodInstantiation()); which implies it can be used with generic interfaces |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
That is not the meaning of the precondition. Its actually the opposite. If the precondition isn't true, the method can't be used, and if the method is on a generic interface then HasClassOrMethodInstantiation will return true, so !HasClassOrMethodInstantiation will be false.
| // RequiresInstMethodDescArg() | ||
| // The method is itself generic and is shared between generic | ||
| // instantiations but is not itself generic. Furthermore | ||
| // instantiations but is not itself generic.WTF LOL. Furthermore |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Words are hard, and copy/pasting is easy. The "but is not itself generic" is just wrong and should be deleted from this comment.
| // instantiations but is not itself generic. Furthermore | ||
| // instantiations but is not itself generic.WTF LOL. Furthermore | ||
| // no "this" pointer is given (e.g. a value type method), so we pass in the | ||
| // exact-instantiation method table as an extra argument. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
method table [](start = 27, length = 12)
This should read "MethodDesc"
| MethodTable* pOwnerMT = OwnerClsHnd.GetMethodTable(); | ||
| pOwnerMT = OwnerClsHnd.GetMethodTable(); | ||
| if (!canCastStraightForward && !(pOwnerMT->IsInterface() && pObjMT->CanCastToInterface(pOwnerMT))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
What scenario are you addressing here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Sorry, I found your explanation above. If pOwnerMT isn't an interface, why don't we return false here?
In reply to:570649611 [](ancestors = 570649611)
| false); | ||
| // Shared generic or static per-inst methods andshared methods on generic structs | ||
| // Shared generic or static per-inst methods,shared methods on generic structs and default interface methods |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
This is a good comment change.
davidwrighton commentedFeb 5, 2021
@Rattenkrieg We are interested in this change, and based on the performance numbers I'm convinced its a nice small worthwhile change. One reason why this feature wasn't built is that it crosses feature areas and expertise of various teams, and is difficult for us to actually review for correctness. I have a few concerns
|
danmoseley commentedFeb 10, 2021
Assigning a shepherd for this PR, feel free to change. |
davidwrighton commentedMar 22, 2021
@Rattenkrieg Are you able to answer my questions? If so, please let us know, as otherwise we'll be closing this PR soon. |
Rattenkrieg commentedMar 23, 2021 via email
Hey David! I cannot open this PR anymore, I'm getting This page is taking too long to load. Sorry about that. Please try refreshing > and contact us if the problem persists.Neither mobile version nor desktop one works. Seems that's because I'veaccidentally merged tons of files from main while ago and github isoverwhelmed with big diffs now. So I've missed your questions, sorry.I definitely looking to finish this PR. I guess the only option for me isto resubmit changes with new PR and I would gladly ask you to state yourquestions in the new one. …On Tue, Mar 23, 2021, 03:02 David Wrighton ***@***.***> wrote:@Rattenkrieg <https://github.com/Rattenkrieg> Are you able to answer my questions? If so, please let us know, as otherwise we'll be closing this PR soon. — You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub <#43668 (comment)>, or unsubscribe <https://github.com/notifications/unsubscribe-auth/ABVALU7AGEC2BAPGLN6QY2DTE6V6NANCNFSM4SZAV26A> . |
lambdageek commentedMar 23, 2021 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
@Rattenkrieg you can append |
Rattenkrieg commentedMar 25, 2021
Thank you@lambdageek now I'm back online.@davidwrighton I will address your feedback (hopefully) this weekend and will open a new PR with smaller carbon footprint. |
mangod9 commentedApr 26, 2021
@Rattenkrieg can this PR be closed since you are hoping to open a new one after addressing feedback? |
Rattenkrieg commentedApr 27, 2021
@mangod9 sorry for inconvenience. I was planning to resubmit changes like month ago but currently I have technical issues. I'm really really looking to open new PR soon. |
mangod9 commentedApr 27, 2021
Thanks for the update! |
Fixes#9588
I don't believe this could be that easy, otherwise why this wasn't added indotnet/coreclr#15979 🤔
I'm struggling to find jit asm changes at the moment.
produces no diff, I've also tried with
--frameworkand--corelib.And I've also compared jit output from the following command
where XXX were
diamondshape_r.ilproj,non_virtual_calls_to_instance_methods.ilprojandsharedgenerics_d.ilprojand new project I created with the code from#39419... still no difference.
So my thoughts are: either I've missed some optimization flag or messed with setup in any other way OR we never reaching that code 🤷
Going to try tomorrow on Windows.
@MichalStrehovsky PTAL as you were the author ofdotnet/coreclr#15979 and friends.