-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
JIT emitting unnecessary code for non-emitted method call #34641
Comments
I guess it's #32094 (comment) |
Right, the "wrong in both case" seems to be the jit saving the generic context to report it to the VM. The earlier cases are something different, likely we think we need a frame at one point early on and then optimize something, and don't realize we no longer need a frame. |
In the #32094 (comment) it's a generic method body. Here, in the second example, it is method in a generic class that (might) call another method of the same class. Does it make a difference? Why am I asking: The think that catches my eye is that the x64 version is short when the eliminated call is non-generic (first example). If the eliminated call is generic (second example), the "generic context" is stuck there. I am sorry if I am wrong with reasoning. |
I took a closer look at the second case and we should actually be able to remove the context reporting. There are two conditions in which we report the context (store it to the stack) -- either if the context is used somewhere in the method, or if the VM requests that the jit report the context. In the second example the VM does not request reporting. Initially the context looks like it is used but after some optimization we remove all the uses. Unfortunately we never re-check if the context is still used and go by our early impression. I think we can fix this. |
I have a fix, but it doesn't fire very often on FX (4 methods out of 240K).
Some of the accounting we do (namely Also there is some interesting codegen in the non-shared cases that I want to look at, seems like we ought to be able to get rid of some boxing there. @davidwrighton @janvorli is there some clear picture of when the jit must report the generic context in gc info, outside of the cases where the VM requests the reporting? Comments in the jit code refer to collectable assemblies and suggest we're over-reporting: runtime/src/coreclr/src/jit/importer.cpp Lines 1976 to 1983 in 9e608fc
When the context is passed as a separate arg it is fairly easy for us to accurately determine the use count after optimization. When the context comes from Wondering if (a) we could flag methods that are in collectable assemblies for the jit, and relax reporting for assemblies that aren't collectable, and possibly (b) relax reporting of the |
(a) The collectible assemblies case is for scenarios like (b) Relaxing reporting of Imagine a method that looks like class C<T>
{
void Method(object o)
{
T t = (T)o;
Console.WriteLine(t);
}
} Now, the JIT will generate something like the following psuedocode for
Note that the point at which the this pointer no longer needs to be reported isn't really related to any action performed on the I'll have to do some research to find out about the non-collectability reasons for reporting the generics context. |
One note, is that for the purposes of collectability there is no need for the JIT to use the generic context reporting stack slot for reporting the this pointer, and could simply report it via completely normal GC reporting, it just needs to be reported until after the last pointer pulled from a generic dictionary is no longer in use. |
Ok, thanks.
I added the "ref counting" aspect in dotnet/coreclr#9756 as often times after inlining or various opts we end up not needing the runtime lookup at all, and I wanted to fully dead code them. Didn't realize until now that this may sometimes under-count. |
This turns out to be one of the multi-use box cases (see #9118) that we don't yet handle. Root method boxes, inlinee uses box in both type test and unboxing. We can clean some of this up, but are not able to undo the box. |
The generics context is reported by the jit specially whenever it feeds into runtime lookups, as expansion of those lookups can expose pointers into runtime data structures, and we don't want those data structures to be collected if jitted code is still using them. Sometimes uses of the context are optimized away, and reporting costs code size and GC space, so we don't want to report the context unless there is an actual use. This change revises how the jit keeps track of context use -- instead of trying to incrementally ref count uses of the generics context, we now just leverage existing passes which do local accounting. Initial motivation for this came from dotnet#34641 where the context use was over-reported, but investigation showed we also some times under-report as the context var could be cloned without changing the ref count. So this change fixes both under and over reporting. Closes dotnet#34641.
#34827 should fix the second case -- haven't looked at the first case yet, and may not get to it for a while, since it is x86 only. |
The generics context is reported by the jit specially whenever it feeds into runtime lookups, as expansion of those lookups can expose pointers into runtime data structures, and we don't want those data structures to be collected if jitted code is still using them. Sometimes uses of the context are optimized away, and reporting costs code size and GC space, so we don't want to report the context unless there is an actual use. This change revises how the jit keeps track of context use -- instead of trying to incrementally ref count uses of the generics context, we now just leverage existing passes which do local accounting. Initial motivation for this came from #34641 where the context use was over-reported, but investigation showed we also some times under-report as the context var could be cloned without changing the ref count. So this change fixes both under and over reporting. Closes #34641.
Both x86 and x64 JIT emits unnecessary stack manipulation code (if I read it correctly) for the following (+- minimal example) code snippets of generic code with some asm-compile-time deduction. It seems that it emits the stack manipulation code required for method-call that is asm-compile-time unreachable.
Tested in Sharplab, Core CLR v4.700.19.57202.
sharplab.io link with more faulty variatons
Note: I stumbled opon this, when optimizing hot-path "is default" generic check, because I was not satisfied with this suggestion: https://stackoverflow.com/questions/65351/null-or-default-comparison-of-generic-argument-in-c-sharp/864860#864860
class C<TVal>
and TVal can be any class at runtimeonly wrong in x86:
x86:
x64:
======
wrong in both:
x86:
x64:
The text was updated successfully, but these errors were encountered: