-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Field guards from unreachable code can be moved to reachable code and can cause repeated deoptimization #50245
Comments
I'm surprised the unreachable setter is inlined. The IC should have a usage counter of 0 that blocks the inlining. Maybe some AOT optimizations have broken the old invariant the never inline calls that have not executed? |
I need to clarify that in this issue by "unreachable code" I mean code which was not executed so far. It is still reachable from compiler perspective.
Call specializer ( Even in case of the normal inliner While fully blocking inlining of the calls with usage counter 0 might solve this particular problem, it will probably regress performance in other cases, as re-optimization happens rarely and it is not uncommon to execute code paths which were not executed before optimized compilation. |
There are some, ahm, workarounds in the JIT that try to work around this issue. We try to track if a hoisted check has deoptimized and then prohibit LICM. Look for There is more to this - where we try to track which exactly check needs to be pinned instead of disabling LICM completely, but I am not sure this code works as intended because once the check is moved it looses information about its original deopt id. We should consider fixing this but it requires adding more information to checks/guards and then feeding this information through deopt stubs. Unclear if we want to invest into that. @alexmarkov do you have IL from |
@mraleph In the case I saw in #50239 we can predict this statically, because rhs of the store is a constant ( Maybe we should somehow attribute guard instructions which were never executed (from ICData usage counters) and disallow/limit LICM for such code? |
As a very controversial idea we can also replace code which was never executed with an unconditional deoptimization (in JIT). This might even result in a better performance as we might be able to do more optimizations such as CSE/LICM in the hot code paths. But this could also result in much more deoptimizations/recompilations compared to current setup. |
That will cause bugs. Code coverage relies on at least the first execution going through unoptimized code to increment counters (#42061). Similarly, breakpoints rely on the unoptimized function being compiled first to resolve to a code position. |
Consider the following example:
IL for the unreachable code
obj.foo = const [];
after ApplyClassIds pass:LICM moves GuardFieldClass and GuardFieldType instructions to the beginning of the method:
These instructions always trigger deoptimization as _ImmutableList doesn't match guarded field class _GrowableList.
As a result, this causes OSR->deoptimization->OSR loop, wasted JIT compilations and unoptimized code running all the time.
/cc @mraleph @mkustermann @rmacnak-google
The text was updated successfully, but these errors were encountered: