JIT: Enable inlining of runtime async methods without awaits#124183
JIT: Enable inlining of runtime async methods without awaits#124183jakobbotsch merged 6 commits intodotnet:mainfrom
Conversation
While inlining runtime async methods in general is tricky, it is relatively simple to allow inlining async methods that do not have any awaits in them, and I suspect this is a large fraction of them.
There was a problem hiding this comment.
Pull request overview
Enables inlining of “runtime async” methods in the JIT when the async method body has no awaits, by rejecting inline candidates that require async call setup while adjusting async-context save/restore and EH sizing behavior.
Changes:
- Add a new fatal inline observation for detecting awaits during inlinee import.
- Abort inlining when async call setup is encountered while importing an inlinee.
- Adjust EH table sizing and async-context save/restore behavior to support the new inlining scenario.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| src/coreclr/jit/inline.def | Adds callee “await” observation; removes callsite continuation-handling observation. |
| src/coreclr/jit/importercalls.cpp | Marks inlinees as non-inlineable when async call setup is detected; removes prior callsite continuation-handling inline ban. |
| src/coreclr/jit/fgbasic.cpp | Accounts for an extra EH clause when async context save/restore is enabled. |
| src/coreclr/jit/async.cpp | Tweaks async-context logic for inlining mode (resumed computation, arg injection, return merging). |
|
cc @dotnet/jit-contrib PTAL @AndyAyersMS One thing this doesn't handle is multi-level inlinees where we eventually end up with no awaits. Not sure if that generalization would be simple, however. |
AndyAyersMS
left a comment
There was a problem hiding this comment.
Didn't see anything other than what copilot saw.
|
@jakobbotsch, why do you think a "large fraction" of async methods do not have an await? Such methods would result in a CS1998 warning: "This async method lacks 'await' operators and will run synchronously." Do you want to optimise for "bad" code? If the cost of this optimization is negligible, then this doesn't matter of course. |
I suspect that a large fraction of async functions that we would inline do not have await. Generally successful inlinees are smaller and more likely to be leaf methods than other methods. But yes, @stephentoub pointed CS1998 out too (although we are removing this warning) and it is very possible it means my intuition about inlinees does not carry over to async methods. |
|
What is the rationale behind removing CS1998? Just curious. |
You can see the related discussion in dotnet/roslyn#77001. From a runtime async perspective it is no longer more expensive to have these methods be async, and in fact will often be a deoptimization to not make them async since the alternatives like |
From a language perspective, the cure was worse than the disease. Even before runtime async, folks would get the warning and then bend over backwards to try to avoid it, often making things words, when the right answer was just generally just to use |
While inlining runtime async methods in general is tricky, it is relatively simple to allow inlining async methods that do not have any awaits in them, and I suspect this is a large fraction of them.