You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
JIT: Don't give up on runtime lookup in inliner too early #81432
It is expected that Test<string> will be compiled to just return false. Instead, it's compiled down to:
; Assembly listing for method Program:<<Main>$>g__Test|0_0[System.__Canon]():boolG_M24604_IG01:subrsp,40mov qword ptr [rsp+20H],rcxG_M24604_IG02:movrdx, qword ptr [rcx+38H]movrdx, qword ptr [rdx+10H]testrdx,rdxje SHORT G_M24604_IG04G_M24604_IG03:jmp SHORT G_M24604_IG05G_M24604_IG04:movrdx,0xD1FFAB1E ; global ptrcall CORINFO_HELP_RUNTIMEHANDLE_METHODmovrdx,raxG_M24604_IG05:movrcx,rdxcall[Program:<<Main>$>g__Callee|0_1[System.__Canon]():bool]nopG_M24604_IG06:addrsp,40ret; Total bytes of code 57
It seems that JIT's inliner could be a bit smarter here when currently it gives up when it sees ldtoken T! as a potential runtime lookup. We either can teach it to recognize typeof() == typeof() idiom upfront (and fold) or we can do a sort of "I've just hit a runtime lookup, let me import up to 3 more opcodes to see if I will be able to fold it"
cc @AndyAyersMS@jkotas
A more natural thing might be to keep going until the next stack empty point
Even more natural thing to do would be to fix the problem with inlining dictionary lookups. Even imperfect (not 100% efficient) solution may be better than what we have today.
A more natural thing might be to keep going until the next stack empty point
Even more natural thing to do would be to fix the problem with inlining dictionary lookups. Even imperfect (not 100% efficient) solution may be better than what we have today.
When one shared generic method calls another shared generic method, the flow is roughly:
Generic dictionary lookup of generic context in the caller
The generic context is passed as hidden argument
Generic dictionary lookup of the actual thing (e.g. type) in the callee
It should be doable to mimic this exact flow with inlining as a first iteration:
The inlined code would use chain of generic dictionary lookups to lookup the actual thing. It is not as efficient as it can be, but it is better than no inlining we get today. The VM should be able to fold the two generic lookups into a single generic lookup, but it requires inventing representation of new kind of types on the VM side that we were stuck on.
The JIT should be able to dead-code eliminate the generic lookups if they end up unused (solves the problem described by this issue).
It should be doable to mimic this exact flow with inlining as a first iteration:
Thanks, that sounds like a challenge/interesting task to do, will try this weekend, always wanted to know how this stuff works under the hood. My understanding that currently we give up somewhere here in VM
The dictionaries are eagerly initialized in NativeAOT and that's why we can skip the null check and delay initialization there. We cannot eagerly initialize dictionaries for CoreCLR. It would be large startup hit and it would break for generic types with infinite instantiation cycles that some popular packages use.
I would focus on CoreCLR with this. I guess that the question-mark commas created by dictionary lookups are hard to deal with. Should we import the dictionary lookups as a new GT note and expand this node to the full dictionary lookup later once the high-level optimizations are done?
Should we import the dictionary lookups as a new GT note and expand this node to the full dictionary lookup later once the high-level optimizations are done?
Activity
AndyAyersMS commentedon Jan 31, 2023
Seems like it could be a bit clunky to engineer this?
A more natural thing might be to keep going until the next stack empty point or block boundary.
jkotas commentedon Jan 31, 2023
Even more natural thing to do would be to fix the problem with inlining dictionary lookups. Even imperfect (not 100% efficient) solution may be better than what we have today.
EgorBo commentedon Jan 31, 2023
Is it doable for e.g cross-assembly inlining?
jkotas commentedon Feb 1, 2023
cross-assembly inlining is not a problem
When one shared generic method calls another shared generic method, the flow is roughly:
It should be doable to mimic this exact flow with inlining as a first iteration:
EgorBo commentedon Feb 1, 2023
Thanks, that sounds like a challenge/interesting task to do, will try this weekend, always wanted to know how this stuff works under the hood. My understanding that currently we give up somewhere here in VM
EgorBo commentedon Feb 1, 2023
@jkotas @MichalStrehovsky is it easier to do for NativeAOT? It seems that the runtime lookup is much simpler there? e.g. for
Here is the codegen for both methods (
Test<string>andCallee<string>):no nullchecks for generic context, helper calls, etc
jkotas commentedon Feb 1, 2023
The dictionaries are eagerly initialized in NativeAOT and that's why we can skip the null check and delay initialization there. We cannot eagerly initialize dictionaries for CoreCLR. It would be large startup hit and it would break for generic types with infinite instantiation cycles that some popular packages use.
I would focus on CoreCLR with this. I guess that the question-mark commas created by dictionary lookups are hard to deal with. Should we import the dictionary lookups as a new GT note and expand this node to the full dictionary lookup later once the high-level optimizations are done?
jakobbotsch commentedon Feb 1, 2023
Related: #35551